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+
@ -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

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_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()
{
const int count = SDL_GetNumVideoDisplays();
if (count <= 0) {
glimp.monitorCount = 0;
const int count = SDL_GetNumVideoDisplays();
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)

View File

@ -10,7 +10,6 @@ struct glImp_t {
SDL_Rect monitorRects[MAX_MONITOR_COUNT];
int monitorCount;
int primaryMonitor; // primary 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 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];

View File

@ -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."

View File

@ -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

View File

@ -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 <errno.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 );
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();

View File

@ -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();
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: