FAQ  •  Register  •  Login

Windows: Dual Monitor Support -monitor2 cmdline param

Moderator: Inside3D Admins

<<

Baker

User avatar

Posts: 3175

Joined: Tue Mar 14, 2006 5:15 am

Post Tue Apr 12, 2011 11:21 pm

Windows: Dual Monitor Support -monitor2 cmdline param

Requires: Microsoft Visual Studio.NET 2003 or later. Won't work in MSVC6 without an SDK update (Windows 2003 SP1 SDK) that is no longer available via internet from Microsoft.

This will enable an engine to run on the secondary monitor of a dual monitor system by adding -monitor2 to the command line. What this actually does is switch the primary monitor to the secondary in Vid_Init and restore it back in Vid_Shutdown (which is called by Host_Error). So this is achieves dual monitor support for a client by cheating a little, but it isn't much different than how Quake sets everything to fullscreen and resets it back on shutdown/exit. Reason: Windows and OpenGL on dual monitors may or may not have hardware acceleration on the 2nd monitor. This method works around the issue.

Instructions

1. In the beginning of Vid_Init do something like this (in gl_vidnt.c or vid_wgl.c depending on your engine):
  Code:
if (COM_CheckParm("-monitor2"))
{
   CollectMonitorInformation ();
   if (num_monitors == 2)  // This is written in a 2-monitor way, don't have a third monitor to test triple monitor
        SwapPrimaryMonitor (); // Set monitors_swapped to 1
}

2. At the end of Vid_Shutdown do something like this (in gl_vidnt.c or vid_wgl.c depending on your engine):
  Code:
    if (monitors_swapped)
      RestorePrimaryMonitor ();

3. Create monitors.c

  Code:
#define POINTER_64 __ptr64 //Baker
#include "quakedef.h" // Almost optional!
#include <windows.h>
#define MSGBOX(x) MessageBox(NULL, x, "Info", MB_OK);      // Developer message

typedef struct
{
   char DeviceName[32]; // wingdi.h --> CCHFORMNAME which is 32
   int original_x;
   int original_y;
   int original_width;
   int original_height;
} monitor_def_t;
#define MAX_NUM_MONITORS 2

monitor_def_t monitor_info[MAX_NUM_MONITORS];
int original_primary = -1;
int original_secondary = -1;
int num_monitors;
int monitors_swapped;   

DISPLAY_DEVICE DisplayDevice;

char monitorstring[2048];

DEVMODE deviceMode[MAX_NUM_MONITORS];

void CollectMonitorInformation (void)
{
   int i;
   {
      // Reset monitor info
      num_monitors = 0; // Just in case
      original_primary = -1; // Just in case
      ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
      DisplayDevice.cb = sizeof(DisplayDevice);
   }

   for(i = 0; EnumDisplayDevices(NULL, i, &DisplayDevice, 0) && num_monitors < MAX_NUM_MONITORS; i++)
   {
      if(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER || !(DisplayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
         continue; // Non-monitor

      // Ok we found a monitor
         
      deviceMode[num_monitors].dmSize = sizeof(DEVMODE);
      deviceMode[num_monitors].dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
         
      EnumDisplaySettings(DisplayDevice.DeviceName, ENUM_CURRENT_SETTINGS, &deviceMode[num_monitors]);
   
      // Populate fields
      strcpy (monitor_info[num_monitors].DeviceName, DisplayDevice.DeviceName);   
      monitor_info[num_monitors].original_x      =   deviceMode[num_monitors].dmPosition.x;
      monitor_info[num_monitors].original_y      =   deviceMode[num_monitors].dmPosition.y;
      monitor_info[num_monitors].original_width   =   deviceMode[num_monitors].dmPelsWidth;
      monitor_info[num_monitors].original_height   =   deviceMode[num_monitors].dmPelsHeight;

      if (deviceMode[num_monitors].dmPosition.x == 0 && deviceMode[num_monitors].dmPosition.y ==0)
         original_primary = num_monitors;  // Primary monitor is one with 0,0 xy position
      else
         original_secondary = num_monitors;

      strcat (monitorstring, va("Monitor #%i %s %s x y w h %i %i %i %i\n",
      num_monitors+1,
      monitor_info[num_monitors].DeviceName,
      num_monitors == original_primary ? "(Primary)" : num_monitors == original_secondary ? "(Secondary)" : "(Neither)",
      monitor_info[num_monitors].original_x,
      monitor_info[num_monitors].original_y,
      monitor_info[num_monitors].original_width,
      monitor_info[num_monitors].original_height));

      num_monitors ++;
      
   }
   MSGBOX (va("Num monitors is %i", num_monitors));
   MSGBOX (va("Monitor infos \n\n%s", monitorstring));
   MSGBOX (va("Primary Monitor is %i and secondary is %i", original_primary, original_secondary));

}

void SetPrimaryMonitorOnDeviceName (const char *NewPrimaryMonitor, const char *NewSecondaryMonitor, int SecondaryX, int SecondaryY)
{
   DEVMODE deviceMode_newSecondary;
   DEVMODE deviceMode_newPrimary;

   if (num_monitors == 0)
   {
      MSGBOX ("No monitors ... need to call CollectMonitorInformation first")
      return;
   }

   MSGBOX (va("Setting monitor %s to primary monitor", NewPrimaryMonitor))

   // Get primary infos
   deviceMode_newPrimary.dmSize = sizeof(DEVMODE);
   deviceMode_newPrimary.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
   EnumDisplaySettings(NewPrimaryMonitor, ENUM_CURRENT_SETTINGS, &deviceMode_newPrimary);

   // Get secondary infos
   deviceMode_newSecondary.dmSize = sizeof(DEVMODE);
   deviceMode_newSecondary.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
   EnumDisplaySettings(NewSecondaryMonitor, ENUM_CURRENT_SETTINGS, &deviceMode_newSecondary);

   // move old primary display to new position
   deviceMode_newSecondary.dmFields = DM_POSITION;
   deviceMode_newSecondary.dmPosition.x = deviceMode_newPrimary.dmPelsWidth;
   deviceMode_newSecondary.dmPosition.y = 0;

   ChangeDisplaySettingsEx(NewSecondaryMonitor, &deviceMode_newSecondary, NULL, CDS_UPDATEREGISTRY | CDS_NORESET, NULL);

   // move old secondary display to 0,0
   deviceMode_newPrimary.dmFields = DM_POSITION;
   deviceMode_newPrimary.dmPosition.x = 0;
   deviceMode_newPrimary.dmPosition.y = 0;

   // CDS_UPDATEREGISTRY | CDS_NORESET = Have settings updated in registry, but CDS_NORESET means it won't take effect yet
   ChangeDisplaySettingsEx(NewPrimaryMonitor, &deviceMode_newPrimary, NULL, CDS_SET_PRIMARY | CDS_UPDATEREGISTRY | CDS_NORESET , NULL);
   ChangeDisplaySettingsEx (NULL, NULL, NULL, 0, NULL);  // Force it to take effect

//   Sleep (500); // Let it think a minute
}

void SwapPrimaryMonitor (void)  // In a dual monitor situation, use 0 = first monitor or 1 = second monitor, etc.
{
   // Set secondary monitor to primary
   SetPrimaryMonitorOnDeviceName (monitor_info[original_secondary].DeviceName, monitor_info[original_primary].DeviceName, monitor_info[original_secondary].original_width, monitor_info[original_secondary].original_height);
   monitors_swapped = 1;
}

void RestorePrimaryMonitor (void)
{
   // Set original primary monitor back to primary
   SetPrimaryMonitorOnDeviceName (monitor_info[original_primary].DeviceName, monitor_info[original_secondary].DeviceName, monitor_info[original_primary].original_width, monitor_info[original_primary].original_height);
   monitors_swapped = 0;
}

In Closing

This code is only trivially locked to Quake. In fact, I think the above only uses va (...) from Quake. So it could actually be used in an Windows project pretty much unchanged.

There IS a way to do this "naturally" and just create a Window on the 2nd display, but since you will likely lose hardware acceleration I've found it isn't particularly useful (2 fps sucks). See thread ...
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
<<

r00k

Posts: 906

Joined: Sat Nov 13, 2004 10:39 pm

Post Thu Apr 21, 2011 4:08 am

Works like a champ!

Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 0 guests

Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software for PTF.
Icons provided by Aha Soft