From 63c378cbc42f21b69eac2273ffe7494b407affee Mon Sep 17 00:00:00 2001 From: Klesh Wong Date: Fri, 29 Jan 2021 23:32:21 +0800 Subject: [PATCH] [feature] windows management --- win/WinGetPosEx.ahk | 161 +++++++++++++++++++++++++ win/WindowsTerminal/settings.json | 135 +++++++++++++++++++++ win/win.ahk | 189 +++++++++++++++++------------- 3 files changed, 404 insertions(+), 81 deletions(-) create mode 100644 win/WinGetPosEx.ahk create mode 100644 win/WindowsTerminal/settings.json diff --git a/win/WinGetPosEx.ahk b/win/WinGetPosEx.ahk new file mode 100644 index 0000000..8320e7e --- /dev/null +++ b/win/WinGetPosEx.ahk @@ -0,0 +1,161 @@ +;------------------------------ +; +; Function: WinGetPosEx +; +; Original author: jballi (https://autohotkey.com/boards/viewtopic.php?t=3392) +; +; Update author: RiseUp +; +; Description: +; +; Gets the position, size, and offset of a window. See the *Remarks* section +; for more information. +; +; Parameters: +; +; hWindow - Handle to the window. +; +; X, Y, Width, Height - Output variables. [Optional] If defined, these +; variables contain the coordinates of the window relative to the +; upper-left corner of the screen (X and Y), and the Width and Height of +; the window. +; +; Offset_Left, Offset_Top, Offset_Right, Offset_Bottom - Output variables. +; [Optional] Offset, in pixels, of the actual position of the window +; versus the position of the window as reported by GetWindowRect. If +; moving the window to specific coordinates, add these offset values to +; the appropriate coordinate (X and/or Y) to reflect the true size of the +; window. +; +; Returns: +; +; If successful, the address of a RECTPlus structure is returned. The first +; 16 bytes contains a RECT structure that contains the dimensions of the +; bounding rectangle of the specified window. The dimensions are given in +; screen coordinates that are relative to the upper-left corner of the screen. +; The next 16 bytes contain the offsets (4-byte integer for each of left, +; top, right, and bottom offsets). +; +; Also if successful (and if defined), the output variables (X, Y, Width, +; Height, Offset_Left, Offset_Top, Offset_Right, and Offset_Bottom) are +; updated. See the *Parameters* section for more more information. +; +; If not successful, FALSE is returned. +; +; Requirement: +; +; Windows 2000+ +; +; Remarks, Observations, and Changes: +; +; * Starting with Windows Vista, Microsoft includes the Desktop Window Manager +; (DWM) along with Aero-based themes that use DWM. Aero themes provide new +; features like a translucent glass design with subtle window animations. +; Unfortunately, the DWM doesn't always conform to the OS rules for size and +; positioning of windows. If using an Aero theme, many of the windows are +; actually larger than reported by Windows when using standard commands (Ex: +; WinGetPos, GetWindowRect, etc.) and because of that, are not positioned +; correctly when using standard commands (Ex: gui Show, WinMove, etc.). This +; function was created to 1) identify the true position and size of all +; windows regardless of the window attributes, desktop theme, or version of +; Windows and to 2) identify the appropriate offset that is needed to position +; the window if the window is a different size than reported. +; +; * The true size, position, and offset of a window cannot be determined until +; the window has been rendered. See the example script for an example of how +; to use this function to position a new window. +; +; * 20150906: The "dwmapi\DwmGetWindowAttribute" function can return odd errors +; if DWM is not enabled. One error I've discovered is a return code of +; 0x80070006 with a last error code of 6, i.e. ERROR_INVALID_HANDLE or "The +; handle is invalid." To keep the function operational during this types of +; conditions, the function has been modified to assume that all unexpected +; return codes mean that DWM is not available and continue to process without +; it. When DWM is a possibility (i.e. Vista+), a developer-friendly messsage +; will be dumped to the debugger when these errors occur. +; +; * 20171126: (RiseUp) Changed function to return 4 offset values instead of 2. +; Windows 10 has different offsets for the top versus the bottom of a window, +; so this function no longer assumes a symmetrical offset border around a +; given window. +; +; Credit: +; +; Idea and some code from *KaFu* (AutoIt forum) +; +;------------------------------------------------------------------------------- +WinGetPosEx(hWindow,ByRef X="",ByRef Y="",ByRef Width="",ByRef Height="" + ,ByRef Offset_Left="",ByRef Offset_Top="" + ,ByRef Offset_Right="",ByRef Offset_Bottom="") + { + Static Dummy5693 + ,RECTPlus + ,S_OK:=0x0 + ,DWMWA_EXTENDED_FRAME_BOUNDS:=9 + + ;-- Workaround for AutoHotkey Basic + PtrType:=(A_PtrSize=8) ? "Ptr":"UInt" + + ;-- Get the window's dimensions + ; Note: Only the first 16 bytes of the RECTPlus structure are used by the + ; DwmGetWindowAttribute and GetWindowRect functions. + VarSetCapacity(RECTPlus,32,0) + DWMRC:=DllCall("dwmapi\DwmGetWindowAttribute" + ,PtrType,hWindow ;-- hwnd + ,"UInt",DWMWA_EXTENDED_FRAME_BOUNDS ;-- dwAttribute + ,PtrType,&RECTPlus ;-- pvAttribute + ,"UInt",16) ;-- cbAttribute + + if (DWMRC<>S_OK) + { + if ErrorLevel in -3,-4 ;-- Dll or function not found (older than Vista) + { + ;-- Do nothing else (for now) + } + else + outputdebug, + (ltrim join`s + Function: %A_ThisFunc% - + Unknown error calling "dwmapi\DwmGetWindowAttribute". + RC=%DWMRC%, + ErrorLevel=%ErrorLevel%, + A_LastError=%A_LastError%. + "GetWindowRect" used instead. + ) + + ;-- Collect the position and size from "GetWindowRect" + DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECTPlus) + } + + ;-- Populate the output variables + X:=Left := NumGet(RECTPlus,0,"Int") + Y:=Top := NumGet(RECTPlus,4,"Int") + Right := NumGet(RECTPlus,8,"Int") + Bottom := NumGet(RECTPlus,12,"Int") + Width := Right-Left + Height := Bottom-Top + Offset_Left := 0 + Offset_Top := 0 + Offset_Right := 0 + Offset_Bottom := 0 + + ;-- If DWM is not used (older than Vista or DWM not enabled), we're done + if (DWMRC<>S_OK) + Return &RECTPlus + + ;-- Collect dimensions via GetWindowRect + VarSetCapacity(RECT,16,0) + DllCall("GetWindowRect",PtrType,hWindow,PtrType,&RECT) + GWR_Left := NumGet(RECT,0,"Int") + GWR_Top := NumGet(RECT,4,"Int") + GWR_Right := NumGet(RECT,8,"Int") + GWR_Bottom := NumGet(RECT,12,"Int") + + ;-- Calculate offsets and update output variables + NumPut(Offset_Left := Left - GWR_Left,RECTPlus,16,"Int") + NumPut(Offset_Top := Top - GWR_Top ,RECTPlus,20,"Int") + NumPut(Offset_Right := GWR_Right - Right ,RECTPlus,24,"Int") + NumPut(Offset_Bottom := GWR_Bottom - Bottom ,RECTPlus,28,"Int") + + Return &RECTPlus + } diff --git a/win/WindowsTerminal/settings.json b/win/WindowsTerminal/settings.json new file mode 100644 index 0000000..3e783a0 --- /dev/null +++ b/win/WindowsTerminal/settings.json @@ -0,0 +1,135 @@ +// This file was initially generated by Windows Terminal 1.4.3243.0 +// It should still be usable in newer versions, but newer versions might have additional +// settings, help text, or changes that you will not see unless you clear this file +// and let us generate a new one for you. + +// To view the default settings, hold "alt" while clicking on the "Settings" button. +// For documentation on these settings, see: https://aka.ms/terminal-documentation +{ + "$schema": "https://aka.ms/terminal-profiles-schema", + "initialPosition": "2118,0", + "initialRows": 71, + "initialCols": 142, + + "defaultProfile": "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + + // You can add more global application settings here. + // To learn more about global settings, visit https://aka.ms/terminal-global-settings + + // If enabled, selections are automatically copied to your clipboard. + "copyOnSelect": false, + + // If enabled, formatted data is also copied to your clipboard + "copyFormatting": false, + + // A profile specifies a command to execute paired with information about how it should look and feel. + // Each one of them will appear in the 'New Tab' dropdown, + // and can be invoked from the commandline with `wt.exe -p xxx` + // To learn more about profiles, visit https://aka.ms/terminal-profile-settings + "profiles": { + "defaults": { + // Put settings here that you want to apply to all profiles. + "useAcrylic": false, + "acrylicOpacity": 0.6, + "colorScheme": "One Half Dark", + "cursorShape": "filledBox", + // install the non-window-compatible version + "fontFace": "agave Nerd Font r", + "fontSize": 14 + }, + "list": [ + { + // Make changes here to the powershell.exe profile. + "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + "name": "Windows PowerShell", + "commandline": "powershell.exe", + "hidden": false + }, + { + // Make changes here to the cmd.exe profile. + "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}", + "name": "Command Prompt", + "commandline": "cmd.exe", + "hidden": false + }, + { + "guid": "{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}", + "hidden": false, + "name": "Ubuntu-20.04", + "source": "Windows.Terminal.Wsl" + }, + { + "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}", + "hidden": false, + "name": "Azure Cloud Shell", + "source": "Windows.Terminal.Azure" + }, + { + "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + "hidden": false, + "name": "PowerShell", + "source": "Windows.Terminal.PowershellCore" + }, + { + "guid": "{58ad8b0c-3ef8-5f4d-bc6f-13e4c00f2530}", + "hidden": false, + "name": "Debian", + "source": "Windows.Terminal.Wsl" + } + ] + }, + + // Add custom color schemes to this array. + // To learn more about color schemes, visit https://aka.ms/terminal-color-schemes + "schemes": [ + { + "name": "Frost", + "background": "#666666", + "black": "#3C5712", + "blue": "#17b2ff", + "brightBlack": "#749B36", + "brightBlue": "#27B2F6", + "brightCyan": "#13A8C0", + "brightGreen": "#89AF50", + "brightPurple": "#F2A20A", + "brightRed": "#F49B36", + "brightWhite": "#741274", + "brightYellow": "#991070", + "cyan": "#3C96A6", + "foreground": "#000000", + "green": "#6AAE08", + "purple": "#991070", + "red": "#8D0C0C", + "white": "#6E386E", + "yellow": "#991070" + } + ], + + // Add custom actions and keybindings to this array. + // To unbind a key combination from your defaults.json, set the command to "unbound". + // To learn more about actions and keybindings, visit https://aka.ms/terminal-keybindings + "actions": [ + // Copy and paste are bound to Ctrl+Shift+C and Ctrl+Shift+V in your defaults.json. + // These two lines additionally bind them to Ctrl+C and Ctrl+V. + // To learn more about selection, visit https://aka.ms/terminal-selection + { "command": { "action": "copy", "singleLine": false }, "keys": "ctrl+c" }, + { "command": "paste", "keys": "ctrl+v" }, + + // Press Ctrl+Shift+F to open the search box + { "command": "find", "keys": "ctrl+shift+f" }, + { "command": "prevTab", "keys": "ctrl+pgup" }, + { "command": "nextTab", "keys": "ctrl+pgdn" }, + // Press Alt+Shift+D to open a new pane. + // - "split": "auto" makes this pane open in the direction that provides the most surface area. + // - "splitMode": "duplicate" makes the new pane use the focused pane's profile. + // To learn more about panes, visit https://aka.ms/terminal-panes + { + "command": { + "action": "splitPane", + "split": "auto", + "splitMode": "duplicate" + }, + "keys": "alt+shift+d" + } + ] +} diff --git a/win/win.ahk b/win/win.ahk index fc0ac60..dcf3d8d 100644 --- a/win/win.ahk +++ b/win/win.ahk @@ -1,18 +1,33 @@ +#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases. +; #Warn ; Enable warnings to assist with detecting common errors. +SendMode Input ; Recommended for new scripts due to its superior speed and reliability. +SetWorkingDir %A_ScriptDir% ; Ensures a consistent starting directory. +CoordMode, Mouse, Screen ; mouse coordinates relative to the screen + +; ========================= +; CONFIGURATION +; ========================= +global RATIO := 0.618 + +; ========================= +; BINDINGS +; ========================= ^Esc:: Send ^{``} *#q:: !F4 #Esc:: Reload - - ^BS:: Send ^a Send {BS} return - - #=::SoundSet,+5 #-::SoundSet,-5 #BS::#l - +#f:: ToggleActiveWinMaximum() +^#p::ShowActiveWinGeometry() +#j:: FocusWinByDirection("right") +#k:: FocusWinByDirection("left") +#+j::MoveActiveWinByDirection("right") +#+k::MoveActiveWinByDirection("left") ; Ctrl + Alt + v : paste as plain text ^!v:: @@ -24,6 +39,7 @@ VarSetCapacity(Clip0, 0) ; Free memory Return +; Ctrl + Alt + Shift + v : paste as plain test a replace \ with / ^+!v:: Clip0 = %ClipBoardAll% ClipBoard := StrReplace(ClipBoard, "\", "/") ; Convert to plain text @@ -33,20 +49,36 @@ Return VarSetCapacity(Clip0, 0) ; Free memory Return -; #IfWinActive ahk_class mintty -; ^+v::Send +{Ins} -; ^+c::Send ^{Ins} -; #IfWinActive - +; move cursor to center of window after switching +~#1 Up:: +~#2 Up:: +~#3 Up:: +~#4 Up:: +~#5 Up:: +~#6 Up:: +~#7 Up:: +~#8 Up:: +~#9 Up:: + Sleep 100 + SetCursorToCenterOfActiveWin() + return +~!Tab Up:: + while (GetKeyState("Alt") != 0 or GetKeyState("Tab") != 0) { + Sleep 50 + } + Sleep 100 + WinGetPos, x, y, w, h, A + SetCursorToCenterOfActiveWin() + return +; ========================= +; CAPSLOCK AS HYBRID KEY +; ========================= ; Capslock & h:: Send {Left} ; Capslock & j:: Send {Down} ; Capslock & k:: Send {Up} ; Capslock & l:: Send {Right} -; Capslock & -:: Send {Volume_Down} -; Capslock & =:: Send {Volume_Up} -; Capslock & \:: Send {Media_Play_Pause} ; Capslock & y:: Send {Browser_Back} ; Capslock & u:: Send ^{PgUp} ; Capslock & i:: Send ^{PgDn} @@ -65,94 +97,89 @@ Return ; Return ; Capslock::return -MoveMouseAct(x, y) { +; ========================= +; FUNCTIONS +; ========================= + +#Include, WinGetPosEx.ahk + +ShowGeometry(x, y, w, h) { + MsgBox, , Geometry,% Format("x:{}, y:{}, w: {}, h: {}", x, y, w, h) +} + +ShowActiveWinGeometry() { + WinGetPos x, y, w, h, A + ShowGeometry(x, y, w, h) +} + +SetCursorPos(x, y) { DllCall("SetCursorPos", "int", x, "int", y) +} + +FocusWinUnderCursor() { MouseGetPos, MouseX, MouseY, WinId WinActivate, ahk_id %WinId% - ; Sleep 0.2 +} + +SetCursorToCenterOfActiveWin() { WinGetPos x, y, w, h, A - DllCall("SetCursorPos", "int", x + w / 2, "int", y + h / 2) + SetCursorPos(x + w / 2, y + h / 2) } -MoveCursorMon(toRight) { - CoordMode, Mouse, Screen ; mouse coordinates relative to the screen +FocusWinByPos(x, y) { + SetCursorPos(x, y) + FocusWinUnderCursor() + SetCursorToCenterOfActiveWin() +} + +GetCursorMonGeometry(ByRef x, ByRef y, ByRef w, ByRef h) { MouseGetPos, MouseX, MouseY SysGet, mc, MonitorCount - mi := 0 - x := -10000 - if (toRight) - x:= 10000 - y := MouseY - loop { - SysGet, mon, Monitor, %mi% - monX := Floor((monLeft + monRight) / 2) - monY := Floor((monTop + monBottom) / 2) - if (toRight and monLeft > MouseX and monX < x) Or (!toRight and monRight < MouseX and monX > x) { - x := monX - y := monY - } - if (++mi >= mc) - break - } - ; MouseMove, x, y - MoveMouseAct(x, y) -} - -MoveCursorWin(toRight) { - CoordMode, Mouse, Screen ; mouse coordinates relative to the screen - MouseGetPos, MouseX, MouseY ; find current monitor - SysGet, mc, MonitorCount mi := 0 loop { - SysGet, mon, Monitor, %mi% + SysGet, mon, MonitorWorkArea, %mi% if (monLeft < MouseX and monRight > MouseX) { - x := monLeft + (monRight - monLeft) * (toRight ? 0.75 : 0.25) - y := monTop + monBottom / 2 - MoveMouseAct(x, y) - break + x := monLeft + y := monTop + w := monRight - monLeft + h := monBottom - monTop + return } - if (++mi >= mc) - break } } +FocusWinByDirection(direction) { + global RATIO + GetCursorMonGeometry(x, y, w, h) + wf := RATIO / 2 + hf := 0.5 + if (direction = "right") + wf := RATIO + (1 - RATIO) / 2 + FocusWinByPos(x + w * wf, y + h * hf) +} -#f:: +MoveActiveWinByDirection(direction) { + global RATIO + GetCursorMonGeometry(x, y, w, h) + activeWinId := WinExist("A") + WinGetPosEx(activeWinId, wx, wy, ww, wh, l, t, r, b) + wx := x + wy := y + ww := floor(w * RATIO) + wh := h + if (direction = "right") { + wx := ww + ww := w - ww + } + WinMove, A,, wx - l, wy - t, ww + l + r, wh + t + b +} + +ToggleActiveWinMaximum() { WinGet, isMax, MinMax, A if (isMax) { WinRestore, A } else { WinMaximize, A } - return -#,:: #Left -#.:: #Right -#+u:: #+Left -#+i:: #+Right -#u:: MoveCursorMon(False) -#i:: MoveCursorMon(True) -#k:: MoveCursorWin(False) -#j:: MoveCursorWin(True) - -~#1 Up:: -~#2 Up:: -~#3 Up:: -~#4 Up:: -~#5 Up:: -~#6 Up:: -~#7 Up:: -~#8 Up:: -~#9 Up:: - Sleep 100 - WinGetPos, x, y, w, h, A - DllCall("SetCursorPos", "int", x + w / 2, "int", y + h / 2) - return -~!Tab Up:: - while (GetKeyState("Alt") != 0 or GetKeyState("Tab") != 0) { - Sleep 50 - } - Sleep 100 - WinGetPos, x, y, w, h, A - DllCall("SetCursorPos", "int", x + w / 2, "int", y + h / 2) - return +} \ No newline at end of file