-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTransparentWindow.cs
More file actions
253 lines (219 loc) · 7.79 KB
/
TransparentWindow.cs
File metadata and controls
253 lines (219 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
///
/// Copyright (c) 2019 wakagomo
///
/// This source code is released under the MIT License.
/// http://opensource.org/licenses/mit-license.php
///
using UnityEngine;
using System;
using System.Runtime.InteropServices;
using System.Collections;
/// <summary>
/// Make the window transparent.
/// </summary>
public class TransparentWindow : MonoBehaviour
{
#if !UNITY_EDITOR && UNITY_STANDALONE_WIN
#region WINDOWS API
/// <summary>
/// Returned by the GetThemeMargins function to define the margins of windows that have visual styles applied.
/// </summary>
/// https://docs.microsoft.com/en-us/windows/desktop/api/uxtheme/ns-uxtheme-_margins
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
/// <summary>
/// Retrieves the window handle to the active window attached to the calling thread's message queue.
/// </summary>
/// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getactivewindow
[DllImport("User32.dll")]
private static extern IntPtr GetActiveWindow();
/// <summary>
/// Changes an attribute of the specified window. The function also sets the 32-bit (long) value at the specified offset into the extra window memory.
/// </summary>
/// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setwindowlonga
[DllImport("User32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
/// <summary>
/// Changes the size, position, and Z order of a child, pop-up, or top-level window. These windows are ordered according to their appearance on the screen. The topmost window receives the highest rank and is the first window in the Z order.
/// </summary>
/// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setwindowpos
[DllImport("User32.dll")]
private static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
/// <summary>
/// Extends the window frame into the client area.
/// </summary>
/// https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmextendframeintoclientarea
[DllImport("Dwmapi.dll")]
private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);
#endregion
/// <summary>
/// Should operation be transparene?
/// </summary>
private bool isClickThrough = false;
/// <summary>
/// Is the mouse pointer on an opaque pixel?
/// </summary>
private bool isOnOpaquePixel = true;
/// <summary>
/// The cut off threshold of alpha value.
/// </summary>
private float opaqueThreshold = 0.1f;
/// <summary>
/// An instance of current camera.
/// </summary>
private Camera currentCamera;
/// <summary>
/// 1x1 texture
/// </summary>
private Texture2D colorPickerTexture = null;
/// <summary>
/// Window handle
/// </summary>
private IntPtr windowHandle;
private void Awake()
{
windowHandle = GetActiveWindow();
{ // SetWindowLong
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
SetWindowLong(windowHandle, GWL_STYLE, WS_POPUP);
}
{ // Set extended window style
SetClickThrough(true);
}
{ // SetWindowPos
IntPtr HWND_TOPMOST = new IntPtr(-1);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOMOVE = 0x0002;
const uint SWP_NOACTIVE = 0x0010;
const uint SWP_SHOWWINDOW = 0x0040;
SetWindowPos(windowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVE | SWP_SHOWWINDOW);
}
{ // DwmExtendFrameIntoClientArea
MARGINS margins = new MARGINS()
{
cxLeftWidth = -1
};
DwmExtendFrameIntoClientArea(windowHandle, ref margins);
}
}
/// <summary>
/// Set my window is click-through or not
/// </summary>
/// <param name="through"></param>
private void SetClickThrough(bool through)
{
const int GWL_EXSTYLE = -20;
const uint WS_EX_LAYERD = 0x080000;
const uint WS_EX_TRANSPARENT = 0x00000020;
const uint WS_EX_LEFT = 0x00000000;
if (through)
{
SetWindowLong(windowHandle, GWL_EXSTYLE, WS_EX_LAYERD | WS_EX_TRANSPARENT);
}
else
{
SetWindowLong(windowHandle, GWL_EXSTYLE, WS_EX_LEFT);
}
}
void Start()
{
if (!currentCamera)
{
// カメラ指定がなければメインカメラを探す
currentCamera = Camera.main;
// もしメインカメラが見つからなければ、Findで探す
if (!currentCamera)
{
currentCamera = FindObjectOfType<Camera>();
}
}
// マウス下描画色抽出用テクスチャを準備
colorPickerTexture = new Texture2D(1, 1, TextureFormat.ARGB32, false);
// マウスカーソル直下の色を検査するコルーチンを開始
StartCoroutine(PickColorCoroutine());
Application.runInBackground = true;
}
void Update()
{
// 操作透過/不透過を更新
UpdateClickThrough();
}
/// <summary>
/// 画素の色を基に操作透過状態を切り替える
/// </summary>
void UpdateClickThrough()
{
if (isClickThrough)
{
// 現在が操作透過状態で、かつ不透明画素上にマウスが来たら、操作透過をやめる
if (isOnOpaquePixel)
{
SetClickThrough(false);
isClickThrough = false;
}
}
else
{
// 現在が操作受付中で、かつ透明画素上にマウスが来たら、操作透過に切り替える
if (!isOnOpaquePixel)
{
SetClickThrough(true);
isClickThrough = true;
}
}
}
/// <summary>
/// WaitForEndOfFrame()を用いたコルーチンで、描画完了後の画像を監視
/// </summary>
/// <returns></returns>
private IEnumerator PickColorCoroutine()
{
while (Application.isPlaying)
{
yield return new WaitForEndOfFrame();
ObservePixelUnderCursor(currentCamera);
}
yield return null;
}
/// <summary>
/// マウス直下の画素が透明かどうかを判定
/// </summary>
/// <param name="cam"></param>
void ObservePixelUnderCursor(Camera cam)
{
// カメラが不明ならば何もしない
if (!cam) return;
Vector2 mousePos = Input.mousePosition;
Rect camRect = cam.pixelRect;
// マウスが描画範囲内ならチェックする
if (camRect.Contains(mousePos))
{
try
{
// マウス直下の画素のみReadPixelする
// 参考 http://tsubakit1.hateblo.jp/entry/20131203/1386000440
colorPickerTexture.ReadPixels(new Rect(mousePos, Vector2.one), 0, 0);
Color color = colorPickerTexture.GetPixel(0, 0);
// アルファ値がしきい値以上ならば、不透過とする
isOnOpaquePixel = (color.a >= opaqueThreshold);
}
catch (System.Exception ex)
{
// 稀に範囲外になってしまう?
Debug.LogError(ex.Message);
isOnOpaquePixel = false;
}
}
else
{
isOnOpaquePixel = false;
}
}
#endif // !UNITY_EDITOR && UNITY_STANDALONE_WIN
}