
NativeWindow
Posted Thursday, 20 August, 2009 - 20:25 by Kiena| Project: | The Open Toolkit library |
| Version: | 0.9.x-dev |
| Component: | Code |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Kiena |
| Status: | closed |
Description
The main topic of this request for change/feature is the new OpenTK.NativeWindow class which will expose the internal INativeWindow implementations at a lower level than GameWindow, and will actually become latter's base class.
The extraction of INativeWindow specific details of GameWindow at the same time provided an opportunity for cleanup and made unavoidable the revision of several implementation and even design details.
The main purpose of this request is to let everyone interested and/or affected by these changes to provide feedback and participate in the decision making.
Details in the following posts.


Comments
#1
The included first draft is compilable, but is quite far from the intended final version.
It's hopefully sufficient for providing a correct overview of what these classes are to become like.
A few
TODOs in theGameWindowclass are from the version in the trunk, but the numerous new ones in both classes are notifications for unresolved details, and all should be resolved in a way that is acceptable for as many users of the library as possible, before inclusion in the official version. The possible changes not marked in the source will be included in following posts.Notes for this version:
NativeWindow(NativeWindow0) is to ease integration of the class into the sources gotten from CVS. Platform specific code refers to Forms.NativeWindow and this intermediate workaround spares everyone from patching those classes too.GameWindowis closed using the native close button (in short, X), but the main priority for now was to present the code for review, that issue should be fixed soon after some control flow examination.(Sources removed due to the addition of newer versions.)
#2
Thank you, I'm studying the code as we speak.
The shutdown issue seems to be caused by a loop in the shutdown logic: the 'X' button is pressed, the Closing is fired, the window closes and the Closing event is fired again. This is probably caused by the Windows INativeWindow implementation - investigating.
#3
The two most important pending details not immediately visible in the sources (unlike typos right after posting):
On*methods:For historical reasons (because samles and documentation say so) the
On*methods are invoked along with rising the corresponding events instead of the events being raised by them. Problems with this approach:On*methods as being the place the corresponding events are raised (allowing filtering or argument manipulation, for example).On*method is invoked isn't consistent in the current implementation ofGameWindow. This would obviously be resolved by the events being raised inside theOn*methods.GameWindowrelies on some calls toOn*Internalmethods (which serve as invokers of both events andOn*methods), which may not be worked around without adding someNativeWindowinternals that are forGameWindowonly. Simply calling the non-internalOn*method wouldn't rise the corresponding event.What the foreseeable effects of the change would be, depending on their current usage:
On*methods, by overriding them:No noticeable change in behavior is expected, but the actual difference would be that the corresponding event doesn't get raised. (Absolutely minimal difference in performance difference, because either no handler, or an empty nameless method is attached to them.)
No noticeable change in behavior is expected, because if not overridden, the
On*the on method raises the event.It's expected the events won't be raised due to their invocation being overridden with application logic. The solution would be to either call the base method as the first or last action in the overriding method or to consistently rely on only one of these features.
GameWindowattached its internal event handlers (to propagate them to the above mentionedOn*methods and events visible onGameWindow) to itsINativeWindowat the end of its construction, but because of the new model involving inheritance, the events exposed byGameWindowactually belong to the base NativeWindow class, and normally would get connected to their handlers before the derived constructor even starts.This could easily cause crashed if the operating system sends a window event and for example logic involving OpenGL operations has been placed into the
On*method the event is propagated to.My solution proposal is the addition of the protected
EnableEventsandDisableEventstoNativeWindowwhich would allow both the delaying, both the stopping of propagation of events arriving from the underlying system. (If the discussed change in the previous point was accepted, it would allow manual event raising by callingOn*methods under all circumstances (unless specifically overridden not to do so).)In case early event calls would cause no problems for an application it would be kind of uncomfortable and easy to forget to actually call
EnableEvents, for this reason my proposal is the addition of a boolean parameter toNativeWindowconstructors that allows events being enabled without any further actions being required.A very small update of the 1st draft is included because the
NativeWindowconstructor didn't include the consideration of the mentioned parameter.(Sources removed due to their addition to the repository.)
#4
I have created a new branch for the NativeWindow updates. Before we continue, please check out from svn://opentk.svn.sourceforge.net/svnroot/opentk/branches/nativewindow. Future changes should go to that branch - this will make our lives much easier in the long run.
I have committed your latest version to the new branch and fixed the shutdown issues. Changes:
Considering our options, it seems best to follow the design guidelines and raise events from the On* methods. This is a breaking change, but you are correct that it's impact to existing is minimal.
This is a more hairy issue. I do not really like having separate methods to enable/disable events explicitly - this is neither intuitive nor discoverable for the user.
One potential solution would be to ensure that events are *only* raised once NativeWindow.ProcessEvents() is called at least once. This can be done easily by having NativeWindow.ProcessEvents() use EnableEvents() on first call (almost the same as your proposal, with the difference that EnableEvents() is called implicitly.)
#5
NativeWindow0->NativeWindowrenaming patch.(Has been applied to the branch.)
#6
[Enable|Disable]Events:
I suggest the following changes:
protected bool OSEvents { get; set; }instead of[Enable|Disable]Eventsmethods. I wouldn't make the property internal because disabling event propagation should be needed during derived class (e.g.GameWindow) disposal if delaying of events was desired during construction. Maybe events could be automatically disabled at a reasonable point as well (Close?), but there may be cases e.g. a context change for some reason, when this level of control could be useful.public void ProcessEvents()enables event propagation if it currently is not, whenever called.protected void ProcessEvents(bool retainOSEvents)method which is optionally usable to consume OS events, yet preserve propagation state. It is called with the defaultfalseargument by its public counterpart. If the argument istruethen propagation won't be disabled, instead it will remain whatever it was like before the call.EDIT:
boolargumenst will be gone, of course.Like this users of the library should not be aware of these features unless they actually need them, I believe.
Note: Suggestions of better names for the
OSEventsproperty andretainOSEventsvariable are welcome.Disabling events automatically in the Close method or at another point is a new idea, and should be decided after the proper place for it in
GameWindowhas been found.#7
Committed NativeWindow0 -> NativeWindow patch.
I am running some tests to determine whether events are likely to cause issues for inheriting classes.
Edit: I haven't encountered any adverse side-effects by enabling events inside the NativeWindow constructor. As far as I can tell, events are only generated when
INativeWindow.ProcessEventsis called. This method is not called by a the NativeWindow constructor, so no events will be generated unexpectedly.Will have to verify this holds for all platforms, however.
#8
This patch needs review and some debugging before it could be committed.
The issue appears at the same spot as before (usage of native close button), but is a different kind.
Included:
I couldn't determine yet which of these changes resulted in the exception thrown during buffer swapping. (Though the same rendering function seems to pass some GL operations without problems (swallowed exceptions?), otherwise it should be the case that rendering is still invoked after the context/native window becomes invalid, when the window is closed this way.) I marked the problem source candidates with *, regrettably couldn't exclude the majority of changes...
(Updated patch in a following post.)
#9
Comments:
INativeWindow.ProcessEvents, there will not be a need to add the Events property (needs review). I agree we should make a strong guarantee for event generation (e.g. no events in constructor), but I think the global event toggle should be avoided if possible.WindowState = WindowState.Normalwill cause problems in that case. A call to RestoreResolution() should suffice.isExiting = true.One potential solution is to make
GameWindow.Exit()/Close()work asynchronously. Both could simply set isExiting to true and the actualbase.Close()could be moved right after the main loop (inside the run method).This still leaves a potential issue of calling Dispose() while the main loop is running, however. Unless we can find a reliable solution for shutting down the main loop, maybe we'll have to reintroduce the internal GameWindowExitException.
I'll be without internet access until Monday, so I won't be able to review/commit patches for a few days.
#10
There definitely is a solution without using the exception for flow control.
Something potentially related unresolved detail not mentioned so far:
Should NativeWindow's Dispose be virtual, and should GameWindow override it? The addition of IDisposable to GameWindow's signature guarantees its Dispose method being called at the end of using blocks, but depending on the variable type it's possible only NativeWindow's is run in a try-finally block.