protected override Vector2 GetPosition() { var p = new Point(); User32.ClientToScreen(_windowHandle, ref p); return(new Vector2(p.X, p.Y)); }
protected override void SetSize(Vector2 value) { // Scale the rect, and set it. Rect r = GetFullWindowRect((int)value.X, (int)value.Y); int width = r.Right - r.Left; int height = r.Bottom - r.Top; // Center on the monitor. Monitor monitor = GetMonitorOfWindow(); if (monitor == null) { Engine.Log.Warning("No monitor attached?", MessageSource.Win32); return; } Vector2 center = monitor.Position + new Vector2(monitor.Width, monitor.Height) / 2 - new Vector2(width, height) / 2; User32.SetWindowPos(_windowHandle, (IntPtr)HwndZOrder.HWND_NOTOPMOST, (int)center.X + r.Left, (int)center.Y + r.Top, r.Right - r.Left, r.Bottom - r.Top, WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_SHOWWINDOW ); }
private void RegisterWindowClass() { _wndProcDelegate = WndProc; var wc = new WindowClassEx { Styles = WindowClassStyles.CS_HREDRAW | WindowClassStyles.CS_VREDRAW | WindowClassStyles.CS_OWNDC, WindowProc = _wndProcDelegate, InstanceHandle = Kernel32.GetModuleHandle(null), CursorHandle = User32.LoadCursor(IntPtr.Zero, (IntPtr)SystemCursor.IDC_ARROW), ClassName = CLASS_NAME }; wc.Size = (uint)Marshal.SizeOf(wc); // Load user icon - if any. wc.IconHandle = User32.LoadImage(Kernel32.GetModuleHandle(null), "#32512", ResourceImageType.IMAGE_ICON, 0, 0, LoadResourceFlags.LR_DEFAULTSIZE | LoadResourceFlags.LR_SHARED); if (wc.IconHandle == IntPtr.Zero) { Kernel32.SetLastError(0); // None loaded - load default. wc.IconHandle = User32.LoadImage(IntPtr.Zero, (IntPtr)SystemIcon.IDI_APPLICATION, ResourceImageType.IMAGE_ICON, 0, 0, LoadResourceFlags.LR_DEFAULTSIZE | LoadResourceFlags.LR_SHARED); } ushort windowClass = User32.RegisterClassEx(ref wc); if (windowClass == 0) { CheckError("Win32: Failed to register window class.", true); } CheckError("Win32: Could not register class."); }
/// <inheritdoc /> protected override bool UpdatePlatform() { // Shift keys on windows tend to "stick" // When one key is held, the other doesn't receive down events, and an up event // is sent only to the latter one. To work around this manually query the status of both shift keys every tick. if (IsFocused) { KeyState lShift = User32.GetAsyncKeyState(VirtualKey.LSHIFT); UpdateKeyStatus(Key.LeftShift, lShift.IsPressed); KeyState rShift = User32.GetAsyncKeyState(VirtualKey.RSHIFT); UpdateKeyStatus(Key.RightShift, rShift.IsPressed); } while (User32.PeekMessage(out Message msg, IntPtr.Zero, 0, 0, PeekMessageFlags.PM_REMOVE)) { // Generated by task manager and stuff like that. // The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. if (msg.Value == WM.QUIT) { IsOpen = false; return(false); } User32.TranslateMessage(ref msg); User32.DispatchMessage(ref msg); } if (HostPaused) { User32.WaitMessage(); } return(true); }
protected override void UpdateDisplayMode() { // Get the monitor Monitor monitor = GetMonitorOfWindow(); if (monitor == null) { Engine.Log.Warning("No monitor attached?", MessageSource.Win32); return; } switch (_mode) { case DisplayMode.Windowed: WindowStyles style = DetermineWindowStyle(); IntPtr res = User32.SetWindowLongPtr(_windowHandle, WindowLongFlags.GWL_STYLE, (IntPtr)style); if (res == IntPtr.Zero) { CheckError("Couldn't change display mode to windowed.", true); } if (_windowModeSize != null) { Size = (Vector2)_windowModeSize; _windowModeSize = null; } // Center window on screen. (This will also apply the SetWindowLongPtr changes, and remove the topmost status when exiting out of fullscreen) Position = monitor.Position + (new Vector2(monitor.Width, monitor.Height) / 2 - Size / 2); break; case DisplayMode.Fullscreen: IntPtr resp = User32.SetWindowLongPtr(_windowHandle, WindowLongFlags.GWL_STYLE, (IntPtr)FULLSCREEN_STYLE); if (resp == IntPtr.Zero) { CheckError("Couldn't change display mode to fullscreen, couldn't apply window style.", true); return; } bool successful = User32.SetWindowPos( _windowHandle, (IntPtr)HwndZOrder.HWND_NOTOPMOST, (int)monitor.Position.X, (int)monitor.Position.Y, monitor.Width, monitor.Height, WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_NOCOPYBITS | WindowPositionFlags.SWP_FRAMECHANGED ); if (!successful) { CheckError("Couldn't change display mode to fullscreen, couldn't apply window rect.", true); } // Center cursor on screen/window. User32.SetCursorPos((int)(monitor.Position.X + monitor.Width / 2), (int)(monitor.Position.Y + monitor.Height / 2)); break; } }
public static void GetFullWindowRect(WindowStyles style, WindowExStyles styleEx, uint dpi, ref Rect rect) { if (IsWindows10AnniversaryUpdateOrGreaterWin32) { User32.AdjustWindowRectExForDpi(ref rect, style, false, styleEx, dpi); } else { User32.AdjustWindowRectEx(ref rect, style, false, styleEx); } }
/// <summary> /// Transform the provided rectangle to fit the scale and position of the window. /// </summary> /// <param name="rect">The rect to transform.</param> /// <returns>The transformed rectangle.</returns> public Rect GetFullWindowRect(ref Rect rect) { uint dpi = DEFAULT_DPI; if (IsWindows10AnniversaryUpdateOrGreaterWin32) { dpi = User32.GetDpiForWindow(_windowHandle); } GetFullWindowRect(DetermineWindowStyle(), WindowExStyles.WS_EX_APPWINDOW, dpi, ref rect); return(rect); }
/// <summary> /// Updates key names according to the current keyboard layout. /// </summary> private void PopulateKeyNames() { var state = new byte[256]; // Initialize the key names array. _keyNames = new string[_scanCodes.Length]; for (var key = (int)Key.Space; key < (int)Key.Last; key++) { uint vk; int scanCode = _scanCodes[key]; if (scanCode == -1) { continue; } if (key >= (int)Key.Kp0 && key <= (int)Key.KpAdd) { uint[] vks = { (uint)VirtualKey.NUMPAD0, (uint)VirtualKey.NUMPAD1, (uint)VirtualKey.NUMPAD2, (uint)VirtualKey.NUMPAD3, (uint)VirtualKey.NUMPAD4, (uint)VirtualKey.NUMPAD5, (uint)VirtualKey.NUMPAD6, (uint)VirtualKey.NUMPAD7, (uint)VirtualKey.NUMPAD8, (uint)VirtualKey.NUMPAD9, (uint)VirtualKey.DECIMAL, (uint)VirtualKey.DIVIDE, (uint)VirtualKey.MULTIPLY, (uint)VirtualKey.SUBTRACT, (uint)VirtualKey.ADD }; vk = vks[key - (int)Key.Kp0]; } else { vk = User32.MapVirtualKey((uint)scanCode, VirtualKeyMapType.MAPVK_VSC_TO_VK); } var chars = new StringBuilder(16); int length = User32.ToUnicode(vk, (uint)scanCode, state, chars, chars.Capacity, 0); if (length == -1) { length = User32.ToUnicode(vk, (uint)scanCode, state, chars, chars.Capacity, 0); } if (length < 1) { continue; } var keyName = new char[5]; Kernel32.WideCharToMultiByte(CodePage.CpUtf8, 0, chars, 1, keyName, keyName.Length, IntPtr.Zero, out bool _); _keyNames[key] = NativeHelpers.StringFromNullTerminated(keyName); } }
public Win32Monitor(DisplayDeviceW adapter, DisplayDeviceW?display) { Name = display != null ? display.Value.DeviceString : adapter.DeviceString; AdapterName = adapter.DeviceName; DeviceName = display == null ? "<<Unknown Device>>" : display.Value.DeviceName; Debug.Assert(Name != null); var dm = new DeviceMode(); dm.dmSize = (short)Marshal.SizeOf(dm); User32.EnumDisplaySettingsW(adapter.DeviceName.PadRight(32, '\0'), (int)DisplaySettings.CurrentSettings, ref dm); Win32Platform.CheckError("Enumerating display settings."); Width = dm.dmPelsWidth; Height = dm.dmPelsHeight; IntPtr dc = Gdi32.CreateDCW("DISPLAY", adapter.DeviceName, null, IntPtr.Zero); Debug.Assert(dc != IntPtr.Zero); if (Win32Platform.IsWindows81OrGreater) { WidthPhysical = Gdi32.GetDeviceCaps(dc, DeviceCap.HorizontalSize); HeightPhysical = Gdi32.GetDeviceCaps(dc, DeviceCap.VertSize); } else { WidthPhysical = (int)(dm.dmPelsWidth * 25.4f / Gdi32.GetDeviceCaps(dc, DeviceCap.LogPixelsX)); HeightPhysical = (int)(dm.dmPelsHeight * 25.4f / Gdi32.GetDeviceCaps(dc, DeviceCap.LogPixelsY)); } Gdi32.DeleteDC(dc); if ((adapter.StateFlags & DisplayDeviceStateFlags.ModesPruned) != 0) { ModesPruned = true; } var rect = new Rect { Left = dm.dmPosition.X, Top = dm.dmPosition.Y, Right = dm.dmPosition.X + dm.dmPelsWidth, Bottom = dm.dmPosition.Y + dm.dmPelsHeight }; User32.EnumDisplayMonitors(IntPtr.Zero, ref rect, MonitorCallback, null); Win32Platform.CheckError("Creating monitor."); }
/// <inheritdoc /> public override void DisplayMessageBox(string message) { IntPtr parentWindow = IntPtr.Zero; if (Window != null) { parentWindow = ((Win32Window)Window).Handle; } else if (HelperWindowHandle != IntPtr.Zero) { parentWindow = HelperWindowHandle; } User32.MessageBox(parentWindow, message, "Something went wrong!", (uint)(0x00000000L | 0x00000010L)); }
/// <summary> /// Convert wParam and lParam to a key. /// </summary> private Key TranslateKey(ulong wParam, ulong lParam) { switch (wParam) { // Control keys require special handling. case (uint)VirtualKey.CONTROL when(lParam& 0x01000000) != 0: return(Key.RightControl); case (uint)VirtualKey.CONTROL: { uint time = User32.GetMessageTime(); // HACK: Alt Gr sends Left Ctrl and then Right Alt in close sequence // We only want the Right Alt message, so if the next message is // Right Alt we ignore this (synthetic) Left Ctrl message if (!User32.PeekMessage(out Message next, IntPtr.Zero, 0, 0, PeekMessageFlags.PM_NOREMOVE)) { return(Key.LeftControl); } if (next.Value != WM.KEYDOWN && next.Value != WM.SYSKEYDOWN && next.Value != WM.KEYUP && next.Value != WM.SYSKEYUP) { return(Key.LeftControl); } if ((uint)next.WParam == (uint)VirtualKey.MENU && ((uint)next.LParam & 0x01000000) != 0 && next.Time == time) { // Next message is Right Alt down so discard this return(Key.Unknown); } return(Key.LeftControl); } // IME notifies that keys have been filtered by setting the virtual // key-code to VK_PROCESSKEY case (uint)VirtualKey.PROCESSKEY: return(Key.Unknown); } int index = NativeHelpers.HiWord(lParam) & 0x1FF; if (index < 0 || index >= _keyCodes.Length) { return(Key.Unknown); } return(_keyCodes[index]); }
protected override void SetPosition(Vector2 position) { if (DisplayMode != DisplayMode.Windowed) { return; } // Get the size and scale it. Vector2 size = Size; Rect rect = GetFullWindowRect((int)size.X, (int)size.Y); // Set the size and position. User32.SetWindowPos(_windowHandle, (IntPtr)HwndZOrder.HWND_NOTOPMOST, (int)position.X + rect.Left, (int)position.Y + rect.Top, 0, 0, WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_NOCOPYBITS | WindowPositionFlags.SWP_SHOWWINDOW | WindowPositionFlags.SWP_NOSIZE); }
public static void GetMonitorContentScale(IntPtr monitor, out float scaleX, out float scaleY) { uint xDpi, yDpi; if (Win32Platform.IsWindows81OrGreater) { ShCore.GetDpiForMonitor(monitor, MonitorDpiType.MDT_EFFECTIVE_DPI, out xDpi, out yDpi); } else { IntPtr dc = User32.GetDC(IntPtr.Zero); xDpi = (uint)Gdi32.GetDeviceCaps(dc, DeviceCap.LogPixelsX); yDpi = (uint)Gdi32.GetDeviceCaps(dc, DeviceCap.LogPixelsY); User32.ReleaseDC(IntPtr.Zero, dc); } scaleX = xDpi / (float)Win32Platform.DEFAULT_DPI; scaleY = yDpi / (float)Win32Platform.DEFAULT_DPI; }
/// <inheritdoc /> protected override bool UpdatePlatform() { // Update input. // NOTE: Shift keys on Windows tend to "stick" when both are pressed as // no key up message is generated by the first key release // The other half of this is in the handling of WM_KEYUP // HACK: Query actual key state and synthesize release events as needed KeyState lShift = User32.GetAsyncKeyState(VirtualKey.LSHIFT); KeyState rShift = User32.GetAsyncKeyState(VirtualKey.RSHIFT); // Check if it was down, but no longer is. if (_keys[(short)Key.LeftShift] && lShift.Value != 0) { UpdateKeyStatus(Key.LeftShift, false); } if (_keys[(short)Key.RightShift] && rShift.Value != 0) { UpdateKeyStatus(Key.RightShift, false); } while (User32.PeekMessage(out Message msg, IntPtr.Zero, 0, 0, PeekMessageFlags.PM_REMOVE)) { // Things like the task manager will generate this message. if (msg.Value == WM.QUIT) { IsOpen = false; return(false); } User32.TranslateMessage(ref msg); User32.DispatchMessage(ref msg); } // Check if focused. if (!IsFocused && !Engine.Configuration.DebugMode) { User32.WaitMessage(); } return(true); }
private void CreateHelperWindow() { HelperWindowHandle = User32.CreateWindowEx( WindowExStyles.WS_EX_OVERLAPPEDWINDOW, CLASS_NAME, "Emotion Helper Window", WindowStyles.WS_CLIPSIBLINGS | WindowStyles.WS_CLIPCHILDREN, 0, 0, 100, 100, IntPtr.Zero, IntPtr.Zero, Kernel32.GetModuleHandle(null), IntPtr.Zero ); if (HelperWindowHandle == IntPtr.Zero) { CheckError("Win32: Failed to create helper window.", true); } // HACK: The command to the first ShowWindow call is ignored if the parent // process passed along a STARTUPINFO, so clear that with a no-op call User32.ShowWindow(HelperWindowHandle, ShowWindowCommands.SW_HIDE); // Register for HID device notifications var dbi = new DevBroadcastDeviceInterfaceW(); dbi.DbccSize = (uint)Marshal.SizeOf(dbi); dbi.DbccDeviceType = DeviceType.DeviceInterface; dbi.DbccClassGuid = User32Guids.GuidDevInterfaceHid; _deviceNotificationHandle = User32.RegisterDeviceNotificationW(HelperWindowHandle, ref dbi, DeviceNotificationFlags.WindowHandle); CheckError("Registering for device notifications."); while (User32.PeekMessage(out Message msg, HelperWindowHandle, 0, 0, PeekMessageFlags.PM_REMOVE)) { User32.TranslateMessage(ref msg); User32.DispatchMessage(ref msg); } Kernel32.SetLastError(0); CheckError("Creating helper window."); }
private bool MonitorCallback(IntPtr hMonitor, IntPtr hdc, ref Rect rect, object dwData) { var monitorInfo = new MonitorInfoEx(); monitorInfo.Size = (uint)Marshal.SizeOf(monitorInfo); // Try to get monitor info. if (!User32.GetMonitorInfo(hMonitor, ref monitorInfo)) { return(false); } // Check if the adapter name matches, in which case this is the handle to this monitor. if (monitorInfo.SzDevice == AdapterName) { Handle = hMonitor; } Position = new Vector2(monitorInfo.MonitorRect.Left, monitorInfo.MonitorRect.Top); return(true); }
/// <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)); }
/// <summary> /// Polls and updates the statuses of connected monitors. /// </summary> private void PollMonitors() { // All monitors are considered inactive until proven otherwise. var inactive = new List <Monitor>(); inactive.AddRange(Monitors); for (uint adapterIndex = 0;; adapterIndex++) { var first = false; var adapter = new DisplayDeviceW(); adapter.Cb = (uint)Marshal.SizeOf(adapter); if (!User32.EnumDisplayDevicesW(null, adapterIndex, ref adapter, 0)) { break; } // If the adapter is inactive - continue; if ((adapter.StateFlags & DisplayDeviceStateFlags.Active) == 0) { continue; } if ((adapter.StateFlags & DisplayDeviceStateFlags.PrimaryDevice) != 0) { first = true; } uint displayIndex; for (displayIndex = 0;; displayIndex++) { var display = new DisplayDeviceW(); display.Cb = (uint)Marshal.SizeOf(display); if (!User32.EnumDisplayDevicesW(adapter.DeviceName, displayIndex, ref display, 0)) { break; } // If not active - continue; if ((display.StateFlags & DisplayDeviceStateFlags.Active) == 0) { continue; } // If active - remove from inactive list. // If something was removed, that means this display exists in the monitor array. if (inactive.RemoveAll(x => x.Name == display.DeviceName) > 0) { continue; } UpdateMonitor(new Win32Monitor(adapter, display), true, first); } // HACK: If an active adapter does not have any display devices (as sometimes happens), add it directly as a monitor. if (displayIndex != 0) { continue; } // Remove self from inactive list. if (inactive.RemoveAll(x => x.Name == adapter.DeviceName) > 0) { continue; } UpdateMonitor(new Win32Monitor(adapter, null), true, first); } foreach (Monitor inactiveMonitor in inactive) { UpdateMonitor(inactiveMonitor, false, false); } }
public void FocusWindow() { User32.BringWindowToTop(_windowHandle); User32.SetForegroundWindow(_windowHandle); User32.SetFocus(_windowHandle); }
protected override Vector2 GetSize() { User32.GetClientRect(_windowHandle, out Rect rect); return(new Vector2(rect.Right, rect.Bottom)); }
/// <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; }
/// <inheritdoc /> protected override void SetupInternal(Configurator config) { IsWindows7OrGreater = IsWindowsVersionOrGreaterWin32( NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWin7), NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWin7), 0); IsWindows8OrGreater = IsWindowsVersionOrGreaterWin32( NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWin8), NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWin8), 0); IsWindows81OrGreater = IsWindowsVersionOrGreaterWin32( NativeHelpers.HiByte((ushort)NtDll.WinVer.Win32WinNTWinBlue), NativeHelpers.LoByte((ushort)NtDll.WinVer.Win32WinNTWinBlue), 0); IsWindows10AnniversaryUpdateOrGreaterWin32 = IsWindows10BuildOrGreaterWin32(14393); IsWindows10CreatorsUpdateOrGreaterWin32 = IsWindows10BuildOrGreaterWin32(15063); var windowsVersionFlags = new List <string>(); if (IsWindows7OrGreater) { windowsVersionFlags.Add(nameof(IsWindows7OrGreater)); } if (IsWindows8OrGreater) { windowsVersionFlags.Add(nameof(IsWindows8OrGreater)); } if (IsWindows81OrGreater) { windowsVersionFlags.Add(nameof(IsWindows81OrGreater)); } if (IsWindows10AnniversaryUpdateOrGreaterWin32) { windowsVersionFlags.Add(nameof(IsWindows10AnniversaryUpdateOrGreaterWin32)); } if (IsWindows10CreatorsUpdateOrGreaterWin32) { windowsVersionFlags.Add(nameof(IsWindows10CreatorsUpdateOrGreaterWin32)); } Engine.Log.Trace(string.Join(", ", windowsVersionFlags), MessageSource.Win32); if (IsWindows10CreatorsUpdateOrGreaterWin32) { User32.SetProcessDpiAwarenessContext(DpiAwarenessContext.DpiAwarenessContextPerMonitorAwareV2); } else if (IsWindows81OrGreater) { User32.SetProcessDpiAwareness(ProcessDpiAwareness.ProcessPerMonitorDpiAware); } else { User32.SetProcessDPIAware(); } // todo: load libraries - if any, probably XInput? // Initialize audio. AudioContext ctx = null; #if OpenAL ctx ??= OpenALAudioAdapter.TryCreate(this); #endif ctx ??= WasApiAudioContext.TryCreate(this); ctx ??= new NullAudioContext(); Audio = ctx; Engine.Log.Trace("Audio init complete.", MessageSource.Win32); PopulateKeyCodes(); RegisterWindowClass(); CreateHelperWindow(); PollMonitors(); Engine.Log.Trace("Platform helpers created.", MessageSource.Win32); var windowInitialSize = new Rect { Right = (int)config.HostSize.X, Bottom = (int)config.HostSize.Y }; GetFullWindowRect(DEFAULT_WINDOW_STYLE, DEFAULT_WINDOW_STYLE_EX, DEFAULT_DPI, ref windowInitialSize); int initialWidth = windowInitialSize.Right - windowInitialSize.Left; int initialHeight = windowInitialSize.Bottom - windowInitialSize.Top; WindowStyles windowStyle = DEFAULT_WINDOW_STYLE; if (config.HiddenWindow) { windowStyle &= ~WindowStyles.WS_VISIBLE; windowStyle &= ~WindowStyles.WS_MINIMIZE; // This will override the hide otherwise config.InitialDisplayMode = DisplayMode.Initial; } IntPtr windowHandle = User32.CreateWindowEx( DEFAULT_WINDOW_STYLE_EX, CLASS_NAME, config.HostTitle, windowStyle, (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 (windowHandle == IntPtr.Zero) { CheckError("Couldn't create window.", true); return; } Engine.Log.Trace("Window created.", MessageSource.Win32); string[] args = Environment.GetCommandLineArgs(); bool forceAngle = CommandLineParser.FindArgument(args, "angle", out string _); bool forceMesa = CommandLineParser.FindArgument(args, "software", out string _); // Create graphics context. if (!forceAngle && !forceMesa) { try { var wgl = new WglGraphicsContext(); wgl.Init(User32.GetDC(_helperWindowHandle), windowHandle, this); if (wgl.Valid) { Context = wgl; } } catch (Exception ex) { Engine.Log.Warning($"Couldn't create WGL context, falling back if possible.\n{ex}", MessageSource.Win32); } } #if ANGLE if (Context == null && !forceMesa) { try { var egl = new EglGraphicsContext(); egl.Init(User32.GetDC(windowHandle), windowHandle, this); if (egl.Valid) { Context = egl; } } catch (Exception ex) { Engine.Log.Warning($"Couldn't create EGL context, falling back if possible.\n{ex}", MessageSource.Win32); } } #endif if (Context == null) { try { var gallium = new GalliumGraphicsContext(); gallium.Init(windowHandle, this); if (gallium.Valid) { Context = gallium; } } catch (Exception ex) { Engine.CriticalError(new Exception("Couldn't create MESA context.", ex)); } } if (Context == null) { Engine.CriticalError(new Exception("Couldn't create graphics context!")); return; } // 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(windowHandle); GetFullWindowRect(ref rect); User32.SetWindowPos(_windowHandle, IntPtr.Zero, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, WindowPositionFlags.SWP_NOACTIVATE | WindowPositionFlags.SWP_NOZORDER); _windowHandle = windowHandle; }
private WindowProc _wndProcDelegate; // This needs to be assigned so the garbage collector doesn't collect it. /// <summary> /// Handler for messages. /// </summary> private unsafe IntPtr WndProc(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam) { if (_windowHandle == IntPtr.Zero) { // 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 = 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)_config.RenderSize.X + offX; mmi->MinTrackSize.Y = (int)_config.RenderSize.Y + offY; return(IntPtr.Zero); case WM.SIZE: int width = NativeHelpers.LoWord((uint)lParam); int height = NativeHelpers.HiWord((uint)lParam); Resized(new Vector2(width, height)); return(IntPtr.Zero); case WM.SETFOCUS: FocusChanged(true); return(IntPtr.Zero); case WM.KILLFOCUS: FocusChanged(false); return(IntPtr.Zero); case WM.SYSCOMMAND: switch ((int)wParam & 0xfff0) { case (int)SysCommand.SC_SCREENSAVE: case (int)SysCommand.SC_MONITORPOWER: if (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.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); } UpdateTextInput((char)wParam); break; case WM.KEYDOWN: case WM.SYSKEYDOWN: case WM.KEYUP: case WM.SYSKEYUP: var virtualKey = (VirtualKey)wParam; var lParamLong = (ulong)lParam; bool up = ((lParamLong >> 31) & 1) != 0; Key key = TranslateKey(virtualKey, lParamLong); if (key == Key.Unknown) { break; } // Don't handle shift keys. Check UpdatePlatform() for more info. if (virtualKey == VirtualKey.SHIFT) { break; } UpdateKeyStatus(key, !up); break; case WM.MOUSEMOVE: int x = NativeHelpers.LoWordS((uint)lParam); int y = NativeHelpers.HiWordS((uint)lParam); MousePosition = new Vector2(x, y); 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: ushort keySpecifier = NativeHelpers.HiWord((ulong)wParam); var mouseKey = Key.Unknown; var buttonDown = false; mouseKey = msg switch { WM.LBUTTONDOWN => Key.MouseKeyLeft, WM.LBUTTONUP => Key.MouseKeyLeft, WM.RBUTTONDOWN => Key.MouseKeyRight, WM.RBUTTONUP => Key.MouseKeyRight, WM.MBUTTONDOWN => Key.MouseKeyMiddle, WM.MBUTTONUP => Key.MouseKeyMiddle, WM.XBUTTONDOWN when keySpecifier == 1 => Key.MouseKey4, WM.XBUTTONUP when keySpecifier == 1 => Key.MouseKey4, WM.XBUTTONDOWN when keySpecifier == 2 => Key.MouseKey5, WM.XBUTTONUP when keySpecifier == 2 => Key.MouseKey5, _ => mouseKey }; if (msg == WM.LBUTTONDOWN || msg == WM.RBUTTONDOWN || msg == WM.MBUTTONDOWN || msg == WM.XBUTTONDOWN) { buttonDown = true; } var nonePressed = true; for (int i = (int)Key.MouseKeyStart + 1; i < (int)Key.MouseKeyEnd; i++) { if (!_keys[i]) { continue; } nonePressed = false; break; } if (nonePressed) { User32.SetCapture(_windowHandle); } UpdateKeyStatus(mouseKey, buttonDown); nonePressed = true; for (int i = (int)Key.MouseKeyStart + 1; i < (int)Key.MouseKeyEnd; i++) { if (!_keys[i]) { continue; } nonePressed = false; break; } if (nonePressed) { User32.ReleaseCapture(); } 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)); }
public void GetWindowContentScale(out float scaleX, out float scaleY) { IntPtr monitor = User32.MonitorFromWindow(_windowHandle, MonitorFlag.MONITOR_DEFAULTTONEAREST); Win32Monitor.GetMonitorContentScale(monitor, out scaleX, out scaleY); }