/// <inheritdoc /> protected override void SetupPlatform(Configurator config) { // todo: load libraries - if any // probably XInput // Initialize audio - Try to create WasApi - otherwise return the fake context so execution can go on. Audio = WasApiAudioContext.TryCreate() ?? (AudioContext) new NullAudioContext(); Engine.Log.Trace("Audio init complete.", MessageSource.Win32); PopulateKeyNames(); if (IsWindows10CreatorsUpdateOrGreaterWin32) { User32.SetProcessDpiAwarenessContext(DpiAwarenessContext.DpiAwarenessContextPerMonitorAwareV2); } else if (IsWindows81OrGreater) { User32.SetProcessDpiAwareness(ProcessDpiAwareness.ProcessPerMonitorDpiAware); } else { User32.SetProcessDPIAware(); } RegisterWindowClass(); CreateHelperWindow(); PollMonitors(); Engine.Log.Trace("Platform init complete.", MessageSource.Win32); var windowInitialSize = new Rect { Right = (int)config.HostSize.X, Bottom = (int)config.HostSize.Y }; Win32Window.GetFullWindowRect(Win32Window.DEFAULT_WINDOW_STYLE, Win32Window.DEFAULT_WINDOW_STYLE_EX, DEFAULT_DPI, ref windowInitialSize); int initialWidth = windowInitialSize.Right - windowInitialSize.Left; int initialHeight = windowInitialSize.Bottom - windowInitialSize.Top; IntPtr handle = User32.CreateWindowEx( Win32Window.DEFAULT_WINDOW_STYLE_EX, CLASS_NAME, config.HostTitle, Win32Window.DEFAULT_WINDOW_STYLE, (int)CreateWindowFlags.CW_USEDEFAULT, (int)CreateWindowFlags.CW_USEDEFAULT, // Position - default initialWidth, initialHeight, // Size - initial IntPtr.Zero, // No parent window IntPtr.Zero, // No window menu Kernel32.GetModuleHandle(null), IntPtr.Zero ); if (handle == IntPtr.Zero) { CheckError("Couldn't create window.", true); return; } Engine.Log.Trace("Window created.", MessageSource.Win32); // Create graphics context - OpenGL. GraphicsContext context = null; try { var wgl = new WglGraphicsContext(); wgl.Init(handle, this); if (wgl.Valid) { context = wgl; } } catch (Exception ex) { Engine.Log.Warning($"Couldn't create WGL context, falling back to MESA if possible.\n{ex}", MessageSource.Win32); } if (context == null) { try { var gallium = new GalliumGraphicsContext(); gallium.Init(handle, this); if (gallium.Valid) { context = gallium; } } catch (Exception ex) { Engine.SubmitError(new Exception("Couldn't create MESA context.", ex)); } } if (context == null) { Engine.SubmitError(new Exception("Couldn't create graphics context!")); return; } // Create Emotion representation of the window. var windowInstance = new Win32Window(handle, context, this); // Adjust window size to account for DPI scaling of the window frame and optionally DPI scaling of the content area. // This cannot be done until we know what monitor it was placed on - so it's done post creation. var rect = new Rect { Right = (int)config.HostSize.X, Bottom = (int)config.HostSize.Y }; rect.ClientToScreen(windowInstance.Handle); windowInstance.GetFullWindowRect(ref rect); User32.SetWindowPos(windowInstance.Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_NOZORDER); Window = windowInstance; }
/// <summary> /// Handler for messages. /// </summary> private unsafe IntPtr WndProc(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam) { // Check if the message is for the main window. Win32Window win = null; if (Window != null && hWnd == ((Win32Window)Window).Handle) { win = (Win32Window)Window; } if (win == null) { // This is the message handling for the hidden helper window // and for a regular window during its initial creation switch (msg) { case WM.CREATE: if (IsWindows10AnniversaryUpdateOrGreaterWin32) { User32.EnableNonClientDpiScaling(hWnd); } break; case WM.DISPLAYCHANGE: PollMonitors(); break; case WM.DEVICECHANGE: break; } return(User32.DefWindowProc(hWnd, msg, wParam, lParam)); } switch (msg) { case WM.GETMINMAXINFO: Rect windowR = win.GetFullWindowRect(0, 0); // ReSharper disable once NotAccessedVariable var mmi = (MinMaxInfo *)lParam; int offX = windowR.Right - windowR.Left; int offY = windowR.Bottom - windowR.Top; mmi->MinTrackSize.X = (int)Engine.Configuration.RenderSize.X + offX; mmi->MinTrackSize.Y = (int)Engine.Configuration.RenderSize.Y + offY; return(IntPtr.Zero); case WM.SIZE: int width = NativeHelpers.LoWord((uint)lParam); int height = NativeHelpers.HiWord((uint)lParam); // Don't send resize event when minimized. if (width != 0 && height != 0) { win.OnResize.Invoke(new Vector2(width, height)); } return(IntPtr.Zero); case WM.SETFOCUS: UpdateFocus(true); return(IntPtr.Zero); case WM.KILLFOCUS: UpdateFocus(false); return(IntPtr.Zero); case WM.SYSCOMMAND: switch ((int)wParam & 0xfff0) { case (int)SysCommand.SC_SCREENSAVE: case (int)SysCommand.SC_MONITORPOWER: if (Window.DisplayMode == DisplayMode.Fullscreen) { // We are running in full screen mode, so disallow // screen saver and screen blanking return(IntPtr.Zero); } break; // User trying to access application menu using ALT? case (int)SysCommand.SC_KEYMENU: return(IntPtr.Zero); } break; case WM.CLOSE: IsOpen = false; return(IntPtr.Zero); // ------------------- INPUT ------------------- case WM.INPUTLANGCHANGE: PopulateKeyNames(); break; case WM.CHAR: case WM.SYSCHAR: case WM.UNICHAR: if (msg == WM.UNICHAR && (int)wParam == 0xFFFF) { // WM_UNICHAR is not sent by Windows, but is sent by some // third-party input method engine // Returning TRUE here announces support for this message return((IntPtr)1); } OnTextInput.Invoke((char)wParam); break; case WM.KEYDOWN: case WM.SYSKEYDOWN: case WM.KEYUP: case WM.SYSKEYUP: var lParamLong = (ulong)lParam; Key key = TranslateKey((ulong)wParam, lParamLong); if (key == Key.Unknown) { break; } // Scan code can be used for debugging purposes. //uint scanCode = NativeHelpers.HiWord(lParamLong) & (uint) 0x1ff; bool up = (((ulong)lParam >> 31) & 1) != 0; if (up && (uint)wParam == (uint)VirtualKey.SHIFT) { // HACK: Release both Shift keys on Shift up event, as when both // are pressed the first release does not emit any event // NOTE: The other half of this is in Update() UpdateKeyStatus(Key.LeftShift, false); UpdateKeyStatus(Key.RightShift, false); } else { UpdateKeyStatus(key, !up); } break; case WM.MOUSEMOVE: int x = NativeHelpers.LoWord((uint)lParam); int y = NativeHelpers.HiWord((uint)lParam); var pos = new Vector2(x, y); MousePosition = Engine.Renderer != null?Engine.Renderer.ScaleMousePosition(pos) : pos; return(IntPtr.Zero); case WM.LBUTTONDOWN: case WM.RBUTTONDOWN: case WM.MBUTTONDOWN: case WM.XBUTTONDOWN: case WM.LBUTTONUP: case WM.RBUTTONUP: case WM.MBUTTONUP: case WM.XBUTTONUP: var mouseKey = MouseKey.Unknown; var buttonDown = false; mouseKey = msg switch { WM.LBUTTONDOWN => MouseKey.Left, WM.LBUTTONUP => MouseKey.Left, WM.RBUTTONDOWN => MouseKey.Right, WM.RBUTTONUP => MouseKey.Right, WM.MBUTTONDOWN => MouseKey.Middle, WM.MBUTTONUP => MouseKey.Middle, _ => mouseKey }; if (msg == WM.LBUTTONDOWN || msg == WM.RBUTTONDOWN || msg == WM.MBUTTONDOWN || msg == WM.XBUTTONDOWN) { buttonDown = true; } var nonePressed = true; foreach (bool keyDown in _mouseKeys) { if (keyDown) { nonePressed = false; } } if (nonePressed) { User32.SetCapture(win.Handle); } UpdateMouseKeyStatus(mouseKey, buttonDown); nonePressed = true; foreach (bool keyDown in _mouseKeys) { if (keyDown) { nonePressed = false; } } if (nonePressed) { User32.ReleaseCapture(); } if (msg == WM.XBUTTONDOWN || msg == WM.XBUTTONUP) { return((IntPtr)1); } return(IntPtr.Zero); case WM.MOUSEWHEEL: var scrollAmount = (short)NativeHelpers.HiWord((ulong)wParam); UpdateScroll(scrollAmount / 120f); return(IntPtr.Zero); } return(User32.DefWindowProc(hWnd, msg, wParam, lParam)); }