WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

Post your working scripts, libraries and tools
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

30 Oct 2015, 12:13

I wrote this as part of another project I am working on, but it seems to be fairly universally useful, so here it is as a configurable class:

Usage:
Set the param passed to the WheelSwitcher constructor to a valid ahk title string that will match all windows of that type.
Open multiple windows of that type and fullscreen them on the same monitor.

Then, if you move the mouse to within 10px of the edge of either side of the screen and roll the mouse wheel, it will cycle between the windows that match that title.

Code: Select all

#SingleInstance force
WheelLookup := {-1: "Down", 1: "Up"}
WheelUp::
	DoSwitch(1)
	return

WheelDown::
	DoSwitch(-1)
	return

DoSwitch(dir){
	global WheelLookup
	WinGet, hwnd, ID, A
	WinGet, ProcessID, PID, % "ahk_id " hwnd
	; Is the active window a valid candidate for wheel switch?
	if (IsWindowFullScreen(hwnd) && IsMouseAtEdgeOfWindow(hwnd)){
		; Get Window Class and EXE
		WinGet, ProcessName, ProcessName, % "ahk_id " hwnd
		WinGetClass, cls, % "ahk_id " hwnd
		
		; Get list of windows matching the Class and EXE
		windows := []
		WinGet, l, list, % "ahk_exe " ProcessName " ahk_class " cls
		Loop, %l%
		{
			w := l%A_Index%
			;if (IsWindowFullScreen(w) && IsMouseAtEdgeOfWindow(w)){
				windows.push(w)
			;}
		}
		windows := ArraySort(windows)
		if (windows.length() > 1){
			new_window := GetNextWindow(hwnd, windows, dir)
			if (new_window)
				WinActivate, % "ahk_id " new_window
		}

	} else {
		; Pass through wheel
		SendWheel(dir)
	}
}

SendWheel(dir){
	global WheelLookup
	Send % "{Wheel" WheelLookup[dir] "}"
}

GetNextWindow(hwnd, windows, dir){
	; Find the current window in the list
	this_index := 0
	max := windows.length()
	Loop % max {
		if (windows[A_Index] == hwnd){
			this_index := A_Index
			break
		}
	}
	if (!this_index)
		return 0
	new_index := this_index + dir
	if (new_index < 1)
		new_index := max
	if (new_index > max)
		new_index := 1
	return windows[new_index]
}

IsWindowFullScreen( hwnd ) {
	;checks if the specified window is full screen
	If ( !WinExist("ahk_id " hwnd) )
		Return false

	WinGet style, Style, % "ahk_id " hwnd
	WinGetPos ,,,winW,winH, % "ahk_id " hwnd
	; 0x800000 is WS_BORDER.
	; 0x20000000 is WS_MINIMIZE.
	; no border and not minimized
	Return (style & 0x20800000 || winH < A_ScreenHeight || winW < A_ScreenWidth) ? false : true
}

IsMouseAtEdgeOfWindow(hwnd){
	MouseGetPos, mx, my
	WinGetPos, wx, wy, ww, wh, % "ahk_id " hwnd
	;tooltip % "hwnd: " hwnd ", mx: " mx ", wx: " wx ", ww: " ww
	if ((mx >=0 && mx < 10) || (mx >= ww - 10 && mx <= ww)){
		return 1
	}
	return 0
}

ArraySort(array){
	for k, v in array {
		sorted .= v "`n"
	}
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		out.push(A_LoopField)
	}
	return out
}
Last edited by evilC on 09 Feb 2016, 12:08, edited 2 times in total.
Danielsan73
Posts: 16
Joined: 07 Nov 2014, 10:20

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

03 Dec 2015, 04:49

Sorry but not work for me on windows 8.1
i try to run it as administrator... nothing happen when roll weel mouse on edges

or i don't know how to active... What is "w" key bind?
bye.
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

03 Dec 2015, 05:08

W is not a wheel bind, w is the class instance.
What kind of window are you trying to switch? The sample code only works with RDP client windows.

If you are trying to use it with another kind of window, you need to change the window title in the line:

w := new WheelSwitcher("ahk_class TscShellContainerClass")

For example, to work with notepad windows, change it to:

w := new WheelSwitcher("ahk_class Notepad")
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

09 Feb 2016, 12:13

Updated OP:

Now works with any application without the need to edit code - if you roll the mouse wheel within 10px of the edge of any full-screen app, it searches for other full-screen apps with the same exe / class and cycles between those.

Order is now preserved: Each time the wheel is rolled, if finds all matching windows, then sorts them by HWND. This means that windows will always have the same order, resulting in more consistent behavior (eg rolling wheel up from one window always goes to the same window)
gallaxhar
Posts: 143
Joined: 03 Sep 2014, 06:35

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

11 May 2016, 04:01

This sounds really cool but it didn't work for me: Windows 10
ran as admin
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

11 May 2016, 04:55

This script relies on a quirk that I found with fullscreen RDP - namely that the mouse wheel does not seem to be "trapped" by the RDP window (Whereas other keys seem to be).

Try this AHK script and see if you can hear a beep while an RDP window is fullscreen and you roll the mouse wheel up.

Code: Select all

~WheelUp::
   soundbeep
   return
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

17 May 2016, 11:36

Here is the script I am currently using:

Code: Select all

#singleinstance force
CoordMode, Mouse, Screen
;~ Gui, Add, Edit, w500 h400 hwndhText
;~ Gui, Show, x0 y0

return

~*WheelUp::
	DoSwitch(1)
	return
	
~*WheelDown::
	DoSwitch(-1)
	return
	
GuiClose:
	ExitApp

DoSwitch(dir){
	global hText
	hwnd := WinExist("A")	
	;~ str := "hwnd: " hwnd "`n"
	;~ str .= "IsWindowFullScreen: " IsWindowFullScreen(hwnd) "`n"
	;~ str .= "`nMatching Windows: `n"
	if (GetKeyState("LALT", "P")){
		alternate_mode := 1
	} else {
		alternate_mode := 0
	}
	; Only proceeed if alternate mode is on or the mouse is at edge of screen
	if ( !(alternate_mode || IsMouseAtEdgeOfMonitor()) )
		return
	windows := GetMatchingWindows(hwnd, alternate_mode)
	windows := FilterWindowsOnThisMonitor(windows)
	windows := ArraySort(windows)
	;~ Loop % windows.length(){
		;~ WinGetTitle, title, % "ahk_id " windows[A_Index]
		;~ WinGetPos, x, y, w, h, % "ahk_id " windows[A_Index]
		;~ str .= windows[A_Index] " - " title " (x: " x ",y: " y ")`n"
	;~ }
	new_window := GetNextWindow(hwnd, windows, dir)
	;~ WinGetTitle, title, % "ahk_id " new_window
	;~ str .= "`nSwitching to: " new_window " (" title ")`n"
	WinActivate, % "ahk_id " new_window
	;~ GuiControl, , % hText, % str
}

; Returns true if the specified HWND is either fullscreen or maximized
IsWindowFullScreen( hwnd ) {
	;checks if the specified window is full screen
	If ( !WinExist("ahk_id " hwnd) )
		Return false

	WinGet style, Style, % "ahk_id " hwnd
	
	if (style & 0x01000000)
		return 1
	
	WinGetPos ,,,winW,winH, % "ahk_id " hwnd
	; 0x800000 is WS_BORDER.
	; 0x20000000 is WS_MINIMIZE.
	; no border and not minimized
	Return (style & 0x20800000 || winH < A_ScreenHeight || winW < A_ScreenWidth) ? false : true
}

; Returns an array of HWNDs that "Match" the current window
; If alternate_mode is off, will only return windows of the same process name...
GetMatchingWindows(hwnd, alternate_mode := 0){
	if (alternate_mode){
		windows := []
		WinGet, l, list
		debug := 1
	} else {
		; Get Window Class and EXE
		WinGet, ProcessName, ProcessName, % "ahk_id " hwnd
		WinGetClass, cls, % "ahk_id " hwnd
		
		; Get list of windows matching the Class and EXE
		windows := []
		WinGet, l, list, % "ahk_exe " ProcessName " ahk_class " cls
	}
	Loop, %l%
	{
		w := l%A_Index%
		;if (alternate_mode || !alternate_mode && IsMouseAtEdgeOfMonitor()){
			windows.push(w)
		;}
	}
	return windows
}

; Filters a window array for only items on the same monitor as the mouse
FilterWindowsOnThisMonitor(windows){
	ExcludeList := {"Program Manager": 1, "BBar": 1, "Sticky Notes": 1}
	current_mon := WhichMonitorIsMouseOn()
	;~ if (!current_mon)
		;~ return
	_windows := []
	Loop % windows.length(){
		WinGetTitle, title, % "ahk_id " windows[A_Index]
		if (title && !ObjHasKey(ExcludeList, title) && IsWindowOnMonitor(windows[A_Index], current_mon)){
		;WinGetPos, x, y, w, h, % "ahk_id " windows[A_Index]
		;if (x >= monAreaLeft && x <= monAreaRight && y >= monAreaTop && y <= monAreaBottom){
			_windows.push(windows[A_Index])
		}
	}
	return _windows
}

; Decides if an individual window is on the same monitor as the mouse
IsWindowOnMonitor(hwnd, monitor){
	WinGetPos, x, y, w, h, % "ahk_id " hwnd
	mx := x + (w/2), my := y + (h/2)
	SysGet, monArea, Monitor, % monitor
	if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
		return 1
	}
	return 0
}

; Finds the window after the current one in a windows array
GetNextWindow(hwnd, windows, dir){
	; Find the current window in the list
	this_index := 0
	max := windows.length()
	Loop % max {
		if (windows[A_Index] == hwnd){
			this_index := A_Index
			break
		}
	}
	if (!this_index)
		return 0
	found := 0
	ct := 0
	new_index := this_index
	while (!found && (ct <= max)){
		ct++
		new_index += dir
		if (new_index < 1)
			new_index := max
		if (new_index > max)
			new_index := 1
		found := 1
	}
	return windows[new_index]
}

; Sorts a windows array by HWND
ArraySort(array){
	for k, v in array {
		sorted .= v "`n"
	}
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		out.push(A_LoopField)
	}
	return out
}

; Is the mouse at the edge of the monitor?
IsMouseAtEdgeOfMonitor(){
	MouseGetPos, mx, my
	mon := WhichMonitorIsMouseOn()
	SysGet, monArea, Monitor, % mon
	if ( (mx >= monAreaLeft && mx <= monAreaLeft + 10) || (mx <= monAreaRight && mx >= monAreaRight - 10) || (my >= monAreaTop && my <= monAreaTop + 10) || (my <= monAreaBottom && my >= monAreaBottom - 10))
		return 1
}

; Does the window encompass the mouse cursor?
DoesWindowEncompassMouse( hwnd ) {
	WinGetPos ,winX,winY,winW,winH, % "ahk_id " hwnd
	MouseGetPos, mX, mY
	return ( (winX >= mX <= (winX + winW)) && (winY >= mY <= (winY + winH)) )
}

; Which monitor is the mouse on?
WhichMonitorIsMouseOn(){
	MouseGetPos, mx, my
	SysGet, mc, MonitorCount
	Loop % mc {
		SysGet, monArea, Monitor, % A_Index
		if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
			return A_Index
		}
	}
	return 0
}
The logic is still basically the same for FullScreen apps (But now works with maximized windows too).
I added a new feature - ALT+Wheel now cycles through all applications on that monitor (excluding minimized windows, but I am working on allowing that)
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

18 May 2016, 11:32

I have also worked out how to make Fullscreen RDP windows show something more descriptive in the "BBar" (The blue bar at the top of the window).

Pass this function the HWND of a window. If it is an RDP window, a GUI will be parented to the BBar with something more descriptive.

If you launched RDP from a shortcut, it will show the name of the shortcut.
If launched through other means, it will just show the title of the window.

Code: Select all

; If the window is an RDP window, add a label to the BBar
LabelRDP(hwnd){
	static LabelledWindows := {}
	WinGetClass, cls, % "ahk_id " hwnd
	if (cls != "TscShellContainerClass" || ObjHasKey(LabelledWindows, hwnd)){
		return
	}
	DetectHiddenWindows, On
	WinGetTitle, title, % "ahk_id " hwnd
	t := StrSplit(title, " - ")
	if (t[3] == "Remote Desktop Connection"){
		title := t[1]
	}
	
	len := StrLen(title)
	if (len < 20){
		pad := ""
		Loop % (20 - len) / 2 {
			pad .= " "
		}
		title := pad title pad
	}

	WinGet, pid, pid, ahk_id %hwnd%
	WinGet, id, list, % "ahk_pid " pid
	bbar := 0
	Loop, %id% {
		this_id := id%A_Index%
		WinGetTitle, btitle, % "ahk_id " this_id
		if (btitle == "BBar"){
			bbar := this_id
			break
		}
	}
	if (!bbar)
		return
	WinGetPos, , , bw, bh, % "ahk_id " bbar
	Gui, New, HwndhOverlay
	Gui, -Caption
	Gui, Add, Text, Center, % title
	Gui, Show
	WinGetPos, , , lw, lh, % "ahk_id " hOverlay
	Gui, % hOverlay ":+Parent" bbar
	Gui, Show, % "x" (bw / 2) - (lw / 2) " y0"
	LabelledWindows[hwnd] := 1
	OutputDebug % "Adding Label for RDP window " hwnd " of " title
}
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

20 Jul 2016, 11:05

I just tested this on Win10 and it worked fine when I ran as admin.

If it doesn't work, try adding msgbox % dir at the start of DoSwitch().
If you do not see a msgbox pop up when you roll the wheel while an RDP session is fullscreen, then the script is simply not seeing the wheel roll.
User avatar
joedf
Posts: 7075
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

20 Jul 2016, 12:43

Hmmm, interesting concept... :think: :)
I've never thought about App-switching by scrolling in corners... :o
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 2133 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

20 Jul 2016, 12:58

There's nothing stopping it working no matter where the mouse is - however in order to do this with RDP, I think you have to leave the hotkey bound to Wheel up/down, and use a GetKeyState check inside the hotkey label to detect the state of modifiers.
It's not just corners either - within 10px of any edge will trigger the switch.
User avatar
joedf
Posts: 7075
Joined: 29 Sep 2013, 17:08
Facebook: J0EDF
Google: +joedf
GitHub: joedf
Location: Canada
Contact:

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

20 Jul 2016, 13:11

Ahhh, I see. okay :)
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500 @ 3.00 GHz, 16GB DDR4 2133 MHz, NVIDIA GTX 1060 6GB | [About Me] | [ASPDM - StdLib Distribution]
[Populate the AHK MiniCity!] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library] | [About the AHK Foundation]
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

11 Aug 2016, 10:18

My current version
Now uses RunAsTask to auto-elevate
Now cycles through windows in title order instead of HWND order

Code: Select all

#singleinstance force
OutputDebug DBGVIEWCLEAR
RunAsTask()
CoordMode, Mouse, Screen
;~ Gui, Add, Edit, w500 h400 hwndhText
;~ Gui, Show, x0 y0
 
return
 
~*WheelUp::
	DoSwitch(1)
	return
 
~*WheelDown::
	DoSwitch(-1)
	return
 
GuiClose:
	ExitApp
 
DoSwitch(dir){
	global hText
	hwnd := WinExist("A")	
	;~ str := "hwnd: " hwnd "`n"
	;~ str .= "IsWindowFullScreen: " IsWindowFullScreen(hwnd) "`n"
	;~ str .= "`nMatching Windows: `n"
	if (GetKeyState("LALT", "P")){
		alternate_mode := 1
	} else {
		alternate_mode := 0
	}
	; Only proceeed if alternate mode is on or the mouse is at edge of screen
	if ( !(alternate_mode || IsMouseAtEdgeOfMonitor()) )
		return
	windows := GetMatchingWindows(hwnd, alternate_mode)
	windows := FilterWindowsOnThisMonitor(windows)
	;windows := SortWindowsByHwnd(windows)
	windows := SortWindowsByTitle(windows)
	;~ Loop % windows.length(){
		;~ WinGetTitle, title, % "ahk_id " windows[A_Index]
		;~ WinGetPos, x, y, w, h, % "ahk_id " windows[A_Index]
		;~ str .= windows[A_Index] " - " title " (x: " x ",y: " y ")`n"
	;~ }
	new_window := GetNextWindow(hwnd, windows, dir)
	WinGetTitle, title, % "ahk_id " new_window
	str .= "Switching to: " new_window " (" title ")`n"
	OutputDebug % "AHK| " str
	WinActivate, % "ahk_id " new_window
	;~ GuiControl, , % hText, % str
}
 
; Returns true if the specified HWND is either fullscreen or maximized
IsWindowFullScreen( hwnd ) {
	;checks if the specified window is full screen
	If ( !WinExist("ahk_id " hwnd) )
		Return false
 
	WinGet style, Style, % "ahk_id " hwnd
 
	if (style & 0x01000000)
		return 1
 
	WinGetPos ,,,winW,winH, % "ahk_id " hwnd
	; 0x800000 is WS_BORDER.
	; 0x20000000 is WS_MINIMIZE.
	; no border and not minimized
	Return (style & 0x20800000 || winH < A_ScreenHeight || winW < A_ScreenWidth) ? false : true
}
 
; Returns an array of HWNDs that "Match" the current window
; If alternate_mode is off, will only return windows of the same process name...
GetMatchingWindows(hwnd, alternate_mode := 0){
	if (alternate_mode){
		windows := []
		WinGet, l, list
		debug := 1
	} else {
		; Get Window Class and EXE
		WinGet, ProcessName, ProcessName, % "ahk_id " hwnd
		WinGetClass, cls, % "ahk_id " hwnd
 
		; Get list of windows matching the Class and EXE
		windows := []
		WinGet, l, list, % "ahk_exe " ProcessName " ahk_class " cls
	}
	Loop, %l%
	{
		w := l%A_Index%
		;if (alternate_mode || !alternate_mode && IsMouseAtEdgeOfMonitor()){
			windows.push(w)
		;}
	}
	return windows
}
 
; Filters a window array for only items on the same monitor as the mouse
FilterWindowsOnThisMonitor(windows){
	ExcludeList := {"Program Manager": 1, "BBar": 1, "Sticky Notes": 1}
	current_mon := WhichMonitorIsMouseOn()
	;~ if (!current_mon)
		;~ return
	_windows := []
	Loop % windows.length(){
		WinGetTitle, title, % "ahk_id " windows[A_Index]
		if (title && !ObjHasKey(ExcludeList, title) && IsWindowOnMonitor(windows[A_Index], current_mon)){
		;WinGetPos, x, y, w, h, % "ahk_id " windows[A_Index]
		;if (x >= monAreaLeft && x <= monAreaRight && y >= monAreaTop && y <= monAreaBottom){
			_windows.push(windows[A_Index])
		}
	}
	return _windows
}
 
; Decides if an individual window is on the same monitor as the mouse
IsWindowOnMonitor(hwnd, monitor){
	WinGetPos, x, y, w, h, % "ahk_id " hwnd
	mx := x + (w/2), my := y + (h/2)
	SysGet, monArea, Monitor, % monitor
	if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
		return 1
	}
	return 0
}
 
; Finds the window after the current one in a windows array
GetNextWindow(hwnd, windows, dir){
	; Find the current window in the list
	this_index := 0
	max := windows.length()
	Loop % max {
		if (windows[A_Index] == hwnd){
			this_index := A_Index
			break
		}
	}
	if (!this_index)
		return 0
	found := 0
	ct := 0
	new_index := this_index
	while (!found && (ct <= max)){
		ct++
		new_index += dir
		if (new_index < 1)
			new_index := max
		if (new_index > max)
			new_index := 1
		if windows[new_index] is number {
			;OutputDebug % "AHK| Found Window #" windows[new_index]
			found := 1
		}
	}
	if (found)
		return windows[new_index]
	else
		return 0
}
 
; Sorts a windows array by HWND
SortWindowsByHwnd(array){
	for k, v in array {
		sorted .= v "`n"
	}
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		out.push(A_LoopField)
	}
	return out
}

; Sorts a windows array by Window Title
SortWindowsByTitle(array){
	for k, v in array {
		WinGetTitle, title, % "ahk_id " v
		str := title "|ws|" v "`n"
		;OutputDebug % "AHK| SortWindowsByTitle: " str
		sorted .= str
	}
	
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		hwnd := StrSplit(A_LoopField, "|ws|")
		hwnd := hwnd[2]
		out.push(hwnd)
	}
	return out
}
 
; Is the mouse at the edge of the monitor?
IsMouseAtEdgeOfMonitor(){
	MouseGetPos, mx, my
	mon := WhichMonitorIsMouseOn()
	SysGet, monArea, Monitor, % mon
	if ( (mx >= monAreaLeft && mx <= monAreaLeft + 10) || (mx <= monAreaRight && mx >= monAreaRight - 10) || (my >= monAreaTop && my <= monAreaTop + 10) || (my <= monAreaBottom && my >= monAreaBottom - 10))
		return 1
}
 
; Does the window encompass the mouse cursor?
DoesWindowEncompassMouse( hwnd ) {
	WinGetPos ,winX,winY,winW,winH, % "ahk_id " hwnd
	MouseGetPos, mX, mY
	return ( (winX >= mX <= (winX + winW)) && (winY >= mY <= (winY + winH)) )
}
 
; Which monitor is the mouse on?
WhichMonitorIsMouseOn(){
	MouseGetPos, mx, my
	SysGet, mc, MonitorCount
	Loop % mc {
		SysGet, monArea, Monitor, % A_Index
		if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
			return A_Index
		}
	}
	return 0
}

/*
 _      _    _               __ __      _      _                      _         _                        
| |__  | |_ | |_  _ __  _   / // /__ _ | |__  | | __ ___   ___  _ __ (_) _ __  | |_     ___   _ __  __ _ 
| '_ \ | __|| __|| '_ \(_) / // // _` || '_ \ | |/ // __| / __|| '__|| || '_ \ | __|   / _ \ | '__|/ _` |
| | | || |_ | |_ | |_) |_ / // /| (_| || | | ||   < \__ \| (__ | |   | || |_) || |_  _| (_) || |  | (_| |
|_| |_| \__| \__|| .__/(_)_//_/  \__,_||_| |_||_|\_\|___/ \___||_|   |_|| .__/  \__|(_)\___/ |_|   \__, |
                 |_|                                                    |_|                        |___/ 
RunAsTask() - Auto-elevates script without UAC prompt |  http://ahkscript.org/boards/viewtopic.php?t=4334        
_________________________________________________________________________________________________________
*/
 
RunAsTask() {                         ;  By SKAN,  http://goo.gl/yG6A1F,  CD:19/Aug/2014 | MD:22/Aug/2014
 
  Local CmdLine, TaskName, TaskExists, XML, TaskSchd, TaskRoot, RunAsTask
  Local TASK_CREATE := 0x2,  TASK_LOGON_INTERACTIVE_TOKEN := 3 
 
  Try TaskSchd  := ComObjCreate( "Schedule.Service" ),    TaskSchd.Connect()
    , TaskRoot  := TaskSchd.GetFolder( "\" )
  Catch
      Return "", ErrorLevel := 1    
 
  CmdLine       := ( A_IsCompiled ? "" : """"  A_AhkPath """" )  A_Space  ( """" A_ScriptFullpath """"  )
  TaskName      := "[RunAsTask] " A_ScriptName " @" SubStr( "000000000"  DllCall( "NTDLL\RtlComputeCrc32"
                   , "Int",0, "WStr",CmdLine, "UInt",StrLen( CmdLine ) * 2, "UInt" ), -9 )
 
  Try RunAsTask := TaskRoot.GetTask( TaskName )
  TaskExists    := ! A_LastError 
 
 
  If ( not A_IsAdmin and TaskExists )      { 
 
    RunAsTask.Run( "" )
    ExitApp
 
  }
 
  If ( not A_IsAdmin and not TaskExists )  { 
 
    Run *RunAs %CmdLine%, %A_ScriptDir%, UseErrorLevel
    ExitApp
 
  }
 
  If ( A_IsAdmin and not TaskExists )      {  
 
    XML := "
    ( LTrim Join
      <?xml version=""1.0"" ?><Task xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task""><Regi
      strationInfo /><Triggers /><Principals><Principal id=""Author""><LogonType>InteractiveToken</LogonT
      ype><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolic
      y>Parallel</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><
      StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate>
      <StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAva
      ilable><IdleSettings><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleS
      ettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><
      RunOnlyIfIdle>false</RunOnlyIfIdle><DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteApp
      Session><UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun><
      ExecutionTimeLimit>PT0S</ExecutionTimeLimit></Settings><Actions Context=""Author""><Exec>
      <Command>"   (  A_IsCompiled ? A_ScriptFullpath : A_AhkPath )       "</Command>
      <Arguments>" ( !A_IsCompiled ? """" A_ScriptFullpath  """" : "" )   "</Arguments>
      <WorkingDirectory>" A_ScriptDir "</WorkingDirectory></Exec></Actions></Task>
    )"    
 
    TaskRoot.RegisterTask( TaskName, XML, TASK_CREATE, "", "", TASK_LOGON_INTERACTIVE_TOKEN )
 
  }         
 
Return TaskName, ErrorLevel := 0
} ; _____________________________________________________________________________________________________
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

09 Sep 2016, 11:40

New version:
This one does away with lots of the checks, and will cycle between any windows which have the same coordinates.

The same rule applies though - it will only cycle windows when the mouse is within 10px of a monitor edge.

This one works great in combination with tiling window managers (I currently use Mosaico) that place windows in the exact same location - so if you have numerous windows stacked perfectly on top of each other in a tile, you can cycle between them with the mouse wheel.
Still works great with fullscreen RDP windows.

Code: Select all

#SingleInstance force
CoordMode, Mouse, Screen
RunAsTask()

~WheelUp::
	DoWheel(1)
	return

~WheelDown::
	DoWheel(-1)
	return

DoWheel(dir){
	if (!IsMouseAtEdgeOfMonitor()){
		return
	}
	MouseGetPos, mx, my, active_hwnd
	;msgbox % "x" ax " y" ay " w" aw " h" ah
	;WinGet, l, list, % "ahk_exe " ProcessName " ahk_class " cls
	windows := []
	WinGet, l, list
	;WinGet, l, list, % "ahk_exe notepad.exe"
	Loop, %l%
	{
		w := l%A_Index%
		;if (alternate_mode || !alternate_mode && IsMouseAtEdgeOfMonitor()){
			windows.push(w)
		;}
	}
	windows := FilterWindows(active_hwnd, windows)
	w := GetNextWindow(active_hwnd, windows, dir)
	if (w){
		WinActivate, % "ahk_id " w
	}
}

IsMouseAtEdgeOfMonitor(){
	MouseGetPos, mx, my
	mon := WhichMonitorIsMouseOn()
	SysGet, monArea, Monitor, % mon
	if ( (mx >= monAreaLeft && mx <= monAreaLeft + 10) || (mx <= monAreaRight && mx >= monAreaRight - 10) || (my >= monAreaTop && my <= monAreaTop + 10) || (my <= monAreaBottom && my >= monAreaBottom - 10))
		return 1
}

; Which monitor is the mouse on?
WhichMonitorIsMouseOn(){
	MouseGetPos, mx, my
	SysGet, mc, MonitorCount
	Loop % mc {
		SysGet, monArea, Monitor, % A_Index
		if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
			return A_Index
		}
	}
	return 0
}

FilterWindows(active_hwnd, windows){
	WinGetPos, ax, ay, aw, ah, % "ahk_id " active_hwnd
	w := []
	for i, hwnd in windows {
		WinGetPos, wx, wy, ww, wh, % "ahk_id " hwnd
		if (wx == ax && wy == ay && ww == aw && wh == ah){
			w.push(hwnd)
		}
	}
	return SortWindowsByHwnd(w)
}

; Sorts a windows array by HWND
SortWindowsByHwnd(array){
	for k, v in array {
		sorted .= v "`n"
	}
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		out.push(A_LoopField)
	}
	return out
}

; Finds the window after the current one in a windows array
GetNextWindow(hwnd, windows, dir){
	; Find the current window in the list
	this_index := 0
	max := windows.length()
	Loop % max {
		if (windows[A_Index] == hwnd){
			this_index := A_Index
			break
		}
	}
	if (!this_index)
		return 0
	found := 0
	ct := 0
	new_index := this_index
	while (!found && (ct <= max)){
		ct++
		new_index += dir
		if (new_index < 1)
			new_index := max
		if (new_index > max)
			new_index := 1
		if windows[new_index] is number {
			;OutputDebug % "AHK| Found Window #" windows[new_index]
			found := 1
		}
	}
	if (found)
		return windows[new_index]
	else
		return 0
}

/*
 _      _    _               __ __      _      _                      _         _                        
| |__  | |_ | |_  _ __  _   / // /__ _ | |__  | | __ ___   ___  _ __ (_) _ __  | |_     ___   _ __  __ _ 
| '_ \ | __|| __|| '_ \(_) / // // _` || '_ \ | |/ // __| / __|| '__|| || '_ \ | __|   / _ \ | '__|/ _` |
| | | || |_ | |_ | |_) |_ / // /| (_| || | | ||   < \__ \| (__ | |   | || |_) || |_  _| (_) || |  | (_| |
|_| |_| \__| \__|| .__/(_)_//_/  \__,_||_| |_||_|\_\|___/ \___||_|   |_|| .__/  \__|(_)\___/ |_|   \__, |
                 |_|                                                    |_|                        |___/ 
RunAsTask() - Auto-elevates script without UAC prompt |  http://ahkscript.org/boards/viewtopic.php?t=4334        
_________________________________________________________________________________________________________
*/
 
RunAsTask() {                         ;  By SKAN,  http://goo.gl/yG6A1F,  CD:19/Aug/2014 | MD:22/Aug/2014
 
  Local CmdLine, TaskName, TaskExists, XML, TaskSchd, TaskRoot, RunAsTask
  Local TASK_CREATE := 0x2,  TASK_LOGON_INTERACTIVE_TOKEN := 3 
 
  Try TaskSchd  := ComObjCreate( "Schedule.Service" ),    TaskSchd.Connect()
    , TaskRoot  := TaskSchd.GetFolder( "\" )
  Catch
      Return "", ErrorLevel := 1    
 
  CmdLine       := ( A_IsCompiled ? "" : """"  A_AhkPath """" )  A_Space  ( """" A_ScriptFullpath """"  )
  TaskName      := "[RunAsTask] " A_ScriptName " @" SubStr( "000000000"  DllCall( "NTDLL\RtlComputeCrc32"
                   , "Int",0, "WStr",CmdLine, "UInt",StrLen( CmdLine ) * 2, "UInt" ), -9 )
 
  Try RunAsTask := TaskRoot.GetTask( TaskName )
  TaskExists    := ! A_LastError 
 
 
  If ( not A_IsAdmin and TaskExists )      { 
 
    RunAsTask.Run( "" )
    ExitApp
 
  }
 
  If ( not A_IsAdmin and not TaskExists )  { 
 
    Run *RunAs %CmdLine%, %A_ScriptDir%, UseErrorLevel
    ExitApp
 
  }
 
  If ( A_IsAdmin and not TaskExists )      {  
 
    XML := "
    ( LTrim Join
      <?xml version=""1.0"" ?><Task xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task""><Regi
      strationInfo /><Triggers /><Principals><Principal id=""Author""><LogonType>InteractiveToken</LogonT
      ype><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolic
      y>Parallel</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><
      StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate>
      <StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAva
      ilable><IdleSettings><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleS
      ettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><
      RunOnlyIfIdle>false</RunOnlyIfIdle><DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteApp
      Session><UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun><
      ExecutionTimeLimit>PT0S</ExecutionTimeLimit></Settings><Actions Context=""Author""><Exec>
      <Command>"   (  A_IsCompiled ? A_ScriptFullpath : A_AhkPath )       "</Command>
      <Arguments>" ( !A_IsCompiled ? """" A_ScriptFullpath  """" : "" )   "</Arguments>
      <WorkingDirectory>" A_ScriptDir "</WorkingDirectory></Exec></Actions></Task>
    )"    
 
    TaskRoot.RegisterTask( TaskName, XML, TASK_CREATE, "", "", TASK_LOGON_INTERACTIVE_TOKEN )
 
  }         
 
Return TaskName, ErrorLevel := 0
} ; _____________________________________________________________________________________________________
User avatar
evilC
Posts: 4697
Joined: 27 Feb 2014, 12:30

Re: WheelSwitcher - Switch Fullscreen apps (eg RDP client) by rolling mouse wheel at edge of screen

12 Sep 2016, 08:17

Further improvement:

On Win10 systems, window size can vary for apparently identically tiled windows - for some windows it thinks they are ~7px out.
This new version allows a difference of 7px for x and y position, and 14px for width and height.

Code: Select all

#SingleInstance force
OutputDebug DBGVIEWCLEAR

CoordMode, Mouse, Screen
RunAsTask()

~WheelUp::
	DoWheel(1)
	return

~WheelDown::
	DoWheel(-1)
	return

DoWheel(dir){
	if (!IsMouseAtEdgeOfMonitor()){
		return
	}
	MouseGetPos, mx, my, active_hwnd
	;msgbox % "x" ax " y" ay " w" aw " h" ah
	;WinGet, l, list, % "ahk_exe " ProcessName " ahk_class " cls
	windows := []
	WinGet, l, list
	;WinGet, l, list, % "ahk_exe notepad.exe"
	Loop, %l%
	{
		w := l%A_Index%
		;if (alternate_mode || !alternate_mode && IsMouseAtEdgeOfMonitor()){
			windows.push(w)
		;}
	}
	windows := FilterWindows(active_hwnd, windows)
	w := GetNextWindow(active_hwnd, windows, dir)
	if (w){
		WinActivate, % "ahk_id " w
	}
}

IsMouseAtEdgeOfMonitor(){
	MouseGetPos, mx, my
	mon := WhichMonitorIsMouseOn()
	SysGet, monArea, Monitor, % mon
	if ( (mx >= monAreaLeft && mx <= monAreaLeft + 10) || (mx <= monAreaRight && mx >= monAreaRight - 10) || (my >= monAreaTop && my <= monAreaTop + 10) || (my <= monAreaBottom && my >= monAreaBottom - 10))
		return 1
}

; Which monitor is the mouse on?
WhichMonitorIsMouseOn(){
	MouseGetPos, mx, my
	SysGet, mc, MonitorCount
	Loop % mc {
		SysGet, monArea, Monitor, % A_Index
		if (mx >= monAreaLeft && mx <= monAreaRight && my >= monAreaTop && my <= monAreaBottom){
			return A_Index
		}
	}
	return 0
}

FilterWindows(active_hwnd, windows){
	static window_diff := 7
	WinGetPos, ax, ay, aw, ah, % "ahk_id " active_hwnd
	;OutputDebug % "AHK| Checking windows against ax: " ax ", ay: " ay ", ah: " ah ", aw: " aw ")"
	w := []
	for i, hwnd in windows {
		WinGetPos, wx, wy, ww, wh, % "ahk_id " hwnd
		WinGetTitle, title, % "ahk_id " hwnd
		;if (wx == ax && wy == ay && ww == aw && wh == ah){
		if (abs(wx-ax) <= window_diff && abs(wy-ay) <= window_diff && abs(ww - aw) <= (window_diff * 2) && abs(wh - ah) <= (window_diff * 2)){
			w.push(hwnd)
			;OutputDebug % "AHK| Adding window: " title
		} else {
			;OutputDebug % "AHK| Filtering out window: " title " ( wx: " wx ", wy:" wy ", wh: " wh ", ww: " ww 
		}
	}
	;return SortWindowsByHwnd(w)
	return SortWindowsByTitle(w)
}

; Sorts a windows array by Window Title
SortWindowsByTitle(array){
	for k, v in array {
		WinGetTitle, title, % "ahk_id " v
		str := title "|ws|" v "`n"
		;OutputDebug % "AHK| SortWindowsByTitle: " str
		sorted .= str
	}
	
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		hwnd := StrSplit(A_LoopField, "|ws|")
		hwnd := hwnd[2]
		out.push(hwnd)
	}
	return out
}

; Sorts a windows array by HWND
SortWindowsByHwnd(array){
	for k, v in array {
		sorted .= v "`n"
	}
	StringTrimRight,sorted,sorted,1
	Sort,sorted, D
	out := []
	Loop, Parse, sorted, `n
	{
		out.push(A_LoopField)
	}
	return out
}

; Finds the window after the current one in a windows array
GetNextWindow(hwnd, windows, dir){
	; Find the current window in the list
	this_index := 0
	max := windows.length()
	Loop % max {
		if (windows[A_Index] == hwnd){
			this_index := A_Index
			break
		}
	}
	if (!this_index)
		return 0
	found := 0
	ct := 0
	new_index := this_index
	while (!found && (ct <= max)){
		ct++
		new_index += dir
		if (new_index < 1)
			new_index := max
		if (new_index > max)
			new_index := 1
		if windows[new_index] is number {
			;OutputDebug % "AHK| Found Window #" windows[new_index]
			found := 1
		}
	}
	if (found)
		return windows[new_index]
	else
		return 0
}

/*
 _      _    _               __ __      _      _                      _         _                        
| |__  | |_ | |_  _ __  _   / // /__ _ | |__  | | __ ___   ___  _ __ (_) _ __  | |_     ___   _ __  __ _ 
| '_ \ | __|| __|| '_ \(_) / // // _` || '_ \ | |/ // __| / __|| '__|| || '_ \ | __|   / _ \ | '__|/ _` |
| | | || |_ | |_ | |_) |_ / // /| (_| || | | ||   < \__ \| (__ | |   | || |_) || |_  _| (_) || |  | (_| |
|_| |_| \__| \__|| .__/(_)_//_/  \__,_||_| |_||_|\_\|___/ \___||_|   |_|| .__/  \__|(_)\___/ |_|   \__, |
                 |_|                                                    |_|                        |___/ 
RunAsTask() - Auto-elevates script without UAC prompt |  http://ahkscript.org/boards/viewtopic.php?t=4334        
_________________________________________________________________________________________________________
*/
 
RunAsTask() {                         ;  By SKAN,  http://goo.gl/yG6A1F,  CD:19/Aug/2014 | MD:22/Aug/2014
 
  Local CmdLine, TaskName, TaskExists, XML, TaskSchd, TaskRoot, RunAsTask
  Local TASK_CREATE := 0x2,  TASK_LOGON_INTERACTIVE_TOKEN := 3 
 
  Try TaskSchd  := ComObjCreate( "Schedule.Service" ),    TaskSchd.Connect()
    , TaskRoot  := TaskSchd.GetFolder( "\" )
  Catch
      Return "", ErrorLevel := 1    
 
  CmdLine       := ( A_IsCompiled ? "" : """"  A_AhkPath """" )  A_Space  ( """" A_ScriptFullpath """"  )
  TaskName      := "[RunAsTask] " A_ScriptName " @" SubStr( "000000000"  DllCall( "NTDLL\RtlComputeCrc32"
                   , "Int",0, "WStr",CmdLine, "UInt",StrLen( CmdLine ) * 2, "UInt" ), -9 )
 
  Try RunAsTask := TaskRoot.GetTask( TaskName )
  TaskExists    := ! A_LastError 
 
 
  If ( not A_IsAdmin and TaskExists )      { 
 
    RunAsTask.Run( "" )
    ExitApp
 
  }
 
  If ( not A_IsAdmin and not TaskExists )  { 
 
    Run *RunAs %CmdLine%, %A_ScriptDir%, UseErrorLevel
    ExitApp
 
  }
 
  If ( A_IsAdmin and not TaskExists )      {  
 
    XML := "
    ( LTrim Join
      <?xml version=""1.0"" ?><Task xmlns=""http://schemas.microsoft.com/windows/2004/02/mit/task""><Regi
      strationInfo /><Triggers /><Principals><Principal id=""Author""><LogonType>InteractiveToken</LogonT
      ype><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><MultipleInstancesPolic
      y>Parallel</MultipleInstancesPolicy><DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries><
      StopIfGoingOnBatteries>false</StopIfGoingOnBatteries><AllowHardTerminate>false</AllowHardTerminate>
      <StartWhenAvailable>false</StartWhenAvailable><RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAva
      ilable><IdleSettings><StopOnIdleEnd>true</StopOnIdleEnd><RestartOnIdle>false</RestartOnIdle></IdleS
      ettings><AllowStartOnDemand>true</AllowStartOnDemand><Enabled>true</Enabled><Hidden>false</Hidden><
      RunOnlyIfIdle>false</RunOnlyIfIdle><DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteApp
      Session><UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine><WakeToRun>false</WakeToRun><
      ExecutionTimeLimit>PT0S</ExecutionTimeLimit></Settings><Actions Context=""Author""><Exec>
      <Command>"   (  A_IsCompiled ? A_ScriptFullpath : A_AhkPath )       "</Command>
      <Arguments>" ( !A_IsCompiled ? """" A_ScriptFullpath  """" : "" )   "</Arguments>
      <WorkingDirectory>" A_ScriptDir "</WorkingDirectory></Exec></Actions></Task>
    )"    
 
    TaskRoot.RegisterTask( TaskName, XML, TASK_CREATE, "", "", TASK_LOGON_INTERACTIVE_TOKEN )
 
  }         
 
Return TaskName, ErrorLevel := 0
} ; _____________________________________________________________________________________________________

Return to “Scripts and Functions”

Who is online

Users browsing this forum: freespacing, robodesign and 53 guests