#region --- License --- /* Copyright (c) 2007 Stefanos Apostolopoulos * See license.txt for license info */ #endregion using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; using System.Diagnostics; //using OpenTK.OpenGL; namespace OpenTK.Platform.X11 { /// /// Drives GameWindow on X11. /// This class supports OpenTK, and is not intended for use by OpenTK programs. /// internal sealed class X11GLNative : INativeGLWindow, IDisposable { #region --- Fields --- private X11GLContext glContext; private WindowInfo window = new WindowInfo(); private DisplayMode mode = new DisplayMode(); // Number of pending events. private int pending = 0; // C# ResizeEventArgs private ResizeEventArgs resizeEventArgs = new ResizeEventArgs(); // Low level X11 resize request // Event used for event loop. private XEvent e = new XEvent(); private bool disposed; private bool created; #endregion #region --- Public Constructors --- /// /// Constructs and initializes a new X11GLNative window. /// Call CreateWindow to create the actual render window. /// public X11GLNative() { Debug.Print("Native window driver: {0}", this.ToString()); } #endregion #region --- INativeGLWindow Members --- #region public void CreateWindow(DisplayMode mode) /// /// Opens a new render window with the given DisplayMode. /// /// The DisplayMode of the render window. /// /// Creates the window visual and colormap. Associates the colormap/visual /// with the window and raises the window on top of the window stack. /// /// Colormap creation is currently disabled. /// /// public void CreateWindow(DisplayMode mode) { if (created) { throw new ApplicationException("Render window already exists!"); } else { Debug.Print("Creating native window with mode: {0}", mode.ToString()); Debug.Indent(); window.Display = API.OpenDisplay(null); // null == default display if (window.Display == IntPtr.Zero) { throw new Exception("Could not open connection to X"); } window.Screen = API.DefaultScreen(window.Display); window.RootWindow = API.RootWindow(window.Display, window.Screen); Debug.Print( "Display: {0}, Screen {1}, Root window: {2}", window.Display, window.Screen, window.RootWindow ); glContext = new X11GLContext(window, mode); window.VisualInfo = glContext.CreateVisual(); // Create a window on this display using the visual above Debug.Write("Creating output window... "); XSetWindowAttributes attributes = new XSetWindowAttributes(); attributes.colormap = glContext.colormap; attributes.event_mask = (IntPtr)(EventMask.StructureNotifyMask | EventMask.ExposureMask); SetWindowValuemask mask = SetWindowValuemask.ColorMap | SetWindowValuemask.EventMask; window.Handle = Functions.XCreateWindow(window.Display, window.RootWindow, 0, 0, mode.Width, mode.Height, 0, glContext.XVisualInfo.depth, (int)CreateWindowArgs.InputOutput, glContext.XVisualInfo.visual, (UIntPtr)mask, ref attributes); if (window.Handle == IntPtr.Zero) { throw new Exception("Could not create window."); } /* // Set the window hints XSizeHints hints = new XSizeHints(); hints.x = 0; hints.y = 0; hints.width = 640; hints.height = 480; hints.flags = (IntPtr)(XSizeHintsFlags.USSize | XSizeHintsFlags.USPosition); Functions.XSetWMNormalHints(window.Display, window.Handle, ref hints); XTextProperty text = new XTextProperty(); text.value = "OpenTK Game Window"; text.format = 8; Functions.XSetWMName(window.Display, window.Handle, ref text); Functions.XSetWMProperties( display, window, name, name, 0, // None null, 0, hints );*/ Debug.Print("done! (id: {0})", window.Handle); glContext.windowInfo.Handle = window.Handle; glContext.CreateContext(null, true); API.MapRaised(window.Display, window.Handle); Debug.WriteLine("Mapped window."); //glContext.MakeCurrent(); Debug.WriteLine("Our shiny new context is now current - ready to rock 'n' roll!"); Debug.Unindent(); created = true; } } #endregion #region public void Exit() public void Exit() { Debug.WriteLine("X11GLNative shutdown sequence initiated."); quit = true; Functions.XDestroyWindow(window.Display, window.Handle); window = null; glContext.Dispose(); glContext = null; } #endregion #region public void ProcessEvents() public void ProcessEvents() { // Process all pending events while (true) { pending = Functions.XPending(window.Display); if (pending == 0) return; Functions.XNextEvent(window.Display, ref e); Debug.WriteLine(String.Format("Event: {0} ({1} pending)", e.type, pending)); // Respond to the event e switch (e.type) { case XEventName.ReparentNotify: // TODO: Is there a more suitable place to raise the Create event? // ReparentNotify seems to be the first event raised on window creation. this.OnCreate(EventArgs.Empty); break; case XEventName.CreateNotify: // A child was exists - nothing to do break; case XEventName.DestroyNotify: //glContext.Dispose(); //window = null; //glContext = null; //quit = true; Debug.WriteLine("Window destroyed, shutting down."); break; case XEventName.ConfigureNotify: // If the window size changed, raise the C# Resize event. if (e.ConfigureEvent.width != mode.Width || e.ConfigureEvent.height != mode.Height) { Debug.WriteLine( String.Format( "New res: {0}x{1}", e.ConfigureEvent.width, e.ConfigureEvent.height ) ); resizeEventArgs.Width = e.ConfigureEvent.width; resizeEventArgs.Height = e.ConfigureEvent.height; this.OnResize(resizeEventArgs); } break; default: Debug.WriteLine(String.Format("{0} event was not handled", e.type)); break; } } } #endregion #region public event CreateEvent Create; public event CreateEvent Create; private void OnCreate(EventArgs e) { if (this.Create != null) { Debug.Print("Create event fired from window: {0}", window.ToString()); this.Create(this, e); } } #endregion #region public bool Exists /// /// Returns true if a render window/context exists. /// public bool Exists { get { return created; } } #endregion #region public bool Quit private bool quit; public bool Quit { get { return quit; } } #endregion #region public bool IsIdle public bool IsIdle { get { throw new Exception("The method or operation is not implemented."); } } #endregion #region public bool Fullscreen public bool Fullscreen { get { return false; //throw new Exception("The method or operation is not implemented."); } set { //throw new Exception("The method or operation is not implemented."); } } #endregion #region public IGLContext Context public OpenTK.Platform.IGLContext Context { get { return glContext; } } #endregion #region public IntPtr Handle /// /// Gets the current window handle. /// public IntPtr Handle { get { return this.window.Handle; } } #endregion #region public IWindowInfo WindowInfo public IWindowInfo WindowInfo { get { return window; } } #endregion #endregion #region --- IResizable Members --- #region public int Width public int Width { get { return mode.Width; } set {/* // Clear event struct //Array.Clear(xresize.pad, 0, xresize.pad.Length); // Set requested parameters xresize.ResizeRequest.type = EventType.ResizeRequest; xresize.ResizeRequest.display = this.display; xresize.ResizeRequest.width = value; xresize.ResizeRequest.height = mode.Width; API.SendEvent( this.display, this.window, false, EventMask.StructureNotifyMask, ref xresize );*/ } } #endregion #region public int Height public int Height { get { return mode.Height; } set {/* // Clear event struct //Array.Clear(xresize.pad, 0, xresize.pad.Length); // Set requested parameters xresize.ResizeRequest.type = EventType.ResizeRequest; xresize.ResizeRequest.display = this.display; xresize.ResizeRequest.width = mode.Width; xresize.ResizeRequest.height = value; API.SendEvent( this.display, this.window, false, EventMask.StructureNotifyMask, ref xresize );*/ } } #endregion #region public event ResizeEvent Resize public event ResizeEvent Resize; private void OnResize(ResizeEventArgs e) { mode.Width = e.Width; mode.Height = e.Height; if (this.Resize != null) { this.Resize(this, e); } } #endregion #endregion #region --- IDisposable Members --- public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool manuallyCalled) { if (!disposed) { if (window != null) Functions.XDestroyWindow(window.Display, window.Handle); // Kills connection to the X-Server. We don't want that, // 'cause it kills the ExampleLauncher too. //API.CloseDisplay(display); if (manuallyCalled) { if (glContext != null) glContext.Dispose(); } disposed = true; } } ~X11GLNative() { this.Dispose(false); } #endregion } }