 |
AutoHotkey Community Let's help each other out
|
| View previous topic :: View next topic |
| Author |
Message |
jaco0646
Joined: 07 Oct 2006 Posts: 553 Location: MN, USA
|
Posted: Mon Oct 15, 2007 12:22 am Post subject: Dual Display Toggle (Display Properties Replacement) |
|
|
Here's a script I wrote to duplicate the Display Properties' Settings tab. EDIT: It can now change the primary display! It can extend the Windows desktop, plus change resolution, color quality, and refresh rate.
This script is registry-based so it should be easy to understand and modify for anyone who wants to use it (as opposed to using DLLcalls, which are a bit too complicated for amateur coders like me ).
It should work on Windows XP systems with two displays connected to a single video card, but probably not systems with dual video cards.
Have fun!
| Code: | #SingleInstance force
#NoTrayIcon
#NoEnv
Loop,4
{
RegRead, ID, HKLM,HARDWARE\DEVICEMAP\VIDEO,% "\Device\Video" A_Index-1
StringMid, Key1, ID, 97,4
StringMid, ID, ID, 58,38
RegRead, ID2, HKLM,HARDWARE\DEVICEMAP\VIDEO,% "\Device\Video" A_Index
StringMid, Key2, ID2, 97,4
StringMid, ID2, ID2, 58,38
If (ID=ID2)
break
If A_Index=4
{
MsgBox,16,DDT,Primary and Secondary IDs do not match.
ExitApp
}
}
RegReads:
Loop,2
{
abc := A_Index=1 ? "Pri":"Sec"
Key := A_Index=1 ? Key1:Key2
%abc%_Mons=
Loop, HKCC, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%,2
%abc%_Mons .= "\" A_LoopRegName "|"
RegRead, %abc%_XRes, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, DefaultSettings.XResolution
RegRead, %abc%_YRes, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, DefaultSettings.YResolution
%abc%_wide := %abc%_XRes/%abc%_YRes >= 1.5 ? 1:0
RegRead, %abc%_CQ, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, DefaultSettings.BitsPerPel
RegRead, %abc%_RR, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, DefaultSettings.VRefresh
RegRead, %abc%_Pos, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, Attach.RelativeX
RegRead, %abc%_Ext, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%
, Attach.ToDesktop
}
Loop,2
{
abc := A_Index=1 ? "Pri":"Sec"
xyz := A_Index=1 ? "Sec":"Pri"
%abc%_Pri := %abc%_Pos=0 ? 1:0
%abc%_LoR := %abc%_Pos > %xyz%_XRes ? 1:0
}
If Refresh
return
;
;##############
;#### GUI: ####
;##############
;
Gui, Add, Radio, vPri_Dis gRadio checked Section, Primary Display
Gui, Add, Radio, vSec_Dis gRadio x+10, Secondary Display
Gui, Add, Text, x+10 vRes, %Pri_XRes% x %Pri_YRes% %A_Space%
Gui, Add, CheckBox, x+15 vWide, Widescreen
Gui, Add, Pic, x+10 w16 h16 gPic, %A_WinDir%\System32\desk.cpl
Gui, Add, GroupBox, xs w250 h60 Section, Screen Resolution
Gui, Add, Text, vLeft, Less
Gui, Add, Text, vRight, More
Gui, Add, Slider
, Buddy1Left Buddy2Right xs+33 ys+20 w180 vSlider gSlider AltSubmit Range640-1940 TickInterval100
Gui, Add, GroupBox, ys h60, Color Quality
Gui, Add, DDL, xp+10 yp+20 vCQ AltSubmit
,256 Colors (8 bit)|High Color (16 bit)|True Color (24 bit)|True Color (32 bit)|
Gui, Add, GroupBox, xp-10 ys+70, Refresh Rate
Gui, Add, DDL, xp+10 yp+20 vRR AltSubmit, 60 Hertz|75 Hertz|85 Hertz|100 Hertz|
Gui, Add, CheckBox, vPri xs ys+70, Use this device as the primary monitor.
Gui, Add, CheckBox, vExt gExt, Extend my Windows desktop onto this monitor.
Gui, Add, CheckBox, vPos, Position secondary monitor to the left of primary.
Gui, Add, Button, xs w50 gID, Identify
Gui, Add, Button, xp+231 w50 gOK, OK
Gui, Add, Button, x+10 w50 gCancel, Cancel
Gui, Add, Button, x+10 w50 gApply, Apply
GoSub, Radio
Gui, Show,,Dual Display Toggle (DDT)
return
GuiClose:
ExitApp
;
;##############
;## gLabels: ##
;##############
;
Radio:
Gui, Submit, NoHide
abc := Pri_Dis=1 ? "Pri":"Sec"
xyz := Pri_Dis=1 ? "Sec":"Pri"
{
GuiControl,,Res,% %abc%_XRes " x " %abc%_YRes
GuiControl,,Slider,% %abc%_XRes
GuiControl,,Wide,% %abc%_wide
GuiControl,,Pri,% %abc%_Pri
GuiControl,,Ext,% %abc%_Ext
GuiControl,% "Disable" %abc%_Pri,Ext
GuiControl,% "Disable" %abc%_Pri,Pos
CQ := CQ(%abc%_CQ)
GuiControl,Choose,CQ,%CQ%
RR := RR(%abc%_RR)
GuiControl,Choose,RR,%RR%
If (%abc%_Pri=1) OR (%abc%_Ext=0)
GuiControl,Disable,Pri
Else GuiControl,Enable,Pri
If %abc%_Pri
GuiControl,,Pos,% %xyz%_LoR
Else GuiControl,,Pos,% %abc%_LoR
}
return
Pic:
Run, control desk.cpl`,`,4
return
Slider:
GuiControlGet,Wide,,Wide
If Wide
{
If Slider < 787
{
GuiControl,,Slider,720
GuiControl,,Res,720 x 480
}
Else If Slider < 1003
{
GuiControl,,Slider,854
GuiControl,,Res,854 x 480
}
Else If Slider < 1216
{
GuiControl,,Slider,1152
GuiControl,,Res,1152 x 768
}
Else If Slider < 1280
{
GuiControl,,Slider,1279
GuiControl,,Res,1280 x 720
}
Else If Slider < 1360
{
GuiControl,,Slider,1280
GuiControl,,Res,1280 x 854
}
Else If Slider < 1560
{
GuiControl,,Slider,1440
GuiControl,,Res,1440 x 960
}
Else If Slider < 1800
{
GuiControl,,Slider,1680
GuiControl,,Res,1680 x 1050
}
Else If Slider < 1920
{
GuiControl,,Slider,1919
GuiControl,,Res,1920 x 1080
}
Else
{
GuiControl,,Slider,1920
GuiControl,,Res,1920 x 1200
}
}
Else
{
If Slider < 704
{
GuiControl,,Slider,640
GuiControl,,Res,640 x 480
}
Else If Slider < 784
{
GuiControl,,Slider,768
GuiControl,,Res,768 x 576
}
Else If Slider < 912
{
GuiControl,,Slider,800
GuiControl,,Res,800 x 600
}
Else If Slider < 1152
{
GuiControl,,Slider,1024
GuiControl,,Res,1024 x 768
}
Else If Slider < 1280
{
GuiControl,,Slider,1279
GuiControl,,Res,1280 x 960
}
Else If Slider < 1340
{
GuiControl,,Slider,1280
GuiControl,,Res,1280 x 1024
}
Else If Slider < 1500
{
GuiControl,,Slider,1400
GuiControl,,Res,1400 x 1050
}
Else
{
GuiControl,,Slider,1600
GuiControl,,Res,1600 x 1200
}
}
return
Ext:
GuiControlGet,Ext,,Ext
GuiControl,Enable%Ext%,Pri
If Ext=0
GuiControl,,Pri,0
return
ID:
SysGet,num,80
Loop,%num%
{
SysGet,Mon%A_Index%,Monitor,%A_Index%
YRes := Mon%A_Index%Bottom*.67
num2 := A_Index+1
Gui, %num2%:+AlwaysOnTop -Caption +LastFound +ToolWindow
Gui, %num2%:Font, bold cWhite s%YRes%,Arial
Gui, %num2%:Color, EEAA99
Gui, %num2%:Margin,0,0
Gui, %num2%:Add,Text,,%A_Index%
WinSet, TransColor, EEAA99
Gui, %num2%:Show,Hide
WinGetPos,,,Width,Height
If Mon%A_Index%Left < 0
XRes := Mon%A_Index%Left-(Mon%A_Index%Left/2)-(Width/2)
Else XRes := Mon%A_Index%Left+((Mon%A_Index%Right-Mon%A_Index%Left)/2)-(Width/2)
WinMove, %XRes%,(Mon%A_Index%Bottom/2)-(Height/2)
}
Loop,%num%
Gui,% A_Index+1 ":Show",NA
Sleep,3000
Loop,%num%
Gui,% A_Index+1 ":Destroy"
return
Apply:
Refresh=1
OK:
Gui, Submit
GuiControlGet,Res,,Res
Loop, Parse, Res, %A_Space%
{
If A_Index=1
XRes := A_LoopField
Else If A_Index=3
YRes := A_LoopField
}
xyz := Pri_Dis=1 ? "Sec":"Pri"
If Pri
{
Pos2 := Pos=1 ? (4294967296-%xyz%_XRes):XRes
Pos=0
}
Else
{
Pos := Pos=1 ? (4294967296-XRes):%xyz%_XRes
Pos2=0
}
CQ := CQ(CQ)
RR := RR(RR)
Key := Pri_Dis=1 ? Key1:Key2
Mons := Pri_Dis=1 ? Pri_Mons:Sec_Mons
Loop, Parse, Mons, |
{
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, Attach.RelativeX, %Pos%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, Attach.RelativeY, 0
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, Attach.ToDesktop, %Ext%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, DefaultSettings.BitsPerPel, %CQ%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, DefaultSettings.VRefresh, %RR%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, DefaultSettings.XResolution, %XRes%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, DefaultSettings.YResolution, %YRes%
}
Key := Pri_Dis=1 ? Key2:Key1
Mons := Pri_Dis=1 ? Sec_Mons:Pri_Mons
Loop, Parse, Mons, |
{
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, Attach.RelativeX, %Pos2%
RegWrite, REG_DWORD, HKCC
, System\CurrentControlSet\Control\VIDEO\%ID%\%Key%%A_LoopField%
, Attach.RelativeY, 0
}
DllCall("ChangeDisplaySettings","UInt",0,"UInt",0)
If Refresh
{
GoSub, RegReads
GoSub, Radio
Refresh=0
Gui, Show
return
}
Cancel:
ExitApp
;
;##############
;# Functions: #
;##############
;
CQ(CQ)
{
If CQ < 5
CQ *= 8
Else CQ /= 8
return, CQ
}
RR(RR)
{
If RR < 5
{
RR := Round(RR*13+50,-1)
If (RR > 60) AND (RR < 100)
RR-=5
}
Else RR := Round((RR-50)/13)
return, RR
} |
 _________________ http://autohotkey.net/~jaco0646/
Last edited by jaco0646 on Sat Oct 20, 2007 8:20 pm; edited 2 times in total |
|
| Back to top |
|
 |
Superfraggle
Joined: 02 Nov 2004 Posts: 846 Location: London, UK
|
Posted: Mon Oct 15, 2007 12:51 pm Post subject: |
|
|
I get this error when running it.
| Quote: | ---------------------------
DDT
---------------------------
Primary and Secondary IDs do not match.
---------------------------
OK
---------------------------
|
Im guessing its because although I only have one card, it shows up as two in device manager. _________________ Steve F AKA Superfraggle
http://r.yuwie.com/superfraggle |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Tue Oct 16, 2007 3:56 am Post subject: |
|
|
I get the same error, and I only have one display adapter in Device Manager. This is what happens when you use (afaik) undocumented registry keys rather than documented functions (which are, btw, a PITA to figure out for AutoHotkey.) It does work if I change Video0 and Video1 to Video3 and Video4. Video0 and Video1 are "mirroring drivers" (i.e. not real devices) on my system.
EnumDisplayDevices can be used to retrieve the correct registry paths: | Code: | Device1Key =
Device2Key =
Loop {
if ! EnumDisplayDevices(A_Index, DeviceName, StateFlags, DeviceKey)
break
if (StateFlags & 4) ; DISPLAY_DEVICE_PRIMARY_DEVICE
Device1Key := DeviceKey
else if (StateFlags & 1) ; DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
Device2Key := DeviceKey
if (Device1Key && Device2Key)
break
}
MsgBox Primary: %Device1Key%`nSecondary: %Device2Key% |
I've posted it elsewhere, but since it doesn't have its own thread, I'll post it again here:
| Code: | ; EnumDisplayDevices(Index [, ByRef Name, ByRef StateFlags ] )
;
; Index: One-based index of device to get info for.
; DeviceName: [out] The name of the device.
; StateFlags: [out] Any reasonable combination of the following flags:
; 0x00000001 DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
; 0x00000004 DISPLAY_DEVICE_PRIMARY_DEVICE
; 0x00000008 DISPLAY_DEVICE_MIRRORING_DRIVER
; DeviceKey: [out] Path to the device's registry key relative to HKEY_LOCAL_MACHINE.
;
; Returns true if the display device exists, otherwise false.
;
EnumDisplayDevices(Index, ByRef DeviceName, ByRef StateFlags="", ByRef DeviceKey="")
{
; DISPLAY_DEVICE DisplayDevice
VarSetCapacity(DisplayDevice, 424)
; lpDisplayDevice.cb := sizeof(DISPLAY_DEVICE)
NumPut(424, DisplayDevice, 0)
VarSetCapacity(DeviceName, 32, 0)
VarSetCapacity(DeviceKey, 128, 0)
; For consistency, clear StateFlags in case of failure.
StateFlags = 0
if ! DllCall("EnumDisplayDevices"
, "UInt", 0
, "UInt", Index-1
, "UInt", &DisplayDevice
, "UInt", 0)
return false
StateFlags := NumGet(DisplayDevice, 164)
DllCall("lstrcpynA", "Str", DeviceName, "UInt", &DisplayDevice+4, "int", 32)
DllCall("lstrcpynA", "Str", DeviceKey, "UInt", &DisplayDevice+296, "int", 128)
if (SubStr(DeviceKey,1,18)="\Registry\Machine\")
DeviceKey := SubStr(DeviceKey,19)
return true
} |
| jaco0646 wrote: | | It can reproduce everything except changing the primary display (which I don't know how to do) | Device names returned by EnumDisplayDevices can be used with ChangeDisplaySettingsEx to "set the primary display."
ChangeDisplaySettings(Ex) has a flag CDS_SET_PRIMARY, described as "This device will become the primary device." As far as I can tell, this has no effect. What you actually need to do is set the "position" of the current primary monitor to something other than [0,0], and (optionally if there are only two displays) set the new primary to [0,0]. For example, if I set my primary monitor (which is 1650x1080) to [-1650,0], that puts it to the left of my (previous) secondary, which it sets as primary.
I'll post code if you're interested.
Actually, you might be able to do it by editing the positions in the registry... |
|
| Back to top |
|
 |
jaco0646
Joined: 07 Oct 2006 Posts: 553 Location: MN, USA
|
Posted: Fri Oct 19, 2007 2:41 am Post subject: |
|
|
| lexikos wrote: | | I get the same error, and I only have one display adapter in Device Manager. This is what happens when you use (afaik) undocumented registry keys |
According to this Microsoft article "The Device\Video0 value points to a registry key that has the location of the video driver that Windows is configured to load." So the registry key I used should be correct; however the article does go on to explain how third party drivers can change display settings. BUT, if I'm reading the article correctly, the primary display device should still be found through the Device\Video0 key, its corresponding HKLM key, and the InstalledDisplayDrivers value.
If all that is correct, then on your machine, the InstalledDisplayDrivers value should point to the same location as Video3 (i.e. the code you changed to get the script working); and with a little more coding to dig through those extra keys, I could discern the primary monitor on any XP machine with no DLLcalls.
| lexikos wrote: | | What you actually need to do is set the "position" of the current primary monitor to something other than [0,0], and (optionally if there are only two displays) set the new primary to [0,0]. |
Thank you for this information! I will experiment with these registry settings and hopefully update my script to be able to switch the primary monitor ASAP. _________________ http://autohotkey.net/~jaco0646/ |
|
| Back to top |
|
 |
Lexikos
Joined: 17 Oct 2006 Posts: 2558 Location: Australia, Qld
|
Posted: Fri Oct 19, 2007 3:19 am Post subject: |
|
|
Fair enough. Poorly documented, then. I still say using DllCall is more reliable than editing the registry.
One "help" article that applies to Windows XP may not apply to other versions of Windows. As with most poorly documented registry keys, it's subject to change. ChangeDisplaySettingsEx, on the other hand, is documented to work on Windows 98 and later.
| Quote: | | If all that is correct, then on your machine, the InstalledDisplayDrivers value should point to the same location as Video3 (i.e. the code you changed to get the script working); and with a little more coding to dig through those extra keys, I could discern the primary monitor on any XP machine with no DLLcalls. | \Device\Video0: | Code: | | \REGISTRY\Machine\System\ControlSet001\Services\VgaSave\Device0 |
InstalledDisplayDrivers: | Code: | vga
framebuf
vga256
vga64k
| I don't see how that in any way identifies the primary display...
Anyway, that's not relevant, since I'm not on Windows XP... |
|
| Back to top |
|
 |
jaco0646
Joined: 07 Oct 2006 Posts: 553 Location: MN, USA
|
Posted: Sat Oct 20, 2007 8:35 pm Post subject: update |
|
|
I've updated the script, and it can now change the primary monitor setting. On my PC at least, (XP SP2) the script now performs exactly like the Display Properties' Settings tab.
It also looks for matching display driver GUIDs in all 5 registry values under HKLM\HARDWARE\DEVICEMAP\VIDEO, so it should work on most XP machines. _________________ http://autohotkey.net/~jaco0646/ |
|
| Back to top |
|
 |
hps
Joined: 19 Aug 2005 Posts: 14 Location: Germany
|
Posted: Sun Oct 21, 2007 1:04 pm Post subject: Positioning of the monitors |
|
|
Its working without problems on my setup under XP.
Whats missing for me:
The positioning of the monitors. At home I have two externals, both at
1280x1024.
In Office the integrated with 1024x768 and one external with 1280x1024,
aligned at bottom. With desk.cpl they are always alignet at top. |
|
| Back to top |
|
 |
jaco0646
Joined: 07 Oct 2006 Posts: 553 Location: MN, USA
|
Posted: Sun Oct 21, 2007 4:50 pm Post subject: limitations |
|
|
Thanks for the feedback, I'm glad it works for you! As for the two problems you mention:
| hps wrote: | Whats missing for me:
The positioning of the monitors. At home I have two externals, both at
1280x1024. |
The script only supports two monitors (one external). For one reason, because the code is simpler, and also because I don't have a 3 monitor setup anywhere to test it on. Sorry, that's why I called it Dual Display Toggle.
| hps wrote: | In Office the integrated with 1024x768 and one external with 1280x1024,
aligned at bottom. With desk.cpl they are always alignet at top. |
The current script only supports setting up monitors side by side (juxtaposed) again because the code is simpler. I thought a setup with monitors stacked on top of eachother sounded kind of silly, but it is definitely doable. Currently the script always sets the Attach.RelativeY coordinate to zero. I will consider adding a feature to change it. Thanks for the suggestion! _________________ http://autohotkey.net/~jaco0646/ |
|
| Back to top |
|
 |
|
|
You can post new topics in this forum You can reply to topics in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|