account_deleted's picture

Annoying bug involving GameWindow class --resolved

I have just started using OpenTK. So far, it seems to be the best Opengl (et c.) toolkit I've seen.

One minor problem that's bugging me is that any attribute change to an instance of the GameWindow class, including resizing, switching WindowState, and just about everything else I've tried, does not actually show up until after a fraction-of-a-second delay (that does not happen in the application thread for some reason), and all other changes during that period will cause the change to not happen at all. This interferes with things like setting the width and height at the same time, and has caused a few headaches with saving the state, size, position, et c. of the window for the next time it is opened. For example: (some code removed to save screen space)

private void LoadSettings() {
	while(! input.EndOfStream) {
		settingName, settingValue = input.ReadLine();
		switch(settingName) {
			case "size":
				myGameWindow.Width = int.Parse(settingValue.Split(separator)[0]);
				myGameWindow.Height = int.Parse(settingValue.Split(separator)[1]);
				//Width is not set now. Not setting Height fixes that, but then Height is not set, of course.
				break;
			case "windowstate": ... //no problems
		}
	}
}
private void SaveSettings() {
		output.WriteLine("size : {0}x{1}", myGameWindow.Width, myGameWindow.Height);
		output.WriteLine("windowstate : {0}", myGameWindow.WindowState);
	}
}

When both of the above methods are executed too closely together, some values that were supposed to change will not.

//Width is 640, Height is 480, WindowState is Normal (defaults)
//this is supposed to change the Width, Height, and WindowState
LoadSettings();
//Width is still 640, Height is still 480, WindowState is still Normal.
//If I insert enough Console.Write()'s, I can see the Height and sometimes WindowState change before SaveSettings() is executed.
//Otherwise, they will not change until it is to late.
SaveSettings();
//Height and WindowState usually change somewhere in here. Width will not change because of the problem mentioned earlier.

This is using the latest release available from the front page. (could someone please direct me to the version numbers for future reference?)
running on OpenSuse with Gnome Compiz/Metacity. (seems relevant to windowing issues)


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

This is by design. First, I'll offer the solution and then an explanation for this behavior.

The correct solution is to use GameWindow events:

private void HookEvents()
{
    Resize = delegate { SaveSettings(); };
    WindowStateChanged = delegate { SaveSettings(); };
    // Add any other event you are interested in monitoring
}

And that's it. This is guaranteed to work correctly.

Conceptually, the issue is that GameWindow is using an asynchronous (i.e. event-based) model that you are trying to use in synchronous manner. This cannot work.

Consider how GameWindow works internally:

  1. You change e.g. the Width property.
  2. OpenTK calls XResizeWindow to change the size (or the correct platform-specific function).
  3. The OS informs OpenTK that the window has been resized via a ConfigureEvent (or the correct platform-specific message).

OpenTK cannot update the Width property immediately, because (a) the window may not have been resized yet and (b) the OS may decide to give a different size than the one requested (many X11 window managers will resize the window to fit the screen). In both cases, your SaveSettings() would not only fail to work but also give incorrect results.

OpenTK could poll the OS whenever you read a property but that would expose your application to implementation details (i.e. one OS might update properties asynchronously, while another might update them immediately). This is even worse than the current situation because your code would appear to work, only to break randomly on a different machine.

You could also force GameWindow to update values immediately by adding a call to ProcessEvents in your SaveSettings method:

private void SaveSettings()
{
    ProcessEvents();
    // ...
}

which is another, slightly hackish, solution to the issue.

account_deleted's picture

Fixed it by calling ProcessEvents() after each change made in LoadSettings(). I think the problem with the width not being set was that the resize event was getting overwritten before it was processed.

Thanks!

Edit: fixed it better by using "Size" instead of width and height. No need for ProcessEvents() anymore! =)