improved multi-monitor support

- /monitorlist
- the monitor list gets updated during video restarts and by /monitorlist
- Linux > r_monitor is now 0-based and monitors are sorted top-to-bottom, left-to-right
- Windows > the Windows + shift + left/right arrow key shortcuts should be ok to use
This commit is contained in:
myT 2019-02-20 20:52:34 +01:00
parent e42c05dfc8
commit 3305c6f515
9 changed files with 142 additions and 52 deletions

View File

@ -1,5 +1,5 @@
DD Mmm 18 - 1.51 DD Mmm 19 - 1.51
add: demo recording status query extension for CPMA 1.52+ add: demo recording status query extension for CPMA 1.52+
@ -16,6 +16,12 @@ add: /toggle can now accept a value sequence (2 or more entries) to loop through
if the cvar's current value is in the sequence and not in the last spot, the next one is used if the cvar's current value is in the sequence and not in the last spot, the next one is used
otherwise, the first value in the sequence is used otherwise, the first value in the sequence is used
chg: multi-monitor support improvements
add: /monitorlist will print all detected monitors and the indices to use with r_monitor
chg: the monitor list now gets updated during video restarts and calls to /monitorlist
chg: Linux | r_monitor is a 0-based index and monitors are sorted top-to-bottom, left-to-right
fix: Windows | the Windows + shift + left/right arrow key shortcuts should be usable now
chg: bundling lightmap tiles into texture atlases for improved rendering performance chg: bundling lightmap tiles into texture atlases for improved rendering performance
chg: faster map loads by limiting the rendering back-end's frame-rate chg: faster map loads by limiting the rendering back-end's frame-rate
@ -36,7 +42,7 @@ fix: the last byte of a maximum length bitstream string wasn't parsed ("q3msgboo
fix: aborting demo playback would crash when the "nextdemo" cvar was set to play a demo fix: aborting demo playback would crash when the "nextdemo" cvar was set to play a demo
fix: no longer feeding cs commands that came from a previous gamestate to cgame fix: no longer feeding cs commands that came from a previous gamestate to cgame
example: "/map cpm22" -> "/cv map cpm25" -> elevator sound was broken example in CPMA: "/map cpm22" -> "/cv map cpm25" -> elevator sound was broken
fix: on Windows, could sometimes click outside the engine's window in raw mouse input mode fix: on Windows, could sometimes click outside the engine's window in raw mouse input mode
@ -50,7 +56,7 @@ fix: /video and /stopvideo fixes
fix: broken raw video output when r_width wasn't a multiple of 4 fix: broken raw video output when r_width wasn't a multiple of 4
fix: /stopvideo no longer leaves sound output broken for a while after stopping fix: /stopvideo no longer leaves sound output broken for a while after stopping
fix: /cv and /callvote auto-completion were disabled after cgame was shut down at least once fix: /cv and /callvote map auto-completion was disabled after cgame was shut down at least once
12 Feb 18 - 1.50 12 Feb 18 - 1.50

5
code/linux/linux_help.h Normal file
View File

@ -0,0 +1,5 @@
#define help_r_monitor \
"0-based monitor index\n" \
"Use /" S_COLOR_CMD "monitorlist " S_COLOR_HELP "to print the list of detected monitors.\n" \
"The monitors are ordered top-to-bottom and left-to-right.\n" \
"This means " S_COLOR_VAL "0 " S_COLOR_HELP "specifies the top-left monitor."

View File

@ -1,4 +1,5 @@
#include "linux_local.h" #include "linux_local.h"
#include "linux_help.h"
#include "../renderer/tr_local.h" #include "../renderer/tr_local.h"
#include "../renderer/qgl.h" #include "../renderer/qgl.h"
@ -14,13 +15,13 @@ static cvar_t* r_monitor; // 1-based, 0 means use primary monitor
static const cvarTableItem_t glimp_cvars[] = { static const cvarTableItem_t glimp_cvars[] = {
{ &r_fullscreen, "r_fullscreen", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "full-screen mode" }, { &r_fullscreen, "r_fullscreen", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_BOOL, NULL, NULL, "full-screen mode" },
{ &r_monitor, "r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", NULL, "1-based monitor index, 0=primary" } { &r_monitor, "r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH, CVART_INTEGER, "0", NULL, help_r_monitor }
}; };
static void sdl_PrintMonitorList(); static void sdl_MonitorList_f();
static const cmdTableItem_t glimp_cmds[] = { static const cmdTableItem_t glimp_cmds[] = {
{ "monitorlist", &sdl_PrintMonitorList, NULL, "prints the list of monitors" } { "monitorlist", &sdl_MonitorList_f, NULL, "refreshes and prints the monitor list" }
}; };
@ -28,26 +29,32 @@ static qbool sdl_IsMonitorListValid()
{ {
const int count = glimp.monitorCount; const int count = glimp.monitorCount;
const int curr = glimp.monitor; const int curr = glimp.monitor;
const int prim = glimp.primaryMonitor;
return return
count >= 1 && count >= 1 && count <= MAX_MONITOR_COUNT &&
curr >= 0 && curr >= 0 && curr < count;
curr < count && }
prim >= 0 &&
prim < count &&
glimp.monitorRects[prim].x == 0 && static int sdl_CompareMonitorRects( const void* aPtr, const void* bPtr )
glimp.monitorRects[prim].y == 0; {
const SDL_Rect* const a = (const SDL_Rect*)aPtr;
const SDL_Rect* const b = (const SDL_Rect*)bPtr;
const int dy = a->y - b->y;
if (dy != 0)
return dy;
return a->x - b->x;
} }
static void sdl_CreateMonitorList() static void sdl_CreateMonitorList()
{ {
const int count = SDL_GetNumVideoDisplays();
if (count <= 0) {
glimp.monitorCount = 0; glimp.monitorCount = 0;
const int count = SDL_GetNumVideoDisplays();
if (count <= 0)
return; return;
}
int gi = 0; int gi = 0;
for (int si = 0; si < count; ++si) { for (int si = 0; si < count; ++si) {
@ -58,17 +65,9 @@ static void sdl_CreateMonitorList()
} }
glimp.monitorCount = gi; glimp.monitorCount = gi;
glimp.primaryMonitor = -1; if (sdl_IsMonitorListValid())
const int finalCount = glimp.monitorCount; qsort(glimp.monitorRects, (size_t)glimp.monitorCount, sizeof(glimp.monitorRects[0]), &sdl_CompareMonitorRects);
for(int i = 0; i < finalCount; ++i) { else
const SDL_Rect rect = glimp.monitorRects[i];
if (rect.x == 0 && rect.y == 0) {
glimp.primaryMonitor = i;
break;
}
}
if (!sdl_IsMonitorListValid())
glimp.monitorCount = 0; glimp.monitorCount = 0;
} }
@ -76,15 +75,15 @@ static void sdl_CreateMonitorList()
// call this before creating the window // call this before creating the window
static void sdl_UpdateMonitorIndexFromCvar() static void sdl_UpdateMonitorIndexFromCvar()
{ {
if (glimp.monitorCount <= 0) if (glimp.monitorCount <= 0 || glimp.monitorCount >= MAX_MONITOR_COUNT)
return; return;
const int monitor = Cvar_Get("r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH)->integer; const int monitor = Cvar_Get("r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH)->integer;
if (monitor <= 0 || monitor > glimp.monitorCount) { if (monitor < 0 || monitor >= glimp.monitorCount) {
glimp.monitor = glimp.primaryMonitor; glimp.monitor = 0;
return; return;
} }
glimp.monitor = Com_ClampInt(0, glimp.monitorCount - 1, monitor - 1); glimp.monitor = monitor;
} }
@ -96,25 +95,20 @@ void sdl_UpdateMonitorIndexFromWindow()
// update the glimp index // update the glimp index
const int current = SDL_GetWindowDisplayIndex(glimp.window); const int current = SDL_GetWindowDisplayIndex(glimp.window);
if (current < 0) { if (current < 0 || current >= glimp.monitorCount) {
glimp.monitorCount = 0; glimp.monitorCount = 0;
return; return;
} }
glimp.monitor = current; glimp.monitor = current;
// update the cvar index // update the cvar index
if( r_monitor->integer == 0 && Cvar_Set("r_monitor", va("%d", glimp.monitor));
glimp.monitor == glimp.primaryMonitor)
return;
Cvar_Set("r_monitor", va("%d", glimp.monitor + 1));
} }
static void sdl_GetSafeDesktopRect( SDL_Rect* rect ) static void sdl_GetSafeDesktopRect( SDL_Rect* rect )
{ {
if (glimp.monitorCount <= 0 || if (!sdl_IsMonitorListValid()) {
glimp.monitor < 0 ||
glimp.monitor >= glimp.monitorCount) {
rect->x = 0; rect->x = 0;
rect->y = 0; rect->y = 0;
rect->w = 1280; rect->w = 1280;
@ -128,15 +122,27 @@ static void sdl_GetSafeDesktopRect( SDL_Rect* rect )
static void sdl_PrintMonitorList() static void sdl_PrintMonitorList()
{ {
const int count = glimp.monitorCount; const int count = glimp.monitorCount;
Com_Printf("Monitor count: %d\n", count); if (count <= 0) {
Com_Printf("No monitor detected.\n");
return;
}
Com_Printf("Monitors detected (left is " S_COLOR_CVAR "r_monitor ^7value):\n");
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
const SDL_Rect rect = glimp.monitorRects[i]; const SDL_Rect rect = glimp.monitorRects[i];
Com_Printf("Monitor #%d: %d,%d %dx%d\n", i + 1, rect.x, rect.y, rect.w, rect.h); Com_Printf(S_COLOR_VAL "%d ^7%dx%d at %d,%d\n", i, rect.w, rect.h, rect.x, rect.y);
} }
} }
static void sdl_MonitorList_f()
{
sdl_CreateMonitorList();
sdl_UpdateMonitorIndexFromCvar();
sdl_PrintMonitorList();
}
void Sys_GL_Init() void Sys_GL_Init()
{ {
if (glimp.window != NULL) if (glimp.window != NULL)

View File

@ -10,7 +10,6 @@ struct glImp_t {
SDL_Rect monitorRects[MAX_MONITOR_COUNT]; SDL_Rect monitorRects[MAX_MONITOR_COUNT];
int monitorCount; int monitorCount;
int primaryMonitor; // primary monitor, 0-based
int monitor; // current monitor, 0-based int monitor; // current monitor, 0-based
}; };

View File

@ -413,8 +413,10 @@ static qbool GLW_CreateWindow()
const int x = monRect.left + dx; const int x = monRect.left + dx;
const int y = monRect.top + dy; const int y = monRect.top + dy;
g_wv.duringCreateWindow = qtrue;
g_wv.hWnd = CreateWindowEx( exstyle, CLIENT_WINDOW_TITLE, " " CLIENT_WINDOW_TITLE, style, g_wv.hWnd = CreateWindowEx( exstyle, CLIENT_WINDOW_TITLE, " " CLIENT_WINDOW_TITLE, style,
x, y, w, h, NULL, NULL, g_wv.hInstance, NULL ); x, y, w, h, NULL, NULL, g_wv.hInstance, NULL );
g_wv.duringCreateWindow = qfalse;
if ( !g_wv.hWnd ) if ( !g_wv.hWnd )
ri.Error( ERR_FATAL, "GLW_CreateWindow() - Couldn't create window" ); ri.Error( ERR_FATAL, "GLW_CreateWindow() - Couldn't create window" );
@ -550,6 +552,7 @@ void WIN_SetDesktopDisplaySettings()
static qbool GLW_SetMode() static qbool GLW_SetMode()
{ {
WIN_InitMonitorList();
WIN_UpdateMonitorIndexFromCvar(); WIN_UpdateMonitorIndexFromCvar();
const RECT& monRect = g_wv.monitorRects[g_wv.monitor]; const RECT& monRect = g_wv.monitorRects[g_wv.monitor];

View File

@ -6,4 +6,8 @@ S_COLOR_VAL " 2 " S_COLOR_HELP "= Win32 input"
#define help_in_minimize \ #define help_in_minimize \
"hotkey to minimize/restore the window\n" \ "hotkey to minimize/restore the window\n" \
"Use " S_COLOR_CMD "minimizekeynames " S_COLOR_HELP "to print the list of usable key names." "Use /" S_COLOR_CMD "minimizekeynames " S_COLOR_HELP "to print the list of usable key names."
#define help_r_monitor \
"1-based monitor index, 0=primary\n" \
"Use /" S_COLOR_CMD "monitorlist " S_COLOR_HELP "to print the list of detected monitors."

View File

@ -38,6 +38,7 @@ qbool IN_ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); // returns tr
void IN_Frame(); void IN_Frame();
// misc. Windows-specific stuff // misc. Windows-specific stuff
void WIN_InitMonitorList();
void WIN_UpdateMonitorIndexFromCvar(); void WIN_UpdateMonitorIndexFromCvar();
void WIN_UpdateMonitorIndexFromMainWindow(); void WIN_UpdateMonitorIndexFromMainWindow();
void WIN_UpdateResolution( int width, int height ); void WIN_UpdateResolution( int width, int height );
@ -73,6 +74,8 @@ typedef struct {
// using Sys_Milliseconds // using Sys_Milliseconds
int sysMsgTime; int sysMsgTime;
qbool duringCreateWindow; // qtrue during the call to CreateWindow
RECT monitorRects[MAX_MONITOR_COUNT]; RECT monitorRects[MAX_MONITOR_COUNT];
HMONITOR hMonitors[MAX_MONITOR_COUNT]; HMONITOR hMonitors[MAX_MONITOR_COUNT];
int monitor; // 0-based index of the monitor currently used for display int monitor; // 0-based index of the monitor currently used for display

View File

@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "../client/client.h" #include "../client/client.h"
#include "../qcommon/qcommon.h" #include "../qcommon/qcommon.h"
#include "win_local.h" #include "win_local.h"
#include "win_help.h"
#include "resource.h" #include "resource.h"
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
@ -653,8 +654,12 @@ static BOOL CALLBACK WIN_MonitorEnumCallback( HMONITOR hMonitor, HDC hdcMonitor,
} }
static void WIN_InitMonitorList() void WIN_InitMonitorList()
{ {
g_wv.monitor = 0;
g_wv.primaryMonitor = 0;
g_wv.monitorCount = 0;
EnumDisplayMonitors( NULL, NULL, &WIN_MonitorEnumCallback, 0 ); EnumDisplayMonitors( NULL, NULL, &WIN_MonitorEnumCallback, 0 );
const POINT zero = { 0, 0 }; const POINT zero = { 0, 0 };
@ -675,7 +680,7 @@ void WIN_UpdateMonitorIndexFromCvar()
// use Cvar_Get to enforce the latched change, if any // use Cvar_Get to enforce the latched change, if any
const int monitor = Cvar_Get( "r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH )->integer; const int monitor = Cvar_Get( "r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH )->integer;
Cvar_SetRange( "r_monitor", CVART_INTEGER, "0", va("%d", g_wv.monitorCount) ); Cvar_SetRange( "r_monitor", CVART_INTEGER, "0", va("%d", g_wv.monitorCount) );
Cvar_SetHelp( "r_monitor", "1-based monitor index, 0=primary" ); Cvar_SetHelp( "r_monitor", help_r_monitor );
if ( monitor <= 0 || monitor > g_wv.monitorCount ) { if ( monitor <= 0 || monitor > g_wv.monitorCount ) {
g_wv.monitor = g_wv.primaryMonitor; g_wv.monitor = g_wv.primaryMonitor;
return; return;
@ -707,6 +712,36 @@ void WIN_UpdateMonitorIndexFromMainWindow()
} }
static void WIN_MonitorList_f()
{
WIN_InitMonitorList();
WIN_UpdateMonitorIndexFromCvar();
if ( g_wv.monitorCount <= 0 ) {
Com_Printf( "No monitor detected.\n" );
return;
}
Com_Printf( "Monitors detected (left is " S_COLOR_CVAR "r_monitor ^7value):\n" );
for ( int i = 0; i < g_wv.monitorCount; i++ ) {
const RECT r = g_wv.monitorRects[i];
const int w = (int)( r.right - r.left );
const int h = (int)( r.bottom - r.top );
const char* const p = i == g_wv.primaryMonitor ? " (primary)" : "";
Com_Printf( S_COLOR_VAL "%d ^7%dx%d at %d,%d%s\n",
i + 1, w, h, (int)r.left, (int)r.top, p );
}
}
static void WIN_RegisterMonitorCommands()
{
Cmd_AddCommand( "monitorlist", &WIN_MonitorList_f );
Cmd_SetModule( "monitorlist", MODULE_CLIENT );
Cmd_SetHelp( "monitorlist", "refreshes and prints the monitor list" );
}
/////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////
@ -720,6 +755,7 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
WIN_InstallExceptionHandlers(); WIN_InstallExceptionHandlers();
// done here so the early console can be shown on the primary monitor
WIN_InitMonitorList(); WIN_InitMonitorList();
// done before Com/Sys_Init since we need this for error output // done before Com/Sys_Init since we need this for error output
@ -732,6 +768,7 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) ); Q_strncpyz( sys_cmdline, lpCmdLine, sizeof( sys_cmdline ) );
Com_Init( sys_cmdline ); Com_Init( sys_cmdline );
WIN_RegisterExceptionCommands(); WIN_RegisterExceptionCommands();
WIN_RegisterMonitorCommands();
NET_Init(); NET_Init();

View File

@ -168,6 +168,8 @@ LRESULT CALLBACK MainWndProc (
WPARAM wParam, WPARAM wParam,
LPARAM lParam) LPARAM lParam)
{ {
static qbool draggingWindow = qfalse;
switch (uMsg) switch (uMsg)
{ {
case WM_CREATE: case WM_CREATE:
@ -197,12 +199,16 @@ LRESULT CALLBACK MainWndProc (
WIN_S_Mute( !g_wv.activeApp ); WIN_S_Mute( !g_wv.activeApp );
break; break;
case WM_MOVING:
draggingWindow = qtrue;
break;
case WM_MOVE: case WM_MOVE:
{
if (!r_fullscreen->integer )
{ {
WIN_UpdateMonitorIndexFromMainWindow(); WIN_UpdateMonitorIndexFromMainWindow();
if ( !r_fullscreen->integer )
{
RECT r; RECT r;
r.left = 0; r.left = 0;
r.top = 0; r.top = 0;
@ -218,12 +224,33 @@ LRESULT CALLBACK MainWndProc (
vid_xpos->modified = qfalse; vid_xpos->modified = qfalse;
vid_ypos->modified = qfalse; vid_ypos->modified = qfalse;
} }
draggingWindow = qfalse;
} }
break; break;
case WM_SIZE: case WM_SIZE:
if ( wParam != SIZE_MINIMIZED ) if ( wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED )
WIN_UpdateResolution( (int)LOWORD(lParam), (int)HIWORD(lParam) ); {
WIN_UpdateMonitorIndexFromMainWindow();
// note that WM_SIZE can be called with no actual size change
const int w = (int)LOWORD( lParam );
const int h = (int)HIWORD( lParam );
if ( g_wv.duringCreateWindow )
WIN_UpdateResolution( w, h );
}
break;
case WM_WINDOWPOSCHANGED:
{
const int prevMon = g_wv.monitor;
WIN_UpdateMonitorIndexFromMainWindow();
const int currMon = g_wv.monitor;
if ( !g_wv.duringCreateWindow && !draggingWindow && currMon != prevMon )
Cbuf_AddText( "vid_restart\n" );
}
break; break;
case WM_SYSCOMMAND: case WM_SYSCOMMAND: