Skip to content

Instantly share code, notes, and snippets.

@Pagliacii
Created March 25, 2023 11:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Pagliacii/232343c987b90c736cdca181abb8a674 to your computer and use it in GitHub Desktop.
Save Pagliacii/232343c987b90c736cdca181abb8a674 to your computer and use it in GitHub Desktop.
Make your life easy when you're using Windows
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input ; Recommended for new scripts due to its superior speed and reliability.
#Persistent ; Keeps a script permanently running (that is, until the user closes it or ExitApp is encountered).
; Globals
DesktopCount = 2 ; Windows starts with 2 desktops at boot
CurrentDesktop = 1 ; Desktop count is 1-indexed (Microsoft numbers them this way)
; DLL
hVirtualDesktopAccessor := DllCall("LoadLibrary", "Str", A_ScriptDir . "\virtual-desktop-accessor.dll", "Ptr")
global IsWindowOnDesktopNumberProc := DllCall("GetProcAddress", Ptr, hVirtualDesktopAccessor, AStr, "IsWindowOnDesktopNumber", "Ptr")
;
; This function examines the registry to build an accurate list of the current virtual desktops and which one we're currently on.
; Current desktop UUID appears to be in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\1\VirtualDesktops
; List of desktops appears to be in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops
;
mapDesktopsFromRegistry() {
global CurrentDesktop, DesktopCount
; Get the current desktop UUID. Length should be 32 always, but there's no guarantee this couldn't change in a later Windows release so we check.
IdLength := 32
SessionId := getSessionId()
if (SessionId) {
RegRead, CurrentDesktopId, HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\%SessionId%\VirtualDesktops, CurrentVirtualDesktop
if (CurrentDesktopId) {
IdLength := StrLen(CurrentDesktopId)
}
}
; Get a list of the UUIDs for all virtual desktops on the system
RegRead, DesktopList, HKEY_CURRENT_USER, SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops, VirtualDesktopIDs
if (DesktopList) {
DesktopListLength := StrLen(DesktopList)
; Figure out how many virtual desktops there are
DesktopCount := DesktopListLength / IdLength
}
else {
DesktopCount := 1
}
; Parse the REG_DATA string that stores the array of UUID's for virtual desktops in the registry.
i := 0
while (CurrentDesktopId and i < DesktopCount) {
StartPos := (i * IdLength) + 1
DesktopIter := SubStr(DesktopList, StartPos, IdLength)
OutputDebug, The iterator is pointing at %DesktopIter% and count is %i%.
; Break out if we find a match in the list. If we didn't find anything, keep the
; old guess and pray we're still correct :-D.
if (DesktopIter = CurrentDesktopId) {
CurrentDesktop := i + 1
OutputDebug, Current desktop number is %CurrentDesktop% with an ID of %DesktopIter%.
break
}
i++
}
}
;
; This functions finds out ID of current session.
;
getSessionId()
{
ProcessId := DllCall("GetCurrentProcessId", "UInt")
if ErrorLevel {
OutputDebug, Error getting current process id: %ErrorLevel%
return
}
OutputDebug, Current Process Id: %ProcessId%
DllCall("ProcessIdToSessionId", "UInt", ProcessId, "UInt*", SessionId)
if ErrorLevel {
OutputDebug, Error getting session id: %ErrorLevel%
return
}
OutputDebug, Current Session Id: %SessionId%
return SessionId
}
;
; This function switches to the desktop number provided.
;
switchDesktopByNumber(targetDesktop)
{
global CurrentDesktop, DesktopCount
; There are three issues with switching desktops with active windows in intermediary desktops:
; 1. Occasionally, not all "go right" or "go left" hotkeys are resulting in a switched desktop, this results in switcher getting stuck midway (not at the destination desktop, while at the end CurrentDesktop gets itself set to the target desktop number)
; 2. Switching is not instantaneous anymore, this introduces rapid flashing (each desktop shows itself for a brief moment)
; 3. Flashing orange notifications on taskbar on intermediary windows (https://github.com/pmb6tz/windows-desktop-switcher/issues/8)
; Therefore we will activate taskbar
WinActivate, ahk_class Shell_TrayWnd
; Re-generate the list of desktops and where we fit in that. We do this because
; the user may have switched desktops via some other means than the script.
mapDesktopsFromRegistry()
; Don't attempt to switch to an invalid desktop
if (targetDesktop > DesktopCount || targetDesktop < 1) {
OutputDebug, [invalid] target: %targetDesktop% current: %CurrentDesktop%
return
}
WinActivate, ahk_class Shell_TrayWnd ; Fixes the issue of active windows in intermediate desktops capturing the switch shortcut and therefore delaying or stopping the switchng sequence. More info: https://github.com/pmb6tz/windows-desktop-switcher/pull/19
; Go right until we reach the desktop we want
while(CurrentDesktop < targetDesktop) {
Send ^#{Right}
CurrentDesktop++
OutputDebug, [right] target: %targetDesktop% current: %CurrentDesktop%
}
; Go left until we reach the desktop we want
while(CurrentDesktop > targetDesktop) {
Send ^#{Left}
CurrentDesktop--
OutputDebug, [left] target: %targetDesktop% current: %CurrentDesktop%
}
Sleep, 50
focusTheForemostWindow(targetDesktop)
}
focusTheForemostWindow(targetDesktop) {
foremostWindowId := getForemostWindowIdOnDesktop(targetDesktop)
WinActivate, ahk_id %foremostWindowId%
}
getForemostWindowIdOnDesktop(n) {
n := n - 1 ; Desktops start at 0, while in script it's 1
; winIDList contains a list of windows IDs ordered from the top to the bottom for each desktop.
WinGet winIDList, list
Loop % winIDList {
windowID := % winIDList%A_Index%
windowIsOnDesktop := DllCall(IsWindowOnDesktopNumberProc, UInt, windowID, UInt, n)
; Select the first (and foremost) window which is in the specified desktop.
if (windowIsOnDesktop == 1) {
return windowID
}
}
}
;
; This function creates a new virtual desktop and switches to it
;
createVirtualDesktop()
{
global CurrentDesktop, DesktopCount
Send, #^d
DesktopCount++
CurrentDesktop = %DesktopCount%
OutputDebug, [create] desktops: %DesktopCount% current: %CurrentDesktop%
}
;
; This function deletes the current virtual desktop
;
deleteVirtualDesktop()
{
global CurrentDesktop, DesktopCount
Send, #^{F4}
DesktopCount--
CurrentDesktop--
OutputDebug, [delete] desktops: %DesktopCount% current: %CurrentDesktop%
}
;
; This function debounces the mouse left click
;
debounceLeftClick()
{
Sleep, 10
if !GetKeyState("LButton", "P")
{
n := 1
BlockInput, On
Loop {
Sleep, 10
if (n++ > 9)
break
}
Click
}
else
{
MouseClick, left,,, 1, 0, D ; Hold down the left mouse button.
Loop
{
Sleep, 10
if !GetKeyState("LButton", "P") ; The key has been released, so break out of the loop.
break
}
MouseClick, left,,, 1, 0, U ; Release the mouse button.
}
}
;
; This function pops up the input box to ask user to enter some Unicode to input the corresponding
; character into the current input area.
;
getUnicodeFromInputBox()
{
Sleep, 10
InputBox, Code, AutoHotkey, Enter the Unicode, , 320, 112
Send, {U+%Code%}
}
;
; This function checks if the specified name process exists
; and returns the unique ID (HWND) of the first matching window.
;
processExist(name) {
DetectHiddenWindows, On
existed := WinExist("ahk_exe" name)
DetectHiddenWindows, Off
Return existed
}
;
; This function runs a BAT file
;
runStartEmacsServerBatFile() {
If not processExist("emacs.exe") {
Run .\StartEmacsServer.bat,,Hide
}
}
registerTrayMenuItems() {
Menu, Tray, NoStandard ; Removes original menu
Menu, Tray, Add, Edit via Sublime Text 3, editByST3Handler ; Creates a new menu item.
Menu, Tray, Add, Edit via VSCode, editByVSCodeHandler ; Creates a new menu item.
Menu, Tray, Add ; Creates a separator line.
Menu, Tray, Standard ; Puts original menu back
Menu, Tray, Default, Edit via Sublime Text 3
}
editByST3Handler() {
Run subl "%A_ScriptFullPath%",,Hide
}
editByVSCodeHandler() {
EnvGet, Scoop, "SCOOP"
If !FileExist(Scoop)
Scoop := "D:\Scoop"
code := Scoop . "\apps\vscode\current\bin\code.cmd"
Run "%code%" "%A_ScriptFullPath%",,Hide
}
; Main
SetKeyDelay, 75
registerTrayMenuItems()
mapDesktopsFromRegistry()
OutputDebug, [loading] desktops: %DesktopCount% current: %CurrentDesktop%
runStartEmacsServerBatFile()
OutputDebug, [loading] Emacs server is running now
; User config!
; This section binds the key combo to the switch/create/delete actions
LWin & 1::switchDesktopByNumber(1)
LWin & 2::switchDesktopByNumber(2)
LWin & 3::switchDesktopByNumber(3)
LWin & 4::switchDesktopByNumber(4)
LWin & 5::switchDesktopByNumber(5)
LWin & 6::switchDesktopByNumber(6)
LWin & 7::switchDesktopByNumber(7)
LWin & 8::switchDesktopByNumber(8)
LWin & 9::switchDesktopByNumber(9)
CapsLock & n::switchDesktopByNumber(CurrentDesktop + 1)
CapsLock & p::switchDesktopByNumber(CurrentDesktop - 1)
CapsLock & s::switchDesktopByNumber(CurrentDesktop + 1)
CapsLock & a::switchDesktopByNumber(CurrentDesktop - 1)
CapsLock & c::createVirtualDesktop()
CapsLock & d::deleteVirtualDesktop()
; Alternate keys for this config. Adding these because DragonFly (python) doesn't send CapsLock correctly.
^!1::switchDesktopByNumber(1)
^!2::switchDesktopByNumber(2)
^!3::switchDesktopByNumber(3)
^!4::switchDesktopByNumber(4)
^!5::switchDesktopByNumber(5)
^!6::switchDesktopByNumber(6)
^!7::switchDesktopByNumber(7)
^!8::switchDesktopByNumber(8)
^!9::switchDesktopByNumber(9)
^!n::switchDesktopByNumber(CurrentDesktop + 1)
^!p::switchDesktopByNumber(CurrentDesktop - 1)
^!s::switchDesktopByNumber(CurrentDesktop + 1)
^!a::switchDesktopByNumber(CurrentDesktop - 1)
^!c::createVirtualDesktop()
^!d::deleteVirtualDesktop()
^!t::Run, D:\Scoop\apps\wezterm\current\wezterm.exe,,Hide
CapsLock & x::getUnicodeFromInputBox()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment