From 3305c6f5151888765ea845b6f43dc8e0d777e1ad Mon Sep 17 00:00:00 2001 From: myT Date: Wed, 20 Feb 2019 20:52:34 +0100 Subject: [PATCH] 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 --- changelog.txt | 12 ++++-- code/linux/linux_help.h | 5 +++ code/linux/sdl_glimp.cpp | 84 ++++++++++++++++++++------------------ code/linux/sdl_local.h | 3 +- code/win32/win_glimp.cpp | 3 ++ code/win32/win_help.h | 6 ++- code/win32/win_local.h | 3 ++ code/win32/win_main.cpp | 41 ++++++++++++++++++- code/win32/win_wndproc.cpp | 37 ++++++++++++++--- 9 files changed, 142 insertions(+), 52 deletions(-) create mode 100644 code/linux/linux_help.h diff --git a/changelog.txt b/changelog.txt index 89035b2..0f690fd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,5 @@ -DD Mmm 18 - 1.51 +DD Mmm 19 - 1.51 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 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: 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: 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 @@ -50,7 +56,7 @@ fix: /video and /stopvideo fixes 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: /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 diff --git a/code/linux/linux_help.h b/code/linux/linux_help.h new file mode 100644 index 0000000..774d5d5 --- /dev/null +++ b/code/linux/linux_help.h @@ -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." diff --git a/code/linux/sdl_glimp.cpp b/code/linux/sdl_glimp.cpp index 40d8c43..9585d45 100644 --- a/code/linux/sdl_glimp.cpp +++ b/code/linux/sdl_glimp.cpp @@ -1,4 +1,5 @@ #include "linux_local.h" +#include "linux_help.h" #include "../renderer/tr_local.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[] = { { &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[] = { - { "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 curr = glimp.monitor; - const int prim = glimp.primaryMonitor; return - count >= 1 && - curr >= 0 && - curr < count && - prim >= 0 && - prim < count && - glimp.monitorRects[prim].x == 0 && - glimp.monitorRects[prim].y == 0; + count >= 1 && count <= MAX_MONITOR_COUNT && + curr >= 0 && curr < count; +} + + +static int sdl_CompareMonitorRects( const void* aPtr, const void* bPtr ) +{ + 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() { + glimp.monitorCount = 0; + const int count = SDL_GetNumVideoDisplays(); - if (count <= 0) { - glimp.monitorCount = 0; + if (count <= 0) return; - } int gi = 0; for (int si = 0; si < count; ++si) { @@ -58,17 +65,9 @@ static void sdl_CreateMonitorList() } glimp.monitorCount = gi; - glimp.primaryMonitor = -1; - const int finalCount = glimp.monitorCount; - for(int i = 0; i < finalCount; ++i) { - const SDL_Rect rect = glimp.monitorRects[i]; - if (rect.x == 0 && rect.y == 0) { - glimp.primaryMonitor = i; - break; - } - } - - if (!sdl_IsMonitorListValid()) + if (sdl_IsMonitorListValid()) + qsort(glimp.monitorRects, (size_t)glimp.monitorCount, sizeof(glimp.monitorRects[0]), &sdl_CompareMonitorRects); + else glimp.monitorCount = 0; } @@ -76,15 +75,15 @@ static void sdl_CreateMonitorList() // call this before creating the window static void sdl_UpdateMonitorIndexFromCvar() { - if (glimp.monitorCount <= 0) + if (glimp.monitorCount <= 0 || glimp.monitorCount >= MAX_MONITOR_COUNT) return; const int monitor = Cvar_Get("r_monitor", "0", CVAR_ARCHIVE | CVAR_LATCH)->integer; - if (monitor <= 0 || monitor > glimp.monitorCount) { - glimp.monitor = glimp.primaryMonitor; + if (monitor < 0 || monitor >= glimp.monitorCount) { + glimp.monitor = 0; 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 const int current = SDL_GetWindowDisplayIndex(glimp.window); - if (current < 0) { + if (current < 0 || current >= glimp.monitorCount) { glimp.monitorCount = 0; return; } glimp.monitor = current; // update the cvar index - if( r_monitor->integer == 0 && - glimp.monitor == glimp.primaryMonitor) - return; - Cvar_Set("r_monitor", va("%d", glimp.monitor + 1)); + Cvar_Set("r_monitor", va("%d", glimp.monitor)); } static void sdl_GetSafeDesktopRect( SDL_Rect* rect ) { - if (glimp.monitorCount <= 0 || - glimp.monitor < 0 || - glimp.monitor >= glimp.monitorCount) { + if (!sdl_IsMonitorListValid()) { rect->x = 0; rect->y = 0; rect->w = 1280; @@ -128,15 +122,27 @@ static void sdl_GetSafeDesktopRect( SDL_Rect* rect ) static void sdl_PrintMonitorList() { 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) { 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() { if (glimp.window != NULL) diff --git a/code/linux/sdl_local.h b/code/linux/sdl_local.h index 1f7f174..9d9433c 100644 --- a/code/linux/sdl_local.h +++ b/code/linux/sdl_local.h @@ -10,8 +10,7 @@ struct glImp_t { SDL_Rect monitorRects[MAX_MONITOR_COUNT]; int monitorCount; - int primaryMonitor; // primary monitor, 0-based - int monitor; // current monitor, 0-based + int monitor; // current monitor, 0-based }; diff --git a/code/win32/win_glimp.cpp b/code/win32/win_glimp.cpp index 13c8940..02e3e4b 100644 --- a/code/win32/win_glimp.cpp +++ b/code/win32/win_glimp.cpp @@ -413,8 +413,10 @@ static qbool GLW_CreateWindow() const int x = monRect.left + dx; const int y = monRect.top + dy; + g_wv.duringCreateWindow = qtrue; g_wv.hWnd = CreateWindowEx( exstyle, CLIENT_WINDOW_TITLE, " " CLIENT_WINDOW_TITLE, style, x, y, w, h, NULL, NULL, g_wv.hInstance, NULL ); + g_wv.duringCreateWindow = qfalse; if ( !g_wv.hWnd ) ri.Error( ERR_FATAL, "GLW_CreateWindow() - Couldn't create window" ); @@ -550,6 +552,7 @@ void WIN_SetDesktopDisplaySettings() static qbool GLW_SetMode() { + WIN_InitMonitorList(); WIN_UpdateMonitorIndexFromCvar(); const RECT& monRect = g_wv.monitorRects[g_wv.monitor]; diff --git a/code/win32/win_help.h b/code/win32/win_help.h index 3227c44..e118710 100644 --- a/code/win32/win_help.h +++ b/code/win32/win_help.h @@ -6,4 +6,8 @@ S_COLOR_VAL " 2 " S_COLOR_HELP "= Win32 input" #define help_in_minimize \ "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." diff --git a/code/win32/win_local.h b/code/win32/win_local.h index 9cf8f7c..0399a37 100644 --- a/code/win32/win_local.h +++ b/code/win32/win_local.h @@ -38,6 +38,7 @@ qbool IN_ProcessMessage( UINT msg, WPARAM wParam, LPARAM lParam ); // returns tr void IN_Frame(); // misc. Windows-specific stuff +void WIN_InitMonitorList(); void WIN_UpdateMonitorIndexFromCvar(); void WIN_UpdateMonitorIndexFromMainWindow(); void WIN_UpdateResolution( int width, int height ); @@ -73,6 +74,8 @@ typedef struct { // using Sys_Milliseconds int sysMsgTime; + qbool duringCreateWindow; // qtrue during the call to CreateWindow + RECT monitorRects[MAX_MONITOR_COUNT]; HMONITOR hMonitors[MAX_MONITOR_COUNT]; int monitor; // 0-based index of the monitor currently used for display diff --git a/code/win32/win_main.cpp b/code/win32/win_main.cpp index e727160..85d29bd 100644 --- a/code/win32/win_main.cpp +++ b/code/win32/win_main.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../client/client.h" #include "../qcommon/qcommon.h" #include "win_local.h" +#include "win_help.h" #include "resource.h" #include #include @@ -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 ); const POINT zero = { 0, 0 }; @@ -675,7 +680,7 @@ void WIN_UpdateMonitorIndexFromCvar() // use Cvar_Get to enforce the latched change, if any 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_SetHelp( "r_monitor", "1-based monitor index, 0=primary" ); + Cvar_SetHelp( "r_monitor", help_r_monitor ); if ( monitor <= 0 || monitor > g_wv.monitorCount ) { g_wv.monitor = g_wv.primaryMonitor; 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(); + // done here so the early console can be shown on the primary monitor WIN_InitMonitorList(); // 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 ) ); Com_Init( sys_cmdline ); WIN_RegisterExceptionCommands(); + WIN_RegisterMonitorCommands(); NET_Init(); diff --git a/code/win32/win_wndproc.cpp b/code/win32/win_wndproc.cpp index 5e8d4cf..93e3992 100644 --- a/code/win32/win_wndproc.cpp +++ b/code/win32/win_wndproc.cpp @@ -168,6 +168,8 @@ LRESULT CALLBACK MainWndProc ( WPARAM wParam, LPARAM lParam) { + static qbool draggingWindow = qfalse; + switch (uMsg) { case WM_CREATE: @@ -197,12 +199,16 @@ LRESULT CALLBACK MainWndProc ( WIN_S_Mute( !g_wv.activeApp ); break; + case WM_MOVING: + draggingWindow = qtrue; + break; + case WM_MOVE: { - if (!r_fullscreen->integer ) - { - WIN_UpdateMonitorIndexFromMainWindow(); + WIN_UpdateMonitorIndexFromMainWindow(); + if ( !r_fullscreen->integer ) + { RECT r; r.left = 0; r.top = 0; @@ -218,12 +224,33 @@ LRESULT CALLBACK MainWndProc ( vid_xpos->modified = qfalse; vid_ypos->modified = qfalse; } + + draggingWindow = qfalse; } break; case WM_SIZE: - if ( wParam != SIZE_MINIMIZED ) - WIN_UpdateResolution( (int)LOWORD(lParam), (int)HIWORD(lParam) ); + if ( wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED ) + { + 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; case WM_SYSCOMMAND: