Screen rotation in AHK v2 Topic is solved

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
PieterP
Posts: 20
Joined: 10 Feb 2023, 14:41

Screen rotation in AHK v2

Post by PieterP » 07 Jun 2023, 09:22

Hello,

I'm trying to adjust the "display orientation" setting in Windows 11.
Possible values in Settings > Display > Display Orientation are "Landscape", "Portrait", "Landscape (flipped)", "Portrait (flipped)". (0°, 90°, 180°, 270°)

I can't use the system hotkeys (CTRL+ALT+ARROW KEY) because those no longer exist natively in Windows 11.

When searching the forum, I found an interesting hotkey script from user @malcev here:
viewtopic.php?f=76&t=87453&hilit=ChangeDisplaySettingsW

Code: Select all

VarSetCapacity(DEVMODE, 220, 0)
NumPut(220, DEVMODE, 68, "short")   ; dmSize
DllCall("EnumDisplaySettingsW", "ptr", 0, "int", -1, "ptr", &DEVMODE)
width := NumGet(DEVMODE, 172, "uint")
height := NumGet(DEVMODE, 176, "uint")

NumPut(width, DEVMODE, 176, "int")
NumPut(height, DEVMODE, 172, "int")
NumPut(DMDO_90 := 1, DEVMODE, 84, "int")   ; dmDisplayOrientation
DllCall("ChangeDisplaySettingsW", "ptr", &DEVMODE, "uint", 0)
sleep 2000

NumPut(width, DEVMODE, 172, "int")
NumPut(height, DEVMODE, 176, "int")
NumPut(DMDO_180 := 2, DEVMODE, 84, "int")   ; dmDisplayOrientation
DllCall("ChangeDisplaySettingsW", "ptr", &DEVMODE, "uint", 0)
sleep 2000

NumPut(width, DEVMODE, 176, "int")
NumPut(height, DEVMODE, 172, "int")
NumPut(DMDO_270 := 3, DEVMODE, 84, "int")   ; dmDisplayOrientation
DllCall("ChangeDisplaySettingsW", "ptr", &DEVMODE, "uint", 0)
sleep 2000

NumPut(width, DEVMODE, 172, "int")
NumPut(height, DEVMODE, 176, "int")
NumPut(DMDO_DEFAULT := 0, DEVMODE, 84, "int")   ; dmDisplayOrientation
DllCall("ChangeDisplaySettingsW", "ptr", &DEVMODE, "uint", 0)
Sadly enough I haven't been able to convert this script to a v2 syntax.

Can someone please help?

Many thanks in advance!

Pieter

PieterP
Posts: 20
Joined: 10 Feb 2023, 14:41

Re: Screen rotation in AHK v2

Post by PieterP » 07 Jun 2023, 15:16

Quick status update on the code I got so far...

Code: Select all

#Requires AutoHotkey v2.0

Device_Mode := Buffer(220,0)
NumPut "Short", 220, Device_Mode, 68
DllCall( "EnumDisplaySettingsW", "UInt",0, "UInt",-1, "UInt", Device_Mode.Ptr )
Width := NumGet(Device_Mode, 172, "UInt")
Height := NumGet(Device_Mode, 176, "UInt")

ChangeScreenOrientation(Orientation := "Landscape") {

	Switch Orientation
	{
	Case "Landscape":
		NumPut "Int", Width, Device_Mode, 172
		NumPut "Int", Height, Device_Mode, 176
		NumPut "Int", 0, Device_Mode, 84
	Case "Portrait (flipped)":
		NumPut "Int", Width, Device_Mode, 176
		NumPut "Int", Height, Device_Mode, 172
		NumPut "Int", 3, Device_Mode, 84
	Case "Landscape (flipped)":
		NumPut "Int", Width, Device_Mode, 172
		NumPut "Int", Height, Device_Mode, 176
		NumPut "Int", 2, Device_Mode, 84
	Case "Portrait":
		NumPut "Int", Width, Device_Mode, 176
		NumPut "Int", Height, Device_Mode, 172
		NumPut "Int", 1, Device_Mode, 84
	Default:
		;Default = Landscape
		NumPut "Int", Width, Device_Mode, 172
		NumPut "Int", Height, Device_Mode, 176
		NumPut "Int", 0, Device_Mode, 84
	}

	DllCall( "ChangeDisplaySettingsW", "UInt", Device_Mode.Ptr, "UInt", 0 )
}

Msgbox "Portrait (flipped)"
ChangeScreenOrientation("Portrait (flipped)")
Sleep 2000
Msgbox "Landscape (flipped)"
ChangeScreenOrientation("Landscape (flipped)")
Sleep 2000
Msgbox "Portrait"
ChangeScreenOrientation("Portrait")
Sleep 2000
Msgbox "Landscape"
ChangeScreenOrientation("Landscape")
This code works ONLY when I start the script when the display settings are already in orientation Landscape or Landscape (Flipped) beforehand.
If the display settings are in orientation Portrait or Portrait (Flipped) when the script is launched, it doesn't work.

I'm really not good with these API's. :oops:
Can someone help me figure this out?

Grateful as always!

Pieter

ntepa
Posts: 406
Joined: 19 Oct 2022, 20:52

Re: Screen rotation in AHK v2  Topic is solved

Post by ntepa » 07 Jun 2023, 15:57

Try this

Code: Select all

1::ChangeScreenOrientation(0)
2::ChangeScreenOrientation(90)
3::ChangeScreenOrientation(180)
4::ChangeScreenOrientation(270)

ChangeScreenOrientation(Orientation := "Landscape") {
    static DEVMODE, width, height
    if !IsSet(DEVMODE) {
        DEVMODE := Buffer(220, 0)
        NumPut("short", 220, DEVMODE, 68)   ; dmSize
        DllCall("EnumDisplaySettingsW", "ptr", 0, "int", -1, "ptr", DEVMODE)
        n1 := NumGet(DEVMODE, 172, "uint")
        n2 := NumGet(DEVMODE, 176, "uint")
        if n1 > n2
            width := n1, height := n2
        else
            width := n2, height := n1
    }

    switch Orientation, 0 {
        case "Landscape", 0:
            Landscape:
            NumPut("int", width, DEVMODE, 172)
            NumPut("int", height, DEVMODE, 176)
            NumPut("int", DMDO_DEFAULT := 0, DEVMODE, 84)   ; dmDisplayOrientation
            DllCall("ChangeDisplaySettingsW", "ptr", DEVMODE, "uint", 0)
        case "Portrait (flipped)", 270:
            NumPut("int", width, DEVMODE, 176)
            NumPut("int", height, DEVMODE, 172)
            NumPut("int", DMDO_270 := 3, DEVMODE, 84)   ; dmDisplayOrientation
            DllCall("ChangeDisplaySettingsW", "ptr", DEVMODE, "uint", 0)
        case "Landscape (flipped)", 180:
            NumPut("int", width, DEVMODE, 172)
            NumPut("int", height, DEVMODE, 176)
            NumPut("int", DMDO_180 := 2, DEVMODE, 84)   ; dmDisplayOrientation
            DllCall("ChangeDisplaySettingsW", "ptr", DEVMODE, "uint", 0)
        case "Portrait", 90:
            NumPut("int", width, DEVMODE, 176)
            NumPut("int", height, DEVMODE, 172)
            NumPut("int", DMDO_90 := 1, DEVMODE, 84)   ; dmDisplayOrientation
        default:
            goto Landscape
    }
    DllCall("ChangeDisplaySettingsW", "ptr", DEVMODE, "uint", 0)
}

PieterP
Posts: 20
Joined: 10 Feb 2023, 14:41

Re: Screen rotation in AHK v2

Post by PieterP » 07 Jun 2023, 17:33

Wow! That does the trick! Thank you very much! I'm going to analyze this thoroughly tomorrow when my brain has had some sleep!

Thanks and good night!

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Screen rotation in AHK v2

Post by teadrinker » 07 Jun 2023, 20:15

It can be simplified:

Code: Select all

1::ChangeScreenOrientation(0)
2::ChangeScreenOrientation(90)
3::ChangeScreenOrientation(180)
4::ChangeScreenOrientation(270)

ChangeScreenOrientation(Orientation := 'Landscape') {
    static DMDO_DEFAULT := 0, DMDO_90 := 1, DMDO_180 := 2, DMDO_270 := 3
         , DEVMODE := '', dimension1 := 0, dimension2 := 0, dmSize := 220
    if !DEVMODE {
        NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
        DllCall('EnumDisplaySettings', 'Ptr', 0, 'Int', -1, 'Ptr', DEVMODE)
        n0 := NumGet(DEVMODE, 172, 'UInt')
        n1 := NumGet(DEVMODE, 176, 'UInt')
        Loop 2 {
            dimension%A_Index% := n%(n0 > n1) ^ (A_Index = 1)%
                                | n%(n0 < n1) ^ (A_Index = 1)% << 32
        }
    }
    switch Orientation, false {
        case 'Landscape'          ,   0: i := 1, orientation := DMDO_DEFAULT
        case 'Portrait'           ,  90: i := 2, orientation := DMDO_90
        case 'Landscape (flipped)', 180: i := 1, orientation := DMDO_180
        case 'Portrait (flipped)' , 270: i := 2, orientation := DMDO_270
        default:                         i := 1, orientation := DMDO_DEFAULT
    }
    NumPut('Int'  , orientation , DEVMODE,  84)
    NumPut('Int64', dimension%i%, DEVMODE, 172)
    DllCall('ChangeDisplaySettings', 'Ptr', DEVMODE, 'UInt', 0)
}

ntepa
Posts: 406
Joined: 19 Oct 2022, 20:52

Re: Screen rotation in AHK v2

Post by ntepa » 07 Jun 2023, 22:35

teadrinker wrote:
07 Jun 2023, 20:15
It can be simplified:
Impressive! I tried adding support for multi-monitor:

Code: Select all

1::ChangeScreenOrientation(0)
2::ChangeScreenOrientation(90)
3::ChangeScreenOrientation(180)
4::ChangeScreenOrientation(270)

^1::ChangeScreenOrientation(0, 2)
^2::ChangeScreenOrientation(90, 2)
^3::ChangeScreenOrientation(180, 2)
^4::ChangeScreenOrientation(270, 2)

ChangeScreenOrientation(Orientation := 'Landscape', MonNumber:=1) {
    static DMDO_DEFAULT := 0, DMDO_90 := 1, DMDO_180 := 2, DMDO_270 := 3, dmSize := 220
    dimension1 := 0, dimension2 := 0
    NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
    display := '\\.\DISPLAY' MonNumber
    DllCall('EnumDisplaySettings', 'Str', display, 'Int', -1, 'Ptr', DEVMODE)
    n0 := NumGet(DEVMODE, 172, 'UInt')
    n1 := NumGet(DEVMODE, 176, 'UInt')
    Loop 2 {
        dimension%A_Index% := n%(n0 > n1) ^ (A_Index = 1)%
                            | n%(n0 < n1) ^ (A_Index = 1)% << 32
    }
    switch Orientation, false {
        case 'Landscape'          ,   0: i := 1, orientation := DMDO_DEFAULT
        case 'Portrait'           ,  90: i := 2, orientation := DMDO_90
        case 'Landscape (flipped)', 180: i := 1, orientation := DMDO_180
        case 'Portrait (flipped)' , 270: i := 2, orientation := DMDO_270
        default:                         i := 1, orientation := DMDO_DEFAULT
    }
    NumPut('Int'  , orientation , DEVMODE,  84)
    NumPut('Int64', dimension%i%, DEVMODE, 172)
    DllCall('ChangeDisplaySettingsEx', 'Str', display, 'ptr', DEVMODE, 'ptr', 0, 'int', 0, 'int', 0)
}

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Screen rotation in AHK v2

Post by teadrinker » 07 Jun 2023, 23:39

Good. :) Probably instead of

Code: Select all

dimension1 := 0, dimension2 := 0
Loop 2 {
    dimension%A_Index% := n%(n0 > n1) ^ (A_Index = 1)%
                        | n%(n0 < n1) ^ (A_Index = 1)% << 32
}
would be easier

Code: Select all

b := n0 < n1
dimension1 := n% b% | n%!b% << 32
dimension2 := n%!b% | n% b% << 32

ntepa
Posts: 406
Joined: 19 Oct 2022, 20:52

Re: Screen rotation in AHK v2

Post by ntepa » 08 Jun 2023, 00:38

I updated the code and added a case for Clockwise and CounterClockwise:

Code: Select all

^1::ChangeScreenOrientation("Clockwise")
^2::ChangeScreenOrientation("CounterClockwise")

ChangeScreenOrientation(Orientation:='Landscape', MonNumber:=1) {
    static DMDO_DEFAULT := 0, DMDO_90 := 1, DMDO_180 := 2, DMDO_270 := 3, dmSize := 220
    NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
    display := '\\.\DISPLAY' MonNumber
    DllCall('EnumDisplaySettings', 'Str', display, 'Int', -1, 'Ptr', DEVMODE)
    n0 := NumGet(DEVMODE, 172, 'UInt')
    n1 := NumGet(DEVMODE, 176, 'UInt')
    b := n0 < n1
    dimension1 := n% b% | n%!b% << 32
    dimension2 := n%!b% | n% b% << 32
    currentOrientation := NumGet(DEVMODE, 84, 'Int')
    switch Orientation, false {
        case 'Clockwise':
            Orientation := (--currentOrientation) < DMDO_DEFAULT ? DMDO_270 : currentOrientation
            i := (Orientation&1) + 1
        case 'CClockwise', 'CounterClockwise':
            Orientation := (++currentOrientation) > DMDO_270 ? DMDO_DEFAULT : currentOrientation
            i := (Orientation&1) + 1
        case 'Landscape'          ,   0: i := 1, orientation := DMDO_DEFAULT
        case 'Portrait'           ,  90: i := 2, orientation := DMDO_90
        case 'Landscape (flipped)', 180: i := 1, orientation := DMDO_180
        case 'Portrait (flipped)' , 270: i := 2, orientation := DMDO_270
        default:                         i := 1, orientation := DMDO_DEFAULT
    }
    NumPut('Int'  , orientation , DEVMODE,  84)
    NumPut('Int64', dimension%i%, DEVMODE, 172)
    DllCall('ChangeDisplaySettingsEx', 'Str', display, 'Ptr', DEVMODE, 'Ptr', 0, 'Int', 0, 'Int', 0)
}
Last edited by ntepa on 08 Jun 2023, 13:41, edited 1 time in total.

teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: Screen rotation in AHK v2

Post by teadrinker » 08 Jun 2023, 08:38

This line

Code: Select all

dimension1 := 0, dimension2 := 0
is unnecessary.

PieterP
Posts: 20
Joined: 10 Feb 2023, 14:41

Re: Screen rotation in AHK v2

Post by PieterP » 08 Jun 2023, 10:54

I went with something like this

Code: Select all

GetScreenOrientation() {
	static DEVMODE := '', dmSize := 220
        NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
        DllCall('EnumDisplaySettings', 'Ptr', 0, 'Int', -1, 'Ptr', DEVMODE)
	n2 := NumGet(DEVMODE, 84, 'UInt')
	return n2
}

ChangeScreenOrientation(Orientation := 'Landscape', Save := 1 ) {
	static DEVMODE := '', dmSize := 220, DMDO_DEFAULT := 0, DMDO_90 := 1, DMDO_180 := 2, DMDO_270 := 3, CDS_UPDATEREGISTRY := 1, CDS_NONE := 0, dimension1 := 0, dimension2 := 0
	If !DEVMODE {
		NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
		DllCall('EnumDisplaySettings', 'Ptr', 0, 'Int', -1, 'Ptr', DEVMODE)
		n0 := NumGet(DEVMODE, 172, 'UInt')
		n1 := NumGet(DEVMODE, 176, 'UInt')
		b := n0 < n1
		dimension1 := n% b% | n%!b% << 32
		dimension2 := n%!b% | n% b% << 32
	}

	Switch Orientation, false {
        	Case 'Landscape'          ,   0: i := 1, orientation := DMDO_DEFAULT
	        Case 'Portrait'           ,  90: i := 2, orientation := DMDO_90
        	Case 'Landscape (flipped)', 180: i := 1, orientation := DMDO_180
	        Case 'Portrait (flipped)' , 270: i := 2, orientation := DMDO_270
		Default:                         i := 1, orientation := DMDO_DEFAULT
	}
	NumPut('Int'  , orientation , DEVMODE,  84)
	NumPut('Int64', dimension%i%, DEVMODE, 172)
	
	If (Save = 1) {
		; Keep screen rotation after rebooting
		DllCall('ChangeDisplaySettings', 'Ptr', DEVMODE, 'UInt', CDS_UPDATEREGISTRY)
	} Else {
		; Only temporarily change it for the current windows session
		DllCall('ChangeDisplaySettings', 'Ptr', DEVMODE, 'UInt', CDS_NONE)
	}
}

ChangeScreenOrientation("Portrait", 0)
Msgbox GetScreenOrientation()
ChangeScreenOrientation("Landscape (flipped)", 0)
Msgbox GetScreenOrientation()
ChangeScreenOrientation("Portrait (flipped)", 0)
Msgbox GetScreenOrientation()
ChangeScreenOrientation("Landscape", 0)
Msgbox GetScreenOrientation()
I also added a more permanent save of the settings (=CDS_UPDATEREGISTRY) because after a reboot my screen rotation setting reverted back to the DMDO_DEFAULT.

Appreciate all the effort everyone! :salute:

ntepa
Posts: 406
Joined: 19 Oct 2022, 20:52

Re: Screen rotation in AHK v2

Post by ntepa » 08 Jun 2023, 13:42

teadrinker wrote:
08 Jun 2023, 08:38
This line

Code: Select all

dimension1 := 0, dimension2 := 0
is unnecessary.
Thanks for catching that. I forgot to remove it.

zeit
Posts: 2
Joined: 04 Nov 2022, 07:51

Re: Screen rotation in AHK v2

Post by zeit » 04 Aug 2023, 04:13

teadrinker wrote:
07 Jun 2023, 20:15
It can be simplified:

Code: Select all

1::ChangeScreenOrientation(0)
2::ChangeScreenOrientation(90)
3::ChangeScreenOrientation(180)
4::ChangeScreenOrientation(270)

ChangeScreenOrientation(Orientation := 'Landscape') {
    static DMDO_DEFAULT := 0, DMDO_90 := 1, DMDO_180 := 2, DMDO_270 := 3
         , DEVMODE := '', dimension1 := 0, dimension2 := 0, dmSize := 220
    if !DEVMODE {
        NumPut('Short', dmSize, DEVMODE := Buffer(dmSize, 0), 68)
        DllCall('EnumDisplaySettings', 'Ptr', 0, 'Int', -1, 'Ptr', DEVMODE)
        n0 := NumGet(DEVMODE, 172, 'UInt')
        n1 := NumGet(DEVMODE, 176, 'UInt')
        Loop 2 {
            dimension%A_Index% := n%(n0 > n1) ^ (A_Index = 1)%
                                | n%(n0 < n1) ^ (A_Index = 1)% << 32
        }
    }
    switch Orientation, false {
        case 'Landscape'          ,   0: i := 1, orientation := DMDO_DEFAULT
        case 'Portrait'           ,  90: i := 2, orientation := DMDO_90
        case 'Landscape (flipped)', 180: i := 1, orientation := DMDO_180
        case 'Portrait (flipped)' , 270: i := 2, orientation := DMDO_270
        default:                         i := 1, orientation := DMDO_DEFAULT
    }
    NumPut('Int'  , orientation , DEVMODE,  84)
    NumPut('Int64', dimension%i%, DEVMODE, 172)
    DllCall('ChangeDisplaySettings', 'Ptr', DEVMODE, 'UInt', 0)
}
You guys are genius! It works on Win11 for me. :trollface:

Post Reply

Return to “Ask for Help (v2)”