public void Run(INativeWindowStartupInfo startupInfo) { GdiplusStartupInput gdiPlusStartupInput = GdiplusStartupInput.CreateV1(); GdiPlusAPI.CheckStatus(GdiPlusAPI.GdiplusStartup(out var gdiPlusToken, ref gdiPlusStartupInput, out _)); IntPtr hInstance = Win32API.GetModuleHandleW(null); var windowClass = new WNDCLASSEXW(); windowClass.cbSize = (uint)Marshal.SizeOf <WNDCLASSEXW>(); windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = hInstance; windowClass.lpszClassName = WindowClassName; var atom = Win32API.RegisterClassExW(ref windowClass); if (atom == 0) { throw new InvalidOperationException("Failed to register window class."); } try { const int CW_USEDEFAULT = unchecked ((int)0x80000000); IntPtr hwnd = Win32API.CreateWindowExW( 0, WindowClassName, startupInfo.Title, Win32WindowStyle.WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, startupInfo.Width, startupInfo.Height, IntPtr.Zero, IntPtr.Zero, hInstance, IntPtr.Zero ); if (hwnd == IntPtr.Zero) { throw new InvalidOperationException("Failed to create a window."); } var window = new Win32Window(hwnd, startupInfo); windows.Add(hwnd, window); startupInfo.OnCreate(window); try { Win32API.ShowWindow(hwnd, Win32ShowWindowCommand.SW_SHOWNORMAL); MSG msg = new MSG(); int mRet; while ((mRet = Win32API.GetMessageW(ref msg, IntPtr.Zero, 0, 0)) != 0) { if (mRet == -1) { throw new InvalidOperationException("Failed to retrieve a message."); } Win32API.TranslateMessage(ref msg); Win32API.DispatchMessageW(ref msg); } } finally { // usually window will be destroyed before this point, unless error occurs if (Win32API.DestroyWindow(hwnd) == 0) { // todo: log warning ? } } } finally { if (Win32API.UnregisterClassW(WindowClassName, hInstance) == 0) { // todo: log warning } // todo: use separate finally for GDI+ GdiPlusAPI.GdiplusShutdown(gdiPlusToken); } }
public void Run(INativeWindowStartupInfo window) { XSetWindowAttributes attr = new XSetWindowAttributes(); attr.border_pixel = 0; attr.bit_gravity = 1 /*NorthWestGravity */; attr.event_mask = XEventMask.ExposureMask | XEventMask.ButtonPressMask | XEventMask.ButtonReleaseMask | XEventMask.FocusChangeMask | XEventMask.KeyPressMask | XEventMask.KeyReleaseMask | XEventMask.PointerMotionMask | XEventMask.StructureNotifyMask; attr.colormap = graphics.ColorMap; ulong windowId = LibX11.XCreateWindow ( display, graphics.RootWindow, 0, 0, (uint)window.Width, (uint)window.Height, 1, graphics.PictFormat.depth, WindowClass.InputOutput, graphics.Visual, XSetWindowAttributeMask.CWBorderPixel | XSetWindowAttributeMask.CWBitGravity | XSetWindowAttributeMask.CWEventMask | XSetWindowAttributeMask.CWColormap, ref attr ); X11Window nativeWindow = new X11Window(display, windowId, Invalidate); window.OnCreate(nativeWindow); nativeWindow.SetTitle(window.Title); var protocols = new[] { WM_DELETE_WINDOW }; LibX11.XSetWMProtocols(display, windowId, protocols, protocols.Length); LibX11.XMapWindow(display, windowId); LibX11.XFlush(display); byte[] textBuffer = new byte[32]; bool autoRepeat = false; bool windowClosed = false; while (!windowClosed) { LibX11.XNextEvent(display, out XEvent evt); if (evt.type == XEventType.Expose) { var rect = new Rectangle(evt.ExposeEvent.x, evt.ExposeEvent.y, evt.ExposeEvent.width, evt.ExposeEvent.height); Invalidate(rect); } else if (evt.type == XEventType.MotionNotify) { window.OnMouseMove(new Point(evt.MotionEvent.x, evt.MotionEvent.y)); } else if (evt.type == XEventType.ConfigureNotify) { Size clientArea = new Size(evt.ConfigureEvent.width, evt.ConfigureEvent.height); window.OnResize(clientArea); } else if (evt.type == XEventType.ButtonPress) { window.OnMouseButtonDown( GetMouseButton(evt.ButtonEvent.button), new Point(evt.ButtonEvent.x, evt.ButtonEvent.y), GetModifierKey(evt.ButtonEvent.state) ); } else if (evt.type == XEventType.ButtonRelease) { window.OnMouseButtonUp( GetMouseButton(evt.ButtonEvent.button), new Point(evt.ButtonEvent.x, evt.ButtonEvent.y) ); } else if (evt.type == XEventType.KeyPress) { // XLookupString returns no more than the requested number of characters, but it also writes a zero-byte after them int charCount = LibX11.XLookupString(ref evt.KeyEvent, textBuffer, textBuffer.Length - 1, out var keySym, IntPtr.Zero); var keyCode = X11KeyMap.GetKeyCode(keySym); if (keyCode == NKeyCode.Unknown) { keySym = LibX11.XLookupKeysym(ref evt.KeyEvent, 0); keyCode = X11KeyMap.GetKeyCode(keySym); } window.OnKeyDown(keyCode, GetModifierKey(evt.KeyEvent.state), autoRepeat); autoRepeat = false; if (charCount > 0) { string text = Encoding.UTF8.GetString(textBuffer, 0, charCount); StringBuilder sb = new StringBuilder(text.Length); foreach (char c in text) { if (!char.IsControl(c)) { sb.Append(c); } } if (sb.Length > 0) { window.OnTextInput(sb.ToString()); } } } else if (evt.type == XEventType.KeyRelease) { if (LibX11.XPending(display) > 0) { LibX11.XPeekEvent(display, out XEvent nextEvent); autoRepeat = nextEvent.type == XEventType.KeyPress && nextEvent.KeyEvent.time == evt.KeyEvent.time && nextEvent.KeyEvent.keycode == evt.KeyEvent.keycode; } if (!autoRepeat) { // XLookupString returns no more than the requested number of characters, but it also writes a zero-byte after them LibX11.XLookupString(ref evt.KeyEvent, textBuffer, textBuffer.Length - 1, out var keySym, IntPtr.Zero); var keyCode = X11KeyMap.GetKeyCode(keySym); if (keyCode == NKeyCode.Unknown) { keySym = LibX11.XLookupKeysym(ref evt.KeyEvent, 0); keyCode = X11KeyMap.GetKeyCode(keySym); } window.OnKeyUp(keyCode); } } else if (evt.type == XEventType.FocusIn) { if (IsWindowActivationEvent(evt.FocusChangeEvent.mode, evt.FocusChangeEvent.detail)) { window.OnActivated(); } } else if (evt.type == XEventType.FocusOut) { if (IsWindowActivationEvent(evt.FocusChangeEvent.mode, evt.FocusChangeEvent.detail)) { window.OnDeactivated(); } } else if (evt.type == XEventType.ClientMessage) { var cevt = evt.ClientMessageEvent; if (cevt.message_type == WM_PROTOCOLS && cevt.data0 == WM_DELETE_WINDOW) { windowClosed = true; } else if (cevt.message_type == XA_NWINDOWS_PAINT_COMPLETE) { paintQueued = false; } } if (invalidatedArea != null && !paintQueued) { Rectangle area = invalidatedArea.Value; invalidatedArea = null; using (X11Image image = graphics.CreateImage(area.Width, area.Height)) { using (X11Canvas canvas = graphics.CreateCanvas(image.PixmapId)) { canvas.SetOrigin(area.X, area.Y); window.OnPaint(canvas, area); } using (X11Canvas canvas = graphics.CreateCanvas(windowId)) { canvas.DrawImage(image, area.X, area.Y); } } paintQueued = true; var message = XEvent.CreateClientMessage(windowId, XA_NWINDOWS_PAINT_COMPLETE); LibX11.XSendEvent(display, windowId, 0, XEventMask.NoEventMask, ref message); } } }
internal Win32Window(IntPtr windowHandle, INativeWindowStartupInfo startupInfo) { this.windowHandle = windowHandle; this.startupInfo = startupInfo; }