diff --git a/Source/OpenTK/GameWindow.cs b/Source/OpenTK/GameWindow.cs index 1fc3af41..2fa96467 100644 --- a/Source/OpenTK/GameWindow.cs +++ b/Source/OpenTK/GameWindow.cs @@ -240,6 +240,8 @@ namespace OpenTK glWindow.Closing += delegate(object sender, CancelEventArgs e) { OnClosingInternal(e); }; glWindow.Closed += delegate(object sender, EventArgs e) { OnClosedInternal(e); }; //glWindow.WindowInfoChanged += delegate(object sender, EventArgs e) { OnWindowInfoChangedInternal(e); }; + glWindow.WindowBorderChanged += delegate(object sender, EventArgs e) { OnWindowBorderChangedInternal(e); }; + glWindow.WindowStateChanged += delegate(object sender, EventArgs e) { OnWindowStateChangedInternal(e); }; } catch (Exception e) { @@ -379,6 +381,28 @@ namespace OpenTK #endregion + #region OnWindowBorderChangedInternal + + void OnWindowBorderChangedInternal(EventArgs e) + { + OnWindowBorderChanged(e); + + WindowBorderChanged(this, EventArgs.Empty); + } + + #endregion + + #region OnWindowStateChangedInternal + + void OnWindowStateChangedInternal(EventArgs e) + { + OnWindowStateChanged(e); + + WindowStateChanged(this, e); + } + + #endregion + #region OnUpdateFrameInternal private void OnUpdateFrameInternal(FrameEventArgs e) @@ -456,6 +480,20 @@ namespace OpenTK protected virtual void OnWindowInfoChanged(EventArgs e) { } + /// + /// Called when the WindowBorder for this GameWindow has changed. + /// + /// Not used. + protected virtual void OnWindowBorderChanged(EventArgs e) + { } + + /// + /// Called when the WindowState for this GameWindow has changed. + /// + /// Not used. + protected virtual void OnWindowStateChanged(EventArgs e) + { } + #endregion #region --- Public Members --- @@ -1440,6 +1478,16 @@ namespace OpenTK /// public event EventHandler FocusedChanged = delegate { }; + /// + /// Occurs when the property of the window changes. + /// + public event EventHandler WindowBorderChanged = delegate { }; + + /// + /// Occurs when the property of the window changes. + /// + public event EventHandler WindowStateChanged = delegate { }; + /// /// Occurs whenever a character is typed. /// diff --git a/Source/OpenTK/INativeWindow.cs b/Source/OpenTK/INativeWindow.cs index 7513f6d0..dd7685d1 100644 --- a/Source/OpenTK/INativeWindow.cs +++ b/Source/OpenTK/INativeWindow.cs @@ -196,6 +196,16 @@ namespace OpenTK /// event EventHandler FocusedChanged; + /// + /// Occurs when the property of the window changes. + /// + event EventHandler WindowBorderChanged; + + /// + /// Occurs when the property of the window changes. + /// + event EventHandler WindowStateChanged; + /// /// Occurs whenever a character is typed. /// diff --git a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs index 02d75379..5e881b6b 100644 --- a/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs +++ b/Source/OpenTK/Platform/MacOS/CarbonGLNative.cs @@ -678,103 +678,6 @@ namespace OpenTK.Platform.MacOS #endregion - #region INativeGLWindow Members - - public WindowState WindowState - { - get - { - if (windowState == WindowState.Fullscreen) - return WindowState.Fullscreen; - - if (Carbon.API.IsWindowCollapsed(window.WindowRef)) - return WindowState.Minimized; - - - if (Carbon.API.IsWindowInStandardState(window.WindowRef)) - { - return WindowState.Maximized; - } - - return WindowState.Normal; - } - set - { - if (value == WindowState) - return; - - Debug.Print("Switching window state from {0} to {1}", WindowState, value); - - if (WindowState == WindowState.Fullscreen) - { - UnsetFullscreen(); - } - if (WindowState == WindowState.Minimized) - { - API.CollapseWindow(window.WindowRef, false); - } - Point idealSize; - - switch (value) - { - case WindowState.Fullscreen: - SetFullscreen(); - - break; - - case WindowState.Maximized: - // hack because mac os has no concept of maximized. Instead windows are "zoomed" - // meaning they are maximized up to their reported ideal size. So we report a - // large ideal size. - idealSize = new Point(9000, 9000); - API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomOut, ref idealSize); - break; - - case WindowState.Normal: - if (WindowState == WindowState.Maximized) - { - idealSize = new Point(); - API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomIn, ref idealSize); - } - break; - - case WindowState.Minimized: - API.CollapseWindow(window.WindowRef, true); - - break; - } - - windowState = value; - - OnResize(); - } - } - - public WindowBorder WindowBorder - { - get - { - return windowBorder; - } - set - { - windowBorder = value; - - if (windowBorder == WindowBorder.Resizable) - { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, - WindowAttributes.NoAttributes); - } - else if (windowBorder == WindowBorder.Fixed) - { - API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, - WindowAttributes.Resizable | WindowAttributes.FullZoom); - } - } - } - - #endregion - #region INativeWindow Members public string Title @@ -802,18 +705,6 @@ namespace OpenTK.Platform.MacOS } } - public System.Drawing.Icon Icon - { - get - { - throw new NotImplementedException(); - } - set - { - throw new NotImplementedException(); - } - } - public bool Focused { get { throw new NotImplementedException(); } @@ -921,6 +812,108 @@ namespace OpenTK.Platform.MacOS throw new NotImplementedException(); } + public WindowState WindowState + { + get + { + if (windowState == WindowState.Fullscreen) + return WindowState.Fullscreen; + + if (Carbon.API.IsWindowCollapsed(window.WindowRef)) + return WindowState.Minimized; + + + if (Carbon.API.IsWindowInStandardState(window.WindowRef)) + { + return WindowState.Maximized; + } + + return WindowState.Normal; + } + set + { + if (value == WindowState) + return; + + Debug.Print("Switching window state from {0} to {1}", WindowState, value); + + if (WindowState == WindowState.Fullscreen) + { + UnsetFullscreen(); + } + if (WindowState == WindowState.Minimized) + { + API.CollapseWindow(window.WindowRef, false); + } + Point idealSize; + + switch (value) + { + case WindowState.Fullscreen: + SetFullscreen(); + + break; + + case WindowState.Maximized: + // hack because mac os has no concept of maximized. Instead windows are "zoomed" + // meaning they are maximized up to their reported ideal size. So we report a + // large ideal size. + idealSize = new Point(9000, 9000); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomOut, ref idealSize); + break; + + case WindowState.Normal: + if (WindowState == WindowState.Maximized) + { + idealSize = new Point(); + API.ZoomWindowIdeal(window.WindowRef, WindowPartCode.inZoomIn, ref idealSize); + } + break; + + case WindowState.Minimized: + API.CollapseWindow(window.WindowRef, true); + + break; + } + + windowState = value; + + if (WindowStateChanged != null) + WindowStateChanged(this, EventArgs.Empty); + + OnResize(); + } + } + + public WindowBorder WindowBorder + { + get + { + return windowBorder; + } + set + { + if (windowBorder != value) + { + windowBorder = value; + + if (windowBorder == WindowBorder.Resizable) + { + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.Resizable | WindowAttributes.FullZoom, + WindowAttributes.NoAttributes); + } + else if (windowBorder == WindowBorder.Fixed) + { + API.ChangeWindowAttributes(window.WindowRef, WindowAttributes.NoAttributes, + WindowAttributes.Resizable | WindowAttributes.FullZoom); + } + + if (WindowBorderChanged != null) + WindowBorderChanged(this, EventArgs.Empty); + } + } + } + public event EventHandler Idle; public event EventHandler Load; @@ -949,6 +942,10 @@ namespace OpenTK.Platform.MacOS public event EventHandler FocusedChanged; + public event EventHandler WindowBorderChanged; + + public event EventHandler WindowStateChanged; + public event EventHandler KeyPress; #endregion diff --git a/Source/OpenTK/Platform/Windows/WinGLNative.cs b/Source/OpenTK/Platform/Windows/WinGLNative.cs index 6a6fc8e8..349b01bb 100644 --- a/Source/OpenTK/Platform/Windows/WinGLNative.cs +++ b/Source/OpenTK/Platform/Windows/WinGLNative.cs @@ -797,6 +797,9 @@ namespace OpenTK.Platform.Windows break; } + if (WindowStateChanged != null) + WindowStateChanged(this, EventArgs.Empty); + if (command != 0) Functions.ShowWindow(window.WindowHandle, command); } @@ -849,6 +852,9 @@ namespace OpenTK.Platform.Windows SetWindowPosFlags.FRAMECHANGED); Visible = true; + + if (WindowBorderChanged != null) + WindowBorderChanged(this, EventArgs.Empty); } } @@ -907,6 +913,10 @@ namespace OpenTK.Platform.Windows public event EventHandler FocusedChanged; + public event EventHandler WindowBorderChanged; + + public event EventHandler WindowStateChanged; + public event EventHandler KeyPress; #endregion diff --git a/Source/OpenTK/Platform/X11/X11GLNative.cs b/Source/OpenTK/Platform/X11/X11GLNative.cs index c69f6243..f0e506c3 100644 --- a/Source/OpenTK/Platform/X11/X11GLNative.cs +++ b/Source/OpenTK/Platform/X11/X11GLNative.cs @@ -516,7 +516,7 @@ namespace OpenTK.Platform.X11 #region INativeWindow Members - #region ProcessEvents + #region ProcessEvents public void ProcessEvents() { @@ -839,6 +839,181 @@ namespace OpenTK.Platform.X11 #endregion + #region WindowState + + public OpenTK.WindowState WindowState + { + get + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr atom; + //XWindowAttributes attributes; + bool fullscreen = false; + int maximized = 0; + bool minimized = false; + + Functions.XGetWindowProperty(window.Display, window.WindowHandle, + _atom_net_wm_state, IntPtr.Zero, new IntPtr(256), false, + IntPtr.Zero, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0 && prop != IntPtr.Zero) + { + for (int i = 0; i < (long)nitems; i++) + { + atom = (IntPtr)Marshal.ReadIntPtr(prop, i * IntPtr.Size); + + if (atom == _atom_net_wm_state_maximized_horizontal || + atom == _atom_net_wm_state_maximized_vertical) + maximized++; + else if (atom == _atom_net_wm_state_minimized) + minimized = true; + else if (atom == _atom_net_wm_state_fullscreen) + fullscreen = true; + } + Functions.XFree(prop); + } + + if (minimized) + return OpenTK.WindowState.Minimized; + else if (maximized == 2) + return OpenTK.WindowState.Maximized; + else if (fullscreen) + return OpenTK.WindowState.Fullscreen; + /* + attributes = new XWindowAttributes(); + Functions.XGetWindowAttributes(window.Display, window.WindowHandle, ref attributes); + if (attributes.map_state == MapState.IsUnmapped) + return (OpenTK.WindowState)(-1); + */ + return OpenTK.WindowState.Normal; + } + set + { + OpenTK.WindowState current_state = this.WindowState; + + if (current_state == value) + return; + + Debug.Print("GameWindow {0} changing WindowState from {1} to {2}.", window.WindowHandle.ToString(), + current_state.ToString(), value.ToString()); + + if (current_state == OpenTK.WindowState.Minimized) + Functions.XMapWindow(window.Display, window.WindowHandle); + else if (current_state == OpenTK.WindowState.Fullscreen) + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_remove, + _atom_net_wm_state_fullscreen, + IntPtr.Zero); + else if (current_state == OpenTK.WindowState.Maximized) + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_toggle, + _atom_net_wm_state_maximized_horizontal, + _atom_net_wm_state_maximized_vertical); + + Functions.XSync(window.Display, false); + + // We can't resize the window if its border is fixed, so make it resizable first. + bool temporary_resizable = false; + WindowBorder previous_state = WindowBorder; + if (WindowBorder != WindowBorder.Resizable) + { + temporary_resizable = true; + WindowBorder = WindowBorder.Resizable; + } + + switch (value) + { + case OpenTK.WindowState.Normal: + Functions.XRaiseWindow(window.Display, window.WindowHandle); + + break; + + case OpenTK.WindowState.Maximized: + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, + _atom_net_wm_state_maximized_horizontal, + _atom_net_wm_state_maximized_vertical); + Functions.XRaiseWindow(window.Display, window.WindowHandle); + + break; + + case OpenTK.WindowState.Minimized: + // Todo: multiscreen support + Functions.XIconifyWindow(window.Display, window.WindowHandle, window.Screen); + + break; + + case OpenTK.WindowState.Fullscreen: + //_previous_window_border = this.WindowBorder; + //this.WindowBorder = WindowBorder.Hidden; + Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, + _atom_net_wm_state_fullscreen, IntPtr.Zero); + Functions.XRaiseWindow(window.Display, window.WindowHandle); + + break; + } + + if (temporary_resizable) + WindowBorder = previous_state; + + if (WindowStateChanged != null) + WindowStateChanged(this, EventArgs.Empty); + } + } + + #endregion + + #region WindowBorder + + public OpenTK.WindowBorder WindowBorder + { + get + { + if (IsWindowBorderHidden) + return WindowBorder.Hidden; + + if (IsWindowBorderResizable) + return WindowBorder.Resizable; + else + return WindowBorder.Fixed; + } + set + { + if (WindowBorder == value) + return; + + if (WindowBorder == WindowBorder.Hidden) + EnableWindowDecorations(); + + switch (value) + { + case WindowBorder.Fixed: + Debug.Print("Making WindowBorder fixed."); + SetWindowMinMax((short)Width, (short)Height, (short)Width, (short)Height); + + break; + + case WindowBorder.Resizable: + Debug.Print("Making WindowBorder resizable."); + SetWindowMinMax(_min_width, _min_height, -1, -1); + + break; + + case WindowBorder.Hidden: + Debug.Print("Making WindowBorder hidden."); + DisableWindowDecorations(); + + break; + } + + if (WindowBorderChanged != null) + WindowBorderChanged(this, EventArgs.Empty); + } + } + + #endregion + #region Events public event EventHandler Load; @@ -863,6 +1038,10 @@ namespace OpenTK.Platform.X11 public event EventHandler FocusedChanged; + public event EventHandler WindowBorderChanged; + + public event EventHandler WindowStateChanged; + public event EventHandler KeyPress; #endregion @@ -1128,175 +1307,6 @@ namespace OpenTK.Platform.X11 #endregion - #region publicOpenTK.WindowState WindowState - - public OpenTK.WindowState WindowState - { - get - { - IntPtr actual_atom; - int actual_format; - IntPtr nitems; - IntPtr bytes_after; - IntPtr prop = IntPtr.Zero; - IntPtr atom; - //XWindowAttributes attributes; - bool fullscreen = false; - int maximized = 0; - bool minimized = false; - - Functions.XGetWindowProperty(window.Display, window.WindowHandle, - _atom_net_wm_state, IntPtr.Zero, new IntPtr (256), false, - IntPtr.Zero, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); - - if ((long)nitems > 0 && prop != IntPtr.Zero) - { - for (int i = 0; i < (long)nitems; i++) - { - atom = (IntPtr)Marshal.ReadIntPtr(prop, i * IntPtr.Size); - - if (atom == _atom_net_wm_state_maximized_horizontal || - atom == _atom_net_wm_state_maximized_vertical) - maximized++; - else if (atom == _atom_net_wm_state_minimized) - minimized = true; - else if (atom == _atom_net_wm_state_fullscreen) - fullscreen = true; - } - Functions.XFree(prop); - } - - if (minimized) - return OpenTK.WindowState.Minimized; - else if (maximized == 2) - return OpenTK.WindowState.Maximized; - else if (fullscreen) - return OpenTK.WindowState.Fullscreen; -/* - attributes = new XWindowAttributes(); - Functions.XGetWindowAttributes(window.Display, window.WindowHandle, ref attributes); - if (attributes.map_state == MapState.IsUnmapped) - return (OpenTK.WindowState)(-1); -*/ - return OpenTK.WindowState.Normal; - } - set - { - OpenTK.WindowState current_state = this.WindowState; - - if (current_state == value) - return; - - Debug.Print("GameWindow {0} changing WindowState from {1} to {2}.", window.WindowHandle.ToString(), - current_state.ToString(), value.ToString()); - - if (current_state == OpenTK.WindowState.Minimized) - Functions.XMapWindow(window.Display, window.WindowHandle); - else if (current_state == OpenTK.WindowState.Fullscreen) - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_remove, - _atom_net_wm_state_fullscreen, - IntPtr.Zero); - else if (current_state == OpenTK.WindowState.Maximized) - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_toggle, - _atom_net_wm_state_maximized_horizontal, - _atom_net_wm_state_maximized_vertical); - - Functions.XSync(window.Display, false); - - // We can't resize the window if its border is fixed, so make it resizable first. - bool temporary_resizable = false; - WindowBorder previous_state = WindowBorder; - if (WindowBorder != WindowBorder.Resizable) - { - temporary_resizable = true; - WindowBorder = WindowBorder.Resizable; - } - - switch (value) - { - case OpenTK.WindowState.Normal: - Functions.XRaiseWindow(window.Display, window.WindowHandle); - - break; - - case OpenTK.WindowState.Maximized: - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, - _atom_net_wm_state_maximized_horizontal, - _atom_net_wm_state_maximized_vertical); - Functions.XRaiseWindow(window.Display, window.WindowHandle); - - break; - - case OpenTK.WindowState.Minimized: - // Todo: multiscreen support - Functions.XIconifyWindow(window.Display, window.WindowHandle, window.Screen); - - break; - - case OpenTK.WindowState.Fullscreen: - //_previous_window_border = this.WindowBorder; - //this.WindowBorder = WindowBorder.Hidden; - Functions.SendNetWMMessage(window, _atom_net_wm_state, _atom_add, - _atom_net_wm_state_fullscreen, IntPtr.Zero); - Functions.XRaiseWindow(window.Display, window.WindowHandle); - - break; - } - - if (temporary_resizable) - WindowBorder = previous_state; - } - } - - #endregion - - #region public OpenTK.WindowBorder WindowBorder - - public OpenTK.WindowBorder WindowBorder - { - get - { - if (IsWindowBorderHidden) - return WindowBorder.Hidden; - - if (IsWindowBorderResizable) - return WindowBorder.Resizable; - else - return WindowBorder.Fixed; - } - set - { - if (WindowBorder == value) - return; - - if (WindowBorder == WindowBorder.Hidden) - EnableWindowDecorations(); - - switch (value) - { - case WindowBorder.Fixed: - Debug.Print("Making WindowBorder fixed."); - SetWindowMinMax((short)Width, (short)Height, (short)Width, (short)Height); - - break; - - case WindowBorder.Resizable: - Debug.Print("Making WindowBorder resizable."); - SetWindowMinMax(_min_width, _min_height, -1, -1); - - break; - - case WindowBorder.Hidden: - Debug.Print("Making WindowBorder hidden."); - DisableWindowDecorations(); - - break; - } - } - } - - #endregion - #endregion #region IDisposable Members