Secondary Monitor
#1
Goodfare
Posted 06 August 2007 - 12:19 AM
If so can someone please explain.
#2
Posted 06 August 2007 - 05:08 AM
1st would be by automating the usual process of enabling the second monitor, script recorder could make a basic one.
2nd would be several dll calls I believe
EnumDisplayDevices
and
ChangeDisplaySettingsEx
I am still learning the dllcalls side so the second is beyond me
#3
Posted 08 August 2007 - 08:41 AM
;
; AutoHotkey Version: 1.0.47.02
; Language: English
; Platform: Win9x/NT ??
; Author: Lexikos
;
; Script Function:
; Enable or disable a secondary monitor. ("Extend the desktop onto this monitor")
;
#NoEnv
#NoTrayIcon
GetDisplayDeviceList("Display", false)
if Display2
{
RegRead, Attached, HKEY_CURRENT_CONFIG
, System\CurrentControlSet\Control\VIDEO\%Display2ID%\0001
, Attach.ToDesktop
; This part is in case I get the Display..ID wrong,
; so I don't write junk into the registry.
if (ErrorLevel) {
MsgBox, Error getting display attached status.
return
}
Attached := ! Attached
RegWrite, REG_DWORD, HKEY_CURRENT_CONFIG
, System\CurrentControlSet\Control\VIDEO\%Display2ID%\0001
, Attach.ToDesktop
, %Attached%
; Apply display settings from registry.
DllCall("ChangeDisplaySettings", "UInt", 0, "UInt", 0)
}
ExitApp
; Creates a global array using ArrayName as the base.
; %ArrayName% Number of display devices.
; %ArrayName%Primary The index of the primary display device.
; %ArrayName%%N% Name of display device (used by ChangeDisplaySettingsEx.)
; %ArrayName%%N%ID {GUID} value required for some registry operations.
; Only set if DesktopOnly=false:
; %ArrayName%%N%Attached =1 if "Extend the desktop onto this monitor" is enabled.
GetDisplayDeviceList(ArrayName, DesktopOnly=true)
{
local DisplayDevice, Count, DeviceName, StateFlags, DeviceKey
; DISPLAY_DEVICE DisplayDevice
VarSetCapacity(DisplayDevice, 424)
; lpDisplayDevice.cb := sizeof(DISPLAY_DEVICE)
NumPut(424, DisplayDevice, 0)
if %ArrayName%Primary
%ArrayName%Primary =
VarSetCapacity(DeviceName, 32, 0)
VarSetCapacity(DeviceKey , 128, 0)
Loop
{
if ! DllCall("EnumDisplayDevices"
, "UInt", 0 ; lpDevice (NULL: use iDevNum to identify devices)
, "UInt", A_Index-1 ; iDevNum
, "UInt", &DisplayDevice ; lpDisplayDevice
, "UInt", 0) ; dwFlags
break
StateFlags := NumGet(DisplayDevice, 164)
; Useful DISPLAY_DEVICE.StateFlags:
; #define DISPLAY_DEVICE_ATTACHED_TO_DESKTOP 0x00000001
; #define DISPLAY_DEVICE_PRIMARY_DEVICE 0x00000004
; #define DISPLAY_DEVICE_MIRRORING_DRIVER 0x00000008
if (StateFlags & 8) ; always exclude pseudo-devices
continue
if (DesktopOnly && !(StateFlags & 1))
continue
Count += 1
DllCall("lstrcpynA", "Str", DeviceName , "UInt", &DisplayDevice+4 , "int", 32)
DllCall("lstrcpynA", "Str", DeviceKey , "UInt", &DisplayDevice+296, "int", 128)
%ArrayName%%Count% := DeviceName
; DeviceKey is something like:
; \Registry\Machine\System\CurrentControlSet\Control\Video\{device-specific-value}\0000
; We want the:
; {device-specific-value}
SplitPath, DeviceKey,, DeviceKey ; "up one level"
SplitPath, DeviceKey, %ArrayName%%Count%ID
if (StateFlags & 4)
%ArrayName%Primary := Count
if (!DesktopOnly)
%ArrayName%%Count%Attached := (StateFlags&1)
; else: function only returns attached monitors, no need for %..%Attached.
}
%ArrayName% := Count
}If the GetDisplayDeviceList() function seems overly complex, that's probably because it's not specifically written for this script. For the RegWrite solution, it's only required because different display devices need a different path in the registry.Note the "\0001" part of the registry key is the four digit zero-based index of the monitor.
Also, this won't necessarily work the same on all multi-monitor setups. It should at least work on any setups where one graphics card is powering multiple monitors.
#4
novis
Posted 30 September 2007 - 02:12 PM
also is it possible to switch off primary and switch to secondary instead of extend?
#5
Posted 01 October 2007 - 03:00 AM
For instance, if I disable and re-enable my secondary display, it will move from the right side of my primary to the left side. (If it's already on the left side, it should stay where it is.) Disabling and re-enabling my primary display seems to work without problems, though.
The hotkeys I use are:
#+NumpadMult::EnableDisplayDevice("\\.\DISPLAY1", -1) ; toggle primary
#+NumpadDiv::EnableDisplayDevice("\\.\DISPLAY2", -1) ; toggle secondarySince the device names might be different, this might not work for you. If that is the case, let me know and I'll post a (better) function for getting the right device name (or you could use GetDisplayDeviceList() from my previous post.)EnableDisplayDevice.ahk:
; Enables, disables or toggles a display device.
;
; DeviceName: The name of the device, e.g. \\.\DISPLAY1
; Action: The action to take.
; 0 Disable
; 1 Enable
; -1 Toggle (may not be reliable if NoReset=true)
; NoReset: If true, settings will be saved to the registry, but not applied.
;
; The following can be used to apply settings saved in the registry:
; DllCall("ChangeDisplaySettings", "uint", 0, "uint", 1)
;
; Return values:
; DISP_CHANGE_SUCCESSFUL 0
; DISP_CHANGE_RESTART 1
; DISP_CHANGE_FAILED -1
; DISP_CHANGE_BADMODE -2
; DISP_CHANGE_NOTUPDATED -3
; DISP_CHANGE_BADFLAGS -4
; DISP_CHANGE_BADPARAM -5
;
; Examples:
; ; disable display 2
; EnableDisplayDevice("\\.\DISPLAY2", 0)
; Sleep, 10000
;
; ; simultaneously enable display 2 and disable display 1
; EnableDisplayDevice("\\.\DISPLAY2", 1, true)
; EnableDisplayDevice("\\.\DISPLAY1", 0)
; Sleep, 10000
;
; ; ensure both are enabled
; EnableDisplayDevice("\\.\DISPLAY2", 1, true)
; EnableDisplayDevice("\\.\DISPLAY1")
;
; Note: DeviceNames may vary. Rather than hard-coding the device name,
; EnumDisplayDevices() should be used to enumerate the devices.
;
EnableDisplayDevice(DeviceName, Action=1, NoReset=false)
{
if (Action = -1)
{ ; Determine if the display should be enabled or disabled.
Loop {
if ! EnumDisplayDevices(A_Index, this_name, this_state)
break
if (this_name = DeviceName) {
Action := !(this_state & 1) ; DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
break
}
}
; If Action is still -1, an invalid DeviceName was specified.
; The script will attempt to enable the display device, but
; ChangeDisplaySettingsEx() will most likely return error -5.
}
VarSetCapacity(devmode, 156, 0)
NumPut(156, devmode, 36, "UShort")
; Set DEVMODE.dmFields to indicate which fields are valid.
if (Action) ; Enable
NumPut(0x000020, devmode, 40) ; position={0,0}
else ; Disable
NumPut(0x180020, devmode, 40) ; width=0, height=0, position={0,0}
; Since CDS_NORESET is specified here, if NoReset=true, the user must
; manually call ChangeDisplaySettings(NULL,1) or restart the computer.
err := DllCall("ChangeDisplaySettingsEx", "str", DeviceName
, "uint", &devmode, "uint", 0, "uint", 0x10000001, "uint", 0)
; ChangeDisplaySettings() is called here for two reasons:
; - A restart is otherwise required to enable a secondary display device.
; See: http://support.microsoft.com/kb/308216
; - Disabling display devices with just ChangeDisplaySettingsEx()
; tends to leave them turned on.
if (!err && !NoReset)
err := DllCall("ChangeDisplaySettings", "uint", 0, "uint", 1)
return err
}
I have it saved as EnableDisplayDevice.ahk in my user library, so that no #include is necessary.To bind my previous script to a hotkey (to toggle the secondary display), simply put the hotkey (e.g. #+NumpadMult::) before
GetDisplayDeviceList("Display", false)and return instead of ExitApp.
#6
novis
Posted 02 October 2007 - 05:07 PM
#+NumpadMult::EnableDisplayDevice("\\.\DISPLAY1", -1) ; toggle primary
#+NumpadDiv::EnableDisplayDevice("\\.\DISPLAY2", -1) ; toggle secondary
thank you
np in my case. i got a plasma on dvi#2 for movies so i dont want the primary display to be on at all after the switch.Unfortunately it doesn't preserve the position(s) of the display(s).
My matrox driver only let me Extend even though they call it "independent mode". With my old quadro card and nvidia driver i could simple choose to switch to the other display ie change output from DVI1 to DVI2... such a simple task must be possible?
#7
Posted 03 October 2007 - 12:24 AM
You mean have only one display on at a time? There's an expample in the comments of my script how to simultaneously enable one display and disable the other (i.e. swap.)My matrox driver only let me Extend even though they call it "independent mode". With my old quadro card and nvidia driver i could simple choose to switch to the other display ie change output from DVI1 to DVI2... such a simple task must be possible?
#8
novis
Posted 03 October 2007 - 04:24 AM
i get a error enumerating the device. how do i hardcode it for my display? or do u know what might be the problem?
--> if ! EnumDisplayDevices(A_Index, this_name, this_state)
#9
Posted 03 October 2007 - 07:03 AM
#10
Posted 03 October 2007 - 09:48 AM
<!-- m -->http://www.grc.com/wizmo/wizmo.htm<!-- m -->
and putting it in the same folder as your ahk, and then just running it with the argument to turn off your 2nd monitor.
#11
Posted 03 October 2007 - 12:28 PM
You need EnumDisplayDevices, which I hadn't posted.i get a error enumerating the device. how do i hardcode it for my display? or do u know what might be the problem?
; 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.
;
/* Example 1 (requires EnableDisplayDevice()):
SecondaryDevice =
count = 0
Loop {
if ! EnumDisplayDevices(A_Index, DeviceName, StateFlags)
break
if !(StateFlags & 8) ; not a pseudo-device
if (++count = 2) ; second device
break
}
if DeviceName
EnableDisplayDevice(DeviceName, -1) ; toggle
*/
/* Example 2:
Loop {
if ! EnumDisplayDevices(A_Index, DeviceName, StateFlags)
break
if (StateFlags & 4)
text .= DeviceName " is the primary display device.`n"
else if (StateFlags & 1)
text .= "The desktop extends onto " DeviceName ".`n"
if (StateFlags & 8)
text .= DeviceName " is a pseudo-device.`n"
}
MsgBox %text%
*/
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
}
Example 2 (in the comments) can be used to list all of the display devices.
#12
novis
Posted 03 October 2007 - 02:31 PM
#13
novis
Posted 03 October 2007 - 04:30 PM
Is this a pseudo/place-holding-name that i should replace: \\.\DISPLAY1 ?
should i un-comment anything before i can run it?
-----
Version 1.0.47.04 of AHK
Winxp sp2
Matrox p650 pci-e
#14
Posted 04 October 2007 - 03:25 AM
\\.\DISPLAY1 is my primary display, while \\.\DISPLAY2 is my secondary. \\.\DISPLAYV1 and \\.\DISPLAYV2 are "mirroring drivers," i.e. they don't exist. It may be possible for the desktop to extend onto a pseudo-device, in which case the script would output something like:\\.\DISPLAY1 is the primary display device.
The desktop extends onto \\.\DISPLAY2.
\\.\DISPLAYV1 is a pseudo-device.
\\.\DISPLAYV2 is a pseudo-device.
The exact code I use for my hotkeys is just what I wrote in my previous post:The desktop extends onto \\.\DISPLAYV1.
\\.\DISPLAYV1 is a pseudo-device.
#+NumpadMult::EnableDisplayDevice("\\.\DISPLAY1", -1)
#+NumpadDiv::EnableDisplayDevice("\\.\DISPLAY2", -1)\\.\DISPLAY1 is a display device name, and -1 means "toggle." 0 is disable, and 1 is enable.
#15
novis
Posted 04 October 2007 - 03:58 AM
just to be sure. i got this in a new .ahk and i press: LWin+*, LWin+\ (on Numpad)
#NoEnv ; Recommended for performance and compatibility with future AutoHotkey releases.
#+NumpadMult::EnableDisplayDevice("\\.\DISPLAY1", -1)
#+NumpadDiv::EnableDisplayDevice("\\.\DISPLAY2", -1)
; 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.
;
/* Example 1 (requires EnableDisplayDevice()):
SecondaryDevice =
count = 0
Loop {
if ! EnumDisplayDevices(A_Index, DeviceName, StateFlags)
break
if !(StateFlags & 8) ; not a pseudo-device
if (++count = 2) ; second device
break
}
if DeviceName
EnableDisplayDevice(DeviceName, -1) ; toggle
*/
/* Example 2:
Loop {
if ! EnumDisplayDevices(A_Index, DeviceName, StateFlags)
break
if (StateFlags & 4)
text .= DeviceName " is the primary display device.`n"
else if (StateFlags & 1)
text .= "The desktop extends onto " DeviceName ".`n"
if (StateFlags & 8)
text .= DeviceName " is a pseudo-device.`n"
}
MsgBox %text%
*/
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
}
; Enables, disables or toggles a display device.
;
; DeviceName: The name of the device, e.g. \\.\DISPLAY1
; Action: The action to take.
; 0 Disable
; 1 Enable
; -1 Toggle (may not be reliable if NoReset=true)
; NoReset: If true, settings will be saved to the registry, but not applied.
;
; The following can be used to apply settings saved in the registry:
; DllCall("ChangeDisplaySettings", "uint", 0, "uint", 1)
;
; Return values:
; DISP_CHANGE_SUCCESSFUL 0
; DISP_CHANGE_RESTART 1
; DISP_CHANGE_FAILED -1
; DISP_CHANGE_BADMODE -2
; DISP_CHANGE_NOTUPDATED -3
; DISP_CHANGE_BADFLAGS -4
; DISP_CHANGE_BADPARAM -5
;
; Examples:
; ; disable display 2
; EnableDisplayDevice("\\.\DISPLAY2", 0)
; Sleep, 10000
;
; ; simultaneously enable display 2 and disable display 1
; EnableDisplayDevice("\\.\DISPLAY2", 1, true)
; EnableDisplayDevice("\\.\DISPLAY1", 0)
; Sleep, 10000
;
; ; ensure both are enabled
; EnableDisplayDevice("\\.\DISPLAY2", 1, true)
; EnableDisplayDevice("\\.\DISPLAY1")
;
; Note: DeviceNames may vary. Rather than hard-coding the device name,
; EnumDisplayDevices() should be used to enumerate the devices.
;
EnableDisplayDevice(DeviceName, Action=1, NoReset=false)
{
if (Action = -1)
{ ; Determine if the display should be enabled or disabled.
Loop {
if ! EnumDisplayDevices(A_Index, this_name, this_state)
break
if (this_name = DeviceName) {
Action := !(this_state & 1) ; DISPLAY_DEVICE_ATTACHED_TO_DESKTOP
break
}
}
; If Action is still -1, an invalid DeviceName was specified.
; The script will attempt to enable the display device, but
; ChangeDisplaySettingsEx() will most likely return error -5.
}
VarSetCapacity(devmode, 156, 0)
NumPut(156, devmode, 36, "UShort")
; Set DEVMODE.dmFields to indicate which fields are valid.
if (Action) ; Enable
NumPut(0x000020, devmode, 40) ; position={0,0}
else ; Disable
NumPut(0x180020, devmode, 40) ; width=0, height=0, position={0,0}
; Since CDS_NORESET is specified here, if NoReset=true, the user must
; manually call ChangeDisplaySettings(NULL,1) or restart the computer.
err := DllCall("ChangeDisplaySettingsEx", "str", DeviceName
, "uint", &devmode, "uint", 0, "uint", 0x10000001, "uint", 0)
; ChangeDisplaySettings() is called here for two reasons:
; - A restart is otherwise required to enable a secondary display device.
; See: http://support.microsoft.com/kb/308216
; - Disabling display devices with just ChangeDisplaySettingsEx()
; tends to leave them turned on.
if (!err && !NoReset)
err := DllCall("ChangeDisplaySettings", "uint", 0, "uint", 1)
return err
}




