Wingnet 是一個視窗對齊小工具,透過 Claude 及 Gemini 協作完成,程式碼無需授權即可自由轉載、使用與改寫,不必另行告知。
程式的圖標透過 python 程式碼產生,並排除了部份因韌體熱鍵綁定的HOOK問題。
Last updated: 2026/7/7
#!/usr/bin/env python3
#
# Built with the collaboration of Claude and Gemini.
#
# Copyright (c) 2026
# Released under the MIT License.
#
import ctypes
import ctypes.wintypes as wt
import sys
import threading
from PIL import Image, ImageDraw
import pystray
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
dwmapi = ctypes.windll.dwmapi
class KBDLLHOOKSTRUCT(ctypes.Structure):
_fields_ = [
('vkCode', wt.DWORD),
('scanCode', wt.DWORD),
('flags', wt.DWORD),
('time', wt.DWORD),
('dwExtraInfo', ctypes.c_ulong),
]
class MONITORINFO(ctypes.Structure):
_fields_ = [
('cbSize', wt.DWORD),
('rcMonitor', wt.RECT),
('rcWork', wt.RECT),
('dwFlags', wt.DWORD),
]
class WINDOWPLACEMENT(ctypes.Structure):
_fields_ = [
('length', wt.UINT),
('flags', wt.UINT),
('showCmd', wt.UINT),
('ptMinPosition', wt.POINT),
('ptMaxPosition', wt.POINT),
('rcNormalPosition', wt.RECT),
]
WH_KEYBOARD_LL = 13
WM_KEYDOWN = 0x0100
WM_SYSKEYDOWN = 0x0104
WM_QUIT = 0x0012
HC_ACTION = 0
VK_CONTROL = 0x11
VK_LWIN = 0x5B
VK_RWIN = 0x5C
SW_RESTORE = 9
SW_MAXIMIZE = 3
SW_SHOWMAXIMIZED = 3
SWP_SHOWWINDOW = 0x0040
SWP_NOZORDER = 0x0004
MONITOR_DEFAULTTONEAREST = 0x00000002
HIGH_PRIORITY_CLASS = 0x00000080
DWMWA_EXTENDED_FRAME_BOUNDS = 9
HOTKEY_MAP: dict[int, str] = {
0x25: 'left_half',
0x27: 'right_half',
0x26: 'top_half',
0x28: 'bottom_half',
0x0D: 'maximize',
ord('C'): 'center',
ord('U'): 'top_left',
ord('I'): 'top_right',
ord('J'): 'bottom_left',
ord('K'): 'bottom_right',
ord('D'): 'left_third',
ord('F'): 'center_third',
ord('G'): 'right_third',
ord('E'): 'left_twothirds',
ord('T'): 'right_twothirds',
0x67: 'move_top_left',
0x24: 'move_top_left',
0x61: 'move_bottom_left',
0x23: 'move_bottom_left',
0x69: 'move_top_right',
0x21: 'move_top_right',
0x63: 'move_bottom_right',
0x22: 'move_bottom_right',
}
def get_work_area(hwnd: int) -> tuple[int, int, int, int]:
monitor = user32.MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST)
mi = MONITORINFO()
mi.cbSize = ctypes.sizeof(MONITORINFO)
user32.GetMonitorInfoW(monitor, ctypes.byref(mi))
r = mi.rcWork
return r.left, r.top, r.right - r.left, r.bottom - r.top
def get_shadow_insets(hwnd: int) -> tuple[int, int, int, int]:
win_rect = wt.RECT()
frame_rect = wt.RECT()
user32.GetWindowRect(hwnd, ctypes.byref(win_rect))
hr = dwmapi.DwmGetWindowAttribute(
hwnd,
DWMWA_EXTENDED_FRAME_BOUNDS,
ctypes.byref(frame_rect),
ctypes.sizeof(wt.RECT),
)
if hr != 0:
return 0, 0, 0, 0
return (
frame_rect.left - win_rect.left,
frame_rect.top - win_rect.top,
win_rect.right - frame_rect.right,
win_rect.bottom - frame_rect.bottom,
)
def place_window(hwnd: int, x: int, y: int, w: int, h: int, maximize: bool = False) -> None:
if maximize:
user32.ShowWindow(hwnd, SW_MAXIMIZE)
return
wp = WINDOWPLACEMENT()
wp.length = ctypes.sizeof(WINDOWPLACEMENT)
user32.GetWindowPlacement(hwnd, ctypes.byref(wp))
if wp.showCmd == SW_SHOWMAXIMIZED:
user32.ShowWindow(hwnd, SW_RESTORE)
sl, st, sr, sb = get_shadow_insets(hwnd)
user32.SetWindowPos(
hwnd, 0,
x - sl,
y - st,
w + sl + sr,
h + st + sb,
SWP_SHOWWINDOW | SWP_NOZORDER,
)
def execute_action(action: str) -> None:
hwnd = user32.GetForegroundWindow()
if not hwnd:
return
try:
ox, oy, ow, oh = get_work_area(hwnd)
except Exception:
return
hw = ow // 2
hh = oh // 2
t1 = ow // 3
t2 = ow * 2 // 3
layout: dict[str, tuple[int, int, int, int]] = {
'left_half': (ox, oy, hw, oh),
'right_half': (ox + hw, oy, ow - hw, oh),
'top_half': (ox, oy, ow, hh),
'bottom_half': (ox, oy + hh, ow, oh - hh),
'top_left': (ox, oy, hw, hh),
'top_right': (ox + hw, oy, ow - hw, hh),
'bottom_left': (ox, oy + hh, hw, oh - hh),
'bottom_right': (ox + hw, oy + hh, ow - hw, oh - hh),
'left_third': (ox, oy, t1, oh),
'center_third': (ox + t1, oy, t2 - t1, oh),
'right_third': (ox + t2, oy, ow - t2, oh),
'left_twothirds': (ox, oy, t2, oh),
'right_twothirds': (ox + t1, oy, ow - t1, oh),
}
try:
if action == 'maximize':
place_window(hwnd, 0, 0, 0, 0, maximize=True)
elif action in ('center', 'move_top_left', 'move_bottom_left', 'move_top_right', 'move_bottom_right'):
win_rect = wt.RECT()
user32.GetWindowRect(hwnd, ctypes.byref(win_rect))
cur_w = win_rect.right - win_rect.left
cur_h = win_rect.bottom - win_rect.top
sl, st, sr, sb = get_shadow_insets(hwnd)
vw = cur_w - sl - sr
vh = cur_h - st - sb
if action == 'center':
cx = ox + (ow - vw) // 2
cy = oy + (oh - vh) // 2
elif action == 'move_top_left':
cx = ox
cy = oy
elif action == 'move_bottom_left':
cx = ox
cy = oy + oh - vh
elif action == 'move_top_right':
cx = ox + ow - vw
cy = oy
elif action == 'move_bottom_right':
cx = ox + ow - vw
cy = oy + oh - vh
place_window(hwnd, cx, cy, vw, vh)
elif action in layout:
place_window(hwnd, *layout[action])
except Exception as exc:
print(f'[Wingnet] place_window error: {exc}', file=sys.stderr)
LRESULT = ctypes.c_longlong
_WPARAM = ctypes.c_ulonglong
_LPARAM = ctypes.c_longlong
_LowLevelKeyboardProc = ctypes.WINFUNCTYPE(
LRESULT, ctypes.c_int, _WPARAM, _LPARAM
)
user32.CallNextHookEx.restype = LRESULT
user32.CallNextHookEx.argtypes = [wt.HHOOK, ctypes.c_int, _WPARAM, _LPARAM]
_hook_handle: int = 0
_hook_proc_ref = None
_hook_thread_id: int = 0
_hook_ready = threading.Event()
def _keyboard_proc(nCode: int, wParam: int, lParam: int) -> int:
if nCode == HC_ACTION and wParam in (WM_KEYDOWN, WM_SYSKEYDOWN):
kb = ctypes.cast(lParam, ctypes.POINTER(KBDLLHOOKSTRUCT)).contents
vk = kb.vkCode
ctrl = bool(user32.GetAsyncKeyState(VK_CONTROL) & 0x8000)
win_ = (bool(user32.GetAsyncKeyState(VK_LWIN) & 0x8000) or
bool(user32.GetAsyncKeyState(VK_RWIN) & 0x8000))
if ctrl and win_ and vk in HOTKEY_MAP:
threading.Thread(
target=execute_action,
args=(HOTKEY_MAP[vk],),
daemon=True,
).start()
return 1
return user32.CallNextHookEx(_hook_handle, nCode, wParam, lParam)
def _hook_thread_func() -> None:
global _hook_handle, _hook_proc_ref, _hook_thread_id
_hook_thread_id = kernel32.GetCurrentThreadId()
_hook_proc_ref = _LowLevelKeyboardProc(_keyboard_proc)
_hook_handle = user32.SetWindowsHookExW(
WH_KEYBOARD_LL,
_hook_proc_ref,
None,
0,
)
if not _hook_handle:
err = kernel32.GetLastError()
print(f'[Wingnet] SetWindowsHookExW 失敗,錯誤碼: {err}', file=sys.stderr)
_hook_ready.set()
return
_hook_ready.set()
msg = wt.MSG()
while user32.GetMessageW(ctypes.byref(msg), None, 0, 0) > 0:
user32.TranslateMessage(ctypes.byref(msg))
user32.DispatchMessageW(ctypes.byref(msg))
user32.UnhookWindowsHookEx(_hook_handle)
def stop_hook() -> None:
if _hook_thread_id:
user32.PostThreadMessageW(_hook_thread_id, WM_QUIT, 0, 0)
def _make_icon(size: int = 64) -> Image.Image:
img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
s, m = size - 1, size // 2
c = (80, 160, 255, 255)
draw.rectangle([2, 2, s - 2, s - 2], outline=c, width=3)
draw.line([m, 2, m, s - 2], fill=c, width=2)
draw.line([2, m, s - 2, m], fill=c, width=2)
draw.rectangle([4, 4, m - 2, m - 2], fill=(80, 160, 255, 100))
return img
_MENU_ROWS = [
('Wingnet (v2.1.1)', False),
(None, None),
('── 半版 ─────────────────────', False),
('Ctrl+Win+← 左半', False),
('Ctrl+Win+→ 右半', False),
('Ctrl+Win+↑ 上半', False),
('Ctrl+Win+↓ 下半', False),
('── 最大化 / 置中 ─────────────', False),
('Ctrl+Win+Enter 最大化', False),
('Ctrl+Win+C 置中 (不改變大小)', False),
('── 四角對齊 (不改變大小) ─────', False),
('Ctrl+Win+Num7 對齊左上', False),
('Ctrl+Win+Num9 對齊右上', False),
('Ctrl+Win+Num1 對齊左下', False),
('Ctrl+Win+Num3 對齊右下', False),
('── 四角 ─────────────────────', False),
('Ctrl+Win+U 左上角', False),
('Ctrl+Win+I 右上角', False),
('Ctrl+Win+J 左下角', False),
('Ctrl+Win+K 右下角', False),
('── 三等分 ───────────────────', False),
('Ctrl+Win+D 左 ⅓', False),
('Ctrl+Win+F 中 ⅓', False),
('Ctrl+Win+G 右 ⅓', False),
('Ctrl+Win+E 左 ⅔', False),
('Ctrl+Win+T 右 ⅔', False),
(None, None),
('結束 Wingnet', True),
]
def _on_quit(icon: pystray.Icon, _item) -> None:
stop_hook()
icon.stop()
def create_tray_icon() -> pystray.Icon:
items = []
for label, clickable in _MENU_ROWS:
if label is None:
items.append(pystray.Menu.SEPARATOR)
elif clickable:
items.append(pystray.MenuItem(label, _on_quit))
else:
items.append(pystray.MenuItem(label, None, enabled=False))
return pystray.Icon('Wingnet', _make_icon(), 'Wingnet', pystray.Menu(*items))
def main() -> None:
try:
ctypes.windll.shcore.SetProcessDpiAwareness(2)
except OSError:
user32.SetProcessDPIAware()
kernel32.SetPriorityClass(kernel32.GetCurrentProcess(), HIGH_PRIORITY_CLASS)
hook_thread = threading.Thread(
target=_hook_thread_func, daemon=True, name='Wingnet-Hook'
)
hook_thread.start()
_hook_ready.wait(timeout=3)
if not _hook_handle:
print('[Wingnet] Hook 安裝失敗,程式結束。', file=sys.stderr)
sys.exit(1)
icon = create_tray_icon()
icon.run()
stop_hook()
hook_thread.join(timeout=2)
if __name__ == '__main__':
main()Python展開