Windows 11用の仮想デスクトップの拡張ユーティリティが存在しないなあと思っていたら、どうやら AutoHotkey というユーティリティで自分でスクリプトを書くのが流行りのようだった。
必要なのは次の2つ
VirtualDesktopAccessor.dll は AutoHotkey のスクリプトファイル(.ahk)と同じディレクトリに配置する。
欲しい機能
- Ctrl + Win + Alt + Left / Right のホットキーで、フォーカス中のウィンドウを隣の仮想デスクトップへ移動する
- 画面最上端でマウスホイールを操作することで、仮想デスクトップを切り替える。
VirtualDesktopAccessor.ahk
v2.0 用のスクリプはあまり見かけなかったので、chatGPT に作らせてみた。
; VirtualDesktopAccessor DLL のパスを設定
DllPath := A_ScriptDir "\VirtualDesktopAccessor.dll"
; DLL をロード
DllCall("LoadLibrary", "Str", DllPath)
; ウィンドウを右のデスクトップに移動
#^!Right:: {
hwnd := WinExist("A") ; フォーカス中のウィンドウハンドルを取得
DllCall(DllPath "\MoveWindowToDesktopNumber", "Ptr", hwnd, "Int", GetCurrentDesktopNumber() + 1, "Int")
SwitchToNextDesktop(1)
}
; ウィンドウを左のデスクトップに移動
#^!Left:: {
hwnd := WinExist("A") ; フォーカス中のウィンドウハンドルを取得
DllCall(DllPath "\MoveWindowToDesktopNumber", "Ptr", hwnd, "Int", GetCurrentDesktopNumber() - 1, "Int")
SwitchToNextDesktop(-1)
}
; マウスホイールアップで前の仮想デスクトップへ
~WheelUp::
{
if IsMouseAtScreenTop() {
SwitchToNextDesktop(-1)
}
}
; マウスホイールダウンで次の仮想デスクトップへ
~WheelDown::
{
if IsMouseAtScreenTop() {
SwitchToNextDesktop(1)
}
}
; 現在の仮想デスクトップ番号を取得
GetCurrentDesktopNumber() {
return DllCall(DllPath "\GetCurrentDesktopNumber", "Int")
}
; 仮想デスクトップを切り替える
SwitchToNextDesktop(direction) {
currentDesktop := GetCurrentDesktopNumber()
targetDesktop := currentDesktop + direction
; 左端、右端でループさせない
maxDesktop := 3 ; 仮想デスクトップの数(調整が必要)
if targetDesktop < 0 {
targetDesktop := 0
} else if targetDesktop >= maxDesktop {
targetDesktop := maxDesktop - 1
}
DllCall(DllPath "\GoToDesktopNumber", "Int", targetDesktop)
}
; マウスカーソルが画面上端にいるか確認
IsMouseAtScreenTop() {
CoordMode("Mouse", "Screen") ; 画面全体の座標を使用
xpos := 0
ypos := 0
MouseGetPos(&xpos, &ypos) ; マウス位置を取得
;ToolTip("Mouse Position: (" . xpos . "," . ypos . ")")
return (ypos < 10) ; 画面上端の範囲(10px 以内)
}
追記
ホイール操作が過敏すぎて仮想デスクトップが3つ以上あるときに使いづらかったので、規定時間のうちに1回転以上回したときに反応するようにした。
規定時間内に1回転せずタイムアウトしたときにカウンタを初期化する処理を入れてたら、めちゃ複雑なスクリプトになってしまった。
自分で考えて作ったというより、AIに何度もツッコミを入れてブラッシュアップしたスクリプトだ。AI万歳!!
; VirtualDesktopAccessor DLL のパスを設定
DllPath := A_ScriptDir "\VirtualDesktopAccessor.dll"
global WheelUpCount := 0
global WheelDownCount := 0
DebounceDelay := 100 ; milliseconds
RequiredCount := 1
; DLL をロード
DllCall("LoadLibrary", "Str", DllPath)
; ウィンドウを右のデスクトップに移動
#^!Right:: {
hwnd := WinExist("A") ; フォーカス中のウィンドウハンドルを取得
DllCall(DllPath "\MoveWindowToDesktopNumber", "Ptr", hwnd, "Int", GetCurrentDesktopNumber() + 1, "Int")
SwitchToNextDesktop(1)
}
; ウィンドウを左のデスクトップに移動
#^!Left:: {
hwnd := WinExist("A") ; フォーカス中のウィンドウハンドルを取得
DllCall(DllPath "\MoveWindowToDesktopNumber", "Ptr", hwnd, "Int", GetCurrentDesktopNumber() - 1, "Int")
SwitchToNextDesktop(-1)
}
; マウスホイール操作で前後の仮想デスクトップへ
~WheelUp::
{
if IsMouseAtScreenTop() {
global WheelUpCount
WheelUpCount++
SetTimer(HandleWheelUp, DebounceDelay)
}
}
~WheelDown::
{
if IsMouseAtScreenTop() {
global WheelDownCount
WheelDownCount++
SetTimer(HandleWheelDown, DebounceDelay)
}
}
HandleWheelUp() {
global WheelUpCount
if WheelUpCount > 0 {
SetTimer(HandleWheelUp, -DebounceDelay) ; タイマを一度だけ実行
; MsgBox "WheelUpCount" . WheelUpCount
if WheelUpCount >= RequiredCount {
SwitchToNextDesktop(-1)
}
WheelUpCount := 0
}
}
HandleWheelDown() {
global WheelDownCount
if WheelDownCount > 0 {
SetTimer(HandleWheelDown, -DebounceDelay) ; タイマを一度だけ実行
; MsgBox "WheelDownCount" . WheelDownCount
if WheelDownCount >= RequiredCount {
SwitchToNextDesktop(1)
}
WheelDownCount := 0
}
}
; 現在の仮想デスクトップ番号を取得
GetCurrentDesktopNumber() {
return DllCall(DllPath "\GetCurrentDesktopNumber", "Int")
}
; 仮想デスクトップを切り替える
SwitchToNextDesktop(direction) {
currentDesktop := GetCurrentDesktopNumber()
targetDesktop := currentDesktop + direction
; 左端、右端でループさせない
maxDesktop := 3
if targetDesktop < 0 {
targetDesktop := 0
} else if targetDesktop >= maxDesktop {
targetDesktop := maxDesktop - 1
}
DllCall(DllPath "\GoToDesktopNumber", "Int", targetDesktop)
}
; マウスカーソルが画面上端にいるか確認
IsMouseAtScreenTop() {
CoordMode("Mouse", "Screen") ; 画面全体の座標を使用
xpos := 0
ypos := 0
MouseGetPos(&xpos, &ypos) ; マウス位置を取得
;ToolTip("Mouse Position: (" . xpos . "," . ypos . ")")
return (ypos < 10) ; 画面上端の範囲(10px 以内)
}