feat: setup window using the api
This commit is contained in:
parent
61c18429a2
commit
19cd8e5d03
27 changed files with 1364 additions and 10 deletions
201
Packages/AppWindowUtility/Runtime/Windows/WinApi.cs
Normal file
201
Packages/AppWindowUtility/Runtime/Windows/WinApi.cs
Normal file
|
@ -0,0 +1,201 @@
|
|||
#if UNITY_STANDALONE_WIN
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
|
||||
namespace SatorImaging.AppWindowUtility
|
||||
{
|
||||
public static class WinApi
|
||||
{
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct DwmMargin
|
||||
{
|
||||
public int cxLeftWidth;
|
||||
public int cxRightWidth;
|
||||
public int cyTopHeight;
|
||||
public int cyBottomHeight;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct POINT
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int left;
|
||||
public int top;
|
||||
public int right;
|
||||
public int bottom;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetCursorPos(out POINT lpPoint);
|
||||
|
||||
public static Vector2 GetWindowsMousePosition()
|
||||
{
|
||||
POINT pos;
|
||||
if (GetCursorPos(out pos)) return new Vector2(pos.X, pos.Y);
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetActiveWindow();
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetDesktopWindow();
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint SendMessageTimeout(IntPtr hWnd, uint Msg, uint wParam, uint lParam, uint fuFlags, uint uTimeout, out uint lpdwResult);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChild, string lpszClass, string lpszWindow);
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr FindWindow(string className, string windowName);
|
||||
|
||||
public static IntPtr CurrentWindowHandle = IntPtr.Zero;
|
||||
public static IntPtr GetUnityWindowHandle()
|
||||
{
|
||||
if (CurrentWindowHandle == IntPtr.Zero)
|
||||
{
|
||||
CurrentWindowHandle = FindWindow(null, Application.productName);
|
||||
}
|
||||
return CurrentWindowHandle;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern IntPtr GetForegroundWindow();
|
||||
|
||||
public static bool IsWindowActive()
|
||||
{
|
||||
return GetUnityWindowHandle() == GetForegroundWindow();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong); /*x uint o int unchecked*/
|
||||
[DllImport("user32.dll")]
|
||||
public static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
public static extern bool SetWindowText(IntPtr hwnd, String lpString);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
|
||||
[DllImport("user32.dll")]
|
||||
public static extern bool GetClientRect(IntPtr hWnd, out RECT rect);
|
||||
|
||||
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||||
public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
||||
public static readonly IntPtr HWND_TOP = new IntPtr(0);
|
||||
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
||||
|
||||
|
||||
|
||||
[Flags()]
|
||||
public enum SetWindowPosFlags : uint
|
||||
{
|
||||
AsynchronousWindowPosition = 0x4000,
|
||||
DeferErase = 0x2000,
|
||||
DrawFrame = 0x0020,
|
||||
FrameChanged = 0x0020,
|
||||
HideWindow = 0x0080,
|
||||
DoNotActivate = 0x0010,
|
||||
DoNotCopyBits = 0x0100,
|
||||
IgnoreMove = 0x0002,
|
||||
DoNotChangeOwnerZOrder = 0x0200,
|
||||
DoNotRedraw = 0x0008,
|
||||
DoNotReposition = 0x0200,
|
||||
DoNotSendChangingEvent = 0x0400,
|
||||
IgnoreResize = 0x0001,
|
||||
IgnoreZOrder = 0x0004,
|
||||
ShowWindow = 0x0040,
|
||||
NoFlag = 0x0000,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[DllImport("Dwmapi.dll")]
|
||||
public static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref DwmMargin margins);
|
||||
public static void SetDwmTransparent(bool enable)
|
||||
{
|
||||
var margins = new DwmMargin()
|
||||
{
|
||||
cxLeftWidth = enable ? -1 : 0,
|
||||
};
|
||||
DwmExtendFrameIntoClientArea(GetUnityWindowHandle(), ref margins);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public const int GWL_STYLE = -16;
|
||||
public const uint WS_POPUP = 0x80000000;
|
||||
public const uint WS_BORDER = 0x00800000;
|
||||
public const uint WS_VISIBLE = 0x10000000;
|
||||
public const uint WS_CAPTION = 0x00C00000;
|
||||
public const uint WS_THICKFRAME = 0x00040000;
|
||||
public const int GWL_EXSTYLE = -20;
|
||||
public const uint WS_EX_LAYERED = 0x00080000;
|
||||
public const uint WS_EX_TRANSPARENT = 0x00000020;
|
||||
public const uint WS_EX_DLGMODALFRAME = 0x00000001;
|
||||
public const uint WS_EX_WINDOWEDGE = 0x00000100;
|
||||
public const uint WS_EX_CLIENTEDGE = 0x00000200;
|
||||
public const uint WS_EX_STATICEDGE = 0x00020000;
|
||||
|
||||
|
||||
public const int LWA_COLORKEY = 0x00000001;
|
||||
public const int LWA_ALPHA = 0x00000002;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern int GetWindowTextLength(IntPtr hWnd);
|
||||
|
||||
|
||||
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern bool GetLayeredWindowAttributes(IntPtr hwnd, out uint crKey, out byte bAlpha, out uint dwFlags);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//class
|
||||
}//namespace
|
||||
#endif
|
11
Packages/AppWindowUtility/Runtime/Windows/WinApi.cs.meta
Normal file
11
Packages/AppWindowUtility/Runtime/Windows/WinApi.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d8438e9a2634454429f47962402d6be3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
298
Packages/AppWindowUtility/Runtime/Windows/Windows.cs
Normal file
298
Packages/AppWindowUtility/Runtime/Windows/Windows.cs
Normal file
|
@ -0,0 +1,298 @@
|
|||
#if UNITY_STANDALONE_WIN
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
|
||||
namespace SatorImaging.AppWindowUtility
|
||||
{
|
||||
public class Windows : IPlatformDependent
|
||||
{
|
||||
static IntPtr hWnd;
|
||||
|
||||
static uint defaultWindowStyle;
|
||||
static uint defaultExWindowStyle;
|
||||
|
||||
// unity's shader error color.
|
||||
static uint defaultKeyingColor = 0xFF00FF;
|
||||
static int borderWidth;
|
||||
static int titleBarHeight;
|
||||
|
||||
|
||||
private static bool isAlwaysOnTop = false;
|
||||
private static bool isTransparent = false;
|
||||
private static bool isFrameVisible = true;
|
||||
private static bool isClickThrough = false;
|
||||
private static bool isWallpaper = false;
|
||||
|
||||
private static bool isInitialized = false;
|
||||
private static WinApi.RECT lastClientRect = new WinApi.RECT { left = 32, top = 64, right = 640, bottom = 480 };
|
||||
|
||||
|
||||
|
||||
// features supported
|
||||
public bool AlwaysOnTopSupported { get; } = true;
|
||||
public bool TransparentSupported { get; } = true;
|
||||
public bool FrameVisibilitySupported { get; } = true;
|
||||
public bool ClickThroughSupported { get; } = true;
|
||||
public bool AsWallpaperSupported { get; } = true;
|
||||
public bool KeyingColorSupported { get; } = true;
|
||||
public bool WindowOpacitySupported { get; } = true;
|
||||
public bool WindowPlacementSupported { get; } = true;
|
||||
public bool WindowResizeSupported { get; } = true;
|
||||
|
||||
|
||||
|
||||
public Windows()
|
||||
{
|
||||
if (isInitialized) return;
|
||||
|
||||
hWnd = WinApi.GetUnityWindowHandle();
|
||||
defaultWindowStyle = WinApi.GetWindowLong(WinApi.GetUnityWindowHandle(), WinApi.GWL_STYLE);
|
||||
defaultExWindowStyle = WinApi.GetWindowLong(WinApi.GetUnityWindowHandle(), WinApi.GWL_EXSTYLE);
|
||||
|
||||
// initialize client rect.
|
||||
WinApi.GetClientRect(hWnd, out lastClientRect);
|
||||
|
||||
// calculate title bar and frame size
|
||||
WinApi.RECT windowRect;
|
||||
WinApi.GetWindowRect(hWnd, out windowRect);
|
||||
WinApi.RECT clientRect;
|
||||
WinApi.GetClientRect(hWnd, out clientRect);
|
||||
borderWidth = (windowRect.right - windowRect.left) - clientRect.right;
|
||||
borderWidth = (int)(borderWidth * 0.5f);
|
||||
titleBarHeight = (windowRect.bottom - windowRect.top) - clientRect.bottom - borderWidth;
|
||||
|
||||
|
||||
//Debug.Log($"{typeof(AppWindowUtility).FullName}.Initialize: Title Bar {titleBarHeight}px / Border {borderWidth}px");
|
||||
|
||||
isInitialized = true;
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
public void ResetStyle()
|
||||
{
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, defaultWindowStyle);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, defaultExWindowStyle);
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool GetAlwaysOnTop() => isAlwaysOnTop;
|
||||
public void SetAlwaysOnTop(bool enable)
|
||||
{
|
||||
WinApi.SetWindowPos(
|
||||
hWnd,
|
||||
enable ? WinApi.HWND_TOPMOST : WinApi.HWND_NOTOPMOST,
|
||||
0, 0, 0, 0,
|
||||
WinApi.SetWindowPosFlags.IgnoreMove | WinApi.SetWindowPosFlags.IgnoreResize
|
||||
);
|
||||
|
||||
isAlwaysOnTop = enable;
|
||||
}//
|
||||
|
||||
|
||||
public bool GetTransparent() => isTransparent;
|
||||
public void SetTransparent(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
SetFrameVisibility(false);
|
||||
|
||||
var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle & ~WinApi.WS_EX_LAYERED);
|
||||
WinApi.SetDwmTransparent(true);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
SetFrameVisibility(true);
|
||||
WinApi.SetDwmTransparent(false);
|
||||
}
|
||||
|
||||
isTransparent = enable;
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
public void SetKeyingColor(byte red, byte green, byte blue)
|
||||
{
|
||||
var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED);// | WindowsApi.WS_EX_TRANSPARENT);
|
||||
|
||||
var color = (uint)(blue + (green << 8) + (red << 16));
|
||||
WinApi.SetLayeredWindowAttributes(hWnd, color, 0xFF, WinApi.LWA_COLORKEY);
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
public bool GetFrameVisibility() => isFrameVisible;
|
||||
public void SetFrameVisibility(bool visible)
|
||||
{
|
||||
if (AppWindowUtility.FullScreen) return;
|
||||
if (visible == isFrameVisible) return;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
// must be done BEFORE SetWindowLong
|
||||
MoveWindowRelative(-borderWidth, -titleBarHeight);
|
||||
//ResizeWindowRelative(borderWidth * 2, titleBarHeight + borderWidth);
|
||||
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, defaultWindowStyle);
|
||||
|
||||
// to make uGUI correct.
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
lastClientRect.left - borderWidth,
|
||||
lastClientRect.top - titleBarHeight,
|
||||
lastClientRect.right - lastClientRect.left + borderWidth * 2,
|
||||
lastClientRect.bottom - lastClientRect.top + titleBarHeight + borderWidth,
|
||||
WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
|
||||
);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// store last client size for showing frame again
|
||||
if (!AppWindowUtility.FullScreen)
|
||||
{
|
||||
WinApi.GetClientRect(hWnd, out lastClientRect);
|
||||
//Debug.Log($"SetFrameVisibility: Client Rect stored.");
|
||||
}
|
||||
|
||||
var currStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_STYLE);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_STYLE, currStyle & ~WinApi.WS_BORDER & ~WinApi.WS_THICKFRAME & ~WinApi.WS_CAPTION);
|
||||
|
||||
//// must be done AFTER SetWindowLong
|
||||
MoveWindowRelative(borderWidth, titleBarHeight);
|
||||
// must be change window size 2 times to work correctly, idk why.
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
lastClientRect.left - borderWidth,
|
||||
lastClientRect.top - titleBarHeight,
|
||||
lastClientRect.right,
|
||||
lastClientRect.bottom + 1,
|
||||
WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
|
||||
);
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
lastClientRect.left - borderWidth,
|
||||
lastClientRect.top - titleBarHeight,
|
||||
lastClientRect.right,
|
||||
lastClientRect.bottom - 1,
|
||||
WinApi.SetWindowPosFlags.FrameChanged | WinApi.SetWindowPosFlags.IgnoreMove
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
isFrameVisible = visible;
|
||||
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool GetClickThrough() => isClickThrough;
|
||||
public void SetClickThrough(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
{
|
||||
var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED | WinApi.WS_EX_TRANSPARENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, defaultExWindowStyle);
|
||||
}
|
||||
|
||||
isClickThrough = enable;
|
||||
}//
|
||||
|
||||
|
||||
|
||||
public void SetWindowOpacity(byte opacity)
|
||||
{
|
||||
////////if(0xFF == opacity)
|
||||
{
|
||||
var currExStyle = WinApi.GetWindowLong(hWnd, WinApi.GWL_EXSTYLE);
|
||||
WinApi.SetWindowLong(hWnd, WinApi.GWL_EXSTYLE, currExStyle | WinApi.WS_EX_LAYERED);
|
||||
WinApi.SetLayeredWindowAttributes(hWnd, defaultKeyingColor, opacity, WinApi.LWA_ALPHA);
|
||||
}
|
||||
|
||||
/* do not reset for combination with Transparent
|
||||
else
|
||||
{
|
||||
ResetStyle();
|
||||
}
|
||||
*/
|
||||
}//
|
||||
|
||||
|
||||
|
||||
public bool GetAsWallpaper() => isWallpaper;
|
||||
public void SetAsWallpaper(bool enable)
|
||||
{
|
||||
}//
|
||||
|
||||
|
||||
|
||||
public void MoveWindowRelative(int relativeX, int relativeY)
|
||||
{
|
||||
if (AppWindowUtility.FullScreen) return;
|
||||
|
||||
WinApi.RECT rect;
|
||||
WinApi.GetWindowRect(hWnd, out rect);
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
rect.left + relativeX,
|
||||
rect.top + relativeY,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreResize
|
||||
);
|
||||
}//
|
||||
|
||||
public void MoveWindow(int x, int y)
|
||||
{
|
||||
if (AppWindowUtility.FullScreen) return;
|
||||
|
||||
WinApi.RECT rect;
|
||||
WinApi.GetWindowRect(hWnd, out rect);
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
x,
|
||||
y,
|
||||
rect.right - rect.left,
|
||||
rect.bottom - rect.top,
|
||||
WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreResize
|
||||
);
|
||||
}//
|
||||
|
||||
|
||||
|
||||
public void ResizeWindowRelative(int relativeWidth, int relativeHeight)
|
||||
{
|
||||
if (AppWindowUtility.FullScreen) return;
|
||||
|
||||
WinApi.RECT rect;
|
||||
WinApi.GetWindowRect(hWnd, out rect);
|
||||
WinApi.SetWindowPos(hWnd, IntPtr.Zero,
|
||||
rect.left,
|
||||
rect.top,
|
||||
relativeWidth,
|
||||
relativeHeight,
|
||||
WinApi.SetWindowPosFlags.NoFlag //ApiWindows.SetWindowPosFlags.IgnoreMove
|
||||
);
|
||||
|
||||
}//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//class
|
||||
}//namespace
|
||||
#endif
|
11
Packages/AppWindowUtility/Runtime/Windows/Windows.cs.meta
Normal file
11
Packages/AppWindowUtility/Runtime/Windows/Windows.cs.meta
Normal file
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 00a74ea748d2df149864a0f9d4455e24
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue