A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using System.Drawing;
6using System.Runtime.InteropServices;
7using osu.Framework.Platform.Windows.Native;
8using SDL2;
9
10namespace osu.Framework.Platform.Windows
11{
12 public class WindowsWindow : SDL2DesktopWindow
13 {
14 private const int seticon_message = 0x0080;
15 private const int icon_big = 1;
16 private const int icon_small = 0;
17
18 private const int large_icon_size = 256;
19 private const int small_icon_size = 16;
20
21 private Icon smallIcon;
22 private Icon largeIcon;
23
24 public WindowsWindow()
25 {
26 try
27 {
28 // SDL doesn't handle DPI correctly on windows, but this brings things mostly in-line with expectations. (https://bugzilla.libsdl.org/show_bug.cgi?id=3281)
29 SetProcessDpiAwareness(ProcessDpiAwareness.Process_System_DPI_Aware);
30 }
31 catch
32 {
33 // API doesn't exist on Windows 7 so it needs to be allowed to fail silently.
34 }
35 }
36
37 protected override Size SetBorderless()
38 {
39 SDL.SDL_SetWindowBordered(SDLWindowHandle, SDL.SDL_bool.SDL_FALSE);
40
41 Size positionOffsetHack = new Size(1, 1);
42
43 var newSize = CurrentDisplay.Bounds.Size + positionOffsetHack;
44 var newPosition = CurrentDisplay.Bounds.Location - positionOffsetHack;
45
46 // for now let's use the same 1px hack that we've always used to force borderless.
47 SDL.SDL_SetWindowSize(SDLWindowHandle, newSize.Width, newSize.Height);
48 Position = newPosition;
49
50 return newSize;
51 }
52
53 /// <summary>
54 /// On Windows, SDL will use the same image for both large and small icons (scaled as necessary).
55 /// This can look bad if scaling down a large image, so we use the Windows API directly so as
56 /// to get a cleaner icon set than SDL can provide.
57 /// If called before the window has been created, or we do not find two separate icon sizes, we fall back to the base method.
58 /// </summary>
59 internal override void SetIconFromGroup(IconGroup iconGroup)
60 {
61 smallIcon = iconGroup.CreateIcon(small_icon_size, small_icon_size);
62 largeIcon = iconGroup.CreateIcon(large_icon_size, large_icon_size);
63
64 var windowHandle = WindowHandle;
65
66 if (windowHandle == IntPtr.Zero || largeIcon == null || smallIcon == null)
67 base.SetIconFromGroup(iconGroup);
68 else
69 {
70 SendMessage(windowHandle, seticon_message, (IntPtr)icon_small, smallIcon.Handle);
71 SendMessage(windowHandle, seticon_message, (IntPtr)icon_big, largeIcon.Handle);
72 }
73 }
74
75 public override Point PointToClient(Point point)
76 {
77 ScreenToClient(WindowHandle, ref point);
78 return point;
79 }
80
81 public override Point PointToScreen(Point point)
82 {
83 ClientToScreen(WindowHandle, ref point);
84 return point;
85 }
86
87 [DllImport("SHCore.dll", SetLastError = true)]
88 internal static extern bool SetProcessDpiAwareness(ProcessDpiAwareness awareness);
89
90 internal enum ProcessDpiAwareness
91 {
92 Process_DPI_Unaware = 0,
93 Process_System_DPI_Aware = 1,
94 Process_Per_Monitor_DPI_Aware = 2
95 }
96
97 [DllImport("user32.dll", SetLastError = true)]
98 internal static extern bool ScreenToClient(IntPtr hWnd, ref Point point);
99
100 [DllImport("user32.dll", SetLastError = true)]
101 internal static extern bool ClientToScreen(IntPtr hWnd, ref Point point);
102
103 [DllImport("user32.dll", CharSet = CharSet.Auto)]
104 private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
105 }
106}