View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0004190 | The Dark Mod | Design/Coding | public | 30.07.2015 15:55 | 04.01.2021 05:06 |
Reporter | NagaHuntress | Assigned To | cabalistic | ||
Priority | normal | Severity | normal | Reproducibility | have not tried |
Status | resolved | Resolution | fixed | ||
OS | Linux | ||||
Product Version | SVN | ||||
Target Version | TDM 2.07 | Fixed in Version | TDM 2.07 | ||
Summary | 0004190: A patch to allow alt-tabbing under Linux | ||||
Description | I've created a patch to fix the problem where it was impossible alt-tab (or use any other window manager key combos) to switch away from TDM. This patch works for both windowed and full screen modes. Changes: - It will no longer grab exclusive input of the keyboard by default. This allows the window manager to process the keyboard input and do things like alt-tabbing. To get the old behaviour set 'in_grabkeyboard' to 1 (Exception: Full screen console will now ungrab, previously only windowed console would ungrab.). - In order to get the above behaviour to work in full screen mode, full screen mode initialisation has been modified so that it asks the window manager to handle the window as a full screen window. To get the old behaviour set 'v_nowmfullscreen' to 1 (this will force it to grab the keyboard input too, regardless of 'in_grabkeyboard' or other conditions). I've only tested it on Linux Mint 17.2, 64 bit. | ||||
Tags | No tags attached. | ||||
Attached Files | tdm-linux-window-manager-key-fix.diff (6,660 bytes)
Index: sys/linux/glimp.cpp =================================================================== --- sys/linux/glimp.cpp (revision 6521) +++ sys/linux/glimp.cpp (working copy) @@ -25,12 +25,20 @@ #include <fcntl.h> #include <unistd.h> +#include <X11/Xatom.h> + extern "C" { # include "libXNVCtrl/NVCtrlLib.h" } idCVar sys_videoRam( "sys_videoRam", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Texture memory on the video card (in megabytes) - 0: autodetect", 0, 512 ); +/** +If set and full screen is used, it will NOT notify the window manager that the window is full screen. It will tell X to manage the window directly and it will grab inputs for mouse and keyboard for itself. This will only check at screen initialization time. +This option is useful when the window manager doesn't understand, respect or play nicely with a full screen request. + */ +idCVar v_nowmfullscreen( "v_nowmfullscreen", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "Do not use the window manager for fullscreen. If this is set and full screen is used, it will not notify the window manager and will monopolize both mouse and keyboard inputs. Only used at screen initialization."); + Display *dpy = NULL; static int scrnum = 0; @@ -47,6 +55,11 @@ static int num_vidmodes; static bool vidmode_active = false; +/** Set true if fullscreen mode was set without asking the window manager to treat this window as full screen. +\see {v_nowmfullscreen} +*/ +bool vidmode_nowmfullscreen = false; + // backup gamma ramp static int save_rampsize = 0; static unsigned short *save_red, *save_green, *save_blue; @@ -476,13 +489,23 @@ attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); attr.event_mask = X_MASK; if (vidmode_active) { - mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | - CWEventMask | CWOverrideRedirect; - attr.override_redirect = True; + if(v_nowmfullscreen.GetBool()) { + /*We're not going to cooperate with any window managers, so the window will grab full control.*/ + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWOverrideRedirect | CWBorderPixel; + vidmode_nowmfullscreen = true; + attr.override_redirect = True; + } else { + /*Create a normal window and later inform the window manager that this is fullscreen.*/ + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWBorderPixel; + vidmode_nowmfullscreen = false; + } attr.backing_store = NotUseful; attr.save_under = False; } else { mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + vidmode_nowmfullscreen = false; } win = XCreateWindow(dpy, root, 0, 0, @@ -490,6 +513,16 @@ 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); + if(vidmode_active && !vidmode_nowmfullscreen) { + /*Tell the window manager that this is a full screen window.*/ + const Atom atoms[2] = { XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False), None }; + XChangeProperty( + dpy, + win, + XInternAtom(dpy, "_NET_WM_STATE", False), + XA_ATOM, 32, PropModeReplace, (const unsigned char *)atoms, 1 + ); + } XStoreName(dpy, win, GAME_NAME); // don't let the window be resized Index: sys/linux/input.cpp =================================================================== --- sys/linux/input.cpp (revision 6521) +++ sys/linux/input.cpp (working copy) @@ -25,7 +25,13 @@ idCVar in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" ); idCVar in_dgamouse( "in_dgamouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" ); idCVar in_nograb( "in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "" ); +/** +When set, it grabs X's keyboard input so that it's exclusively handled by the game. Also while set, it prevents the window manager from handling keyboard input, which means that any fancy key combinations handlinded by the window manager can't be handled, preventing common functions like: alt-tabbing, work space switching, volume changing, etc. +Note: Even if set, grabbing of the keyboard is disabled under certain conditions, such as when the console is open. +*/ +idCVar in_grabkeyboard( "in_grabkeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "When set, the keyboard is grabbed, so input goes exclusively to this game. When cleared the window manager is allowed to handle input from the keyboard." ); + // have a working xkb extension static bool have_xkb = false; @@ -32,6 +38,9 @@ // toggled by grab calls - decides if we ignore MotionNotify events static bool mouse_active = false; +///Flags if the keyboard has been already been grabbed. +static bool keyboard_grabbed = false; + // non-DGA pointer-warping mouse input static int mwx, mwy; static int mx = 0, my = 0; @@ -171,12 +180,16 @@ mwy = glConfig.vidHeight / 2; mx = my = 0; } + + /*Grab the keyboard if we're configured to grab it, or if we're doing full screen without the window manager.*/ + if((vidmode_nowmfullscreen || in_grabkeyboard.GetBool()) && !keyboard_grabbed) { + keyboard_grabbed = true; + XGrabKeyboard( dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime ); + } - XGrabKeyboard( dpy, win, - False, - GrabModeAsync, GrabModeAsync, - CurrentTime ); - XSync( dpy, False ); mouse_active = true; @@ -196,7 +209,10 @@ mouse_accel_denominator, mouse_threshold ); XUngrabPointer( dpy, CurrentTime ); - XUngrabKeyboard( dpy, CurrentTime ); + if(keyboard_grabbed) { + XUngrabKeyboard( dpy, CurrentTime ); + keyboard_grabbed = false; + } XWarpPointer( dpy, None, win, 0, 0, 0, 0, @@ -219,6 +235,18 @@ #endif return; } + /*Check if the keyboard should be grabbed or ungrabbed.*/ + if(!vidmode_nowmfullscreen && keyboard_grabbed && (!grabIt || !in_grabkeyboard.GetBool())) { + XUngrabKeyboard( dpy, CurrentTime ); + keyboard_grabbed = false; + } else if((vidmode_nowmfullscreen || (grabIt && in_grabkeyboard.GetBool())) && !keyboard_grabbed) { + /*Grab the keyboard if we're configured to grab it and set to grab, or if we're doing full screen without the window manager.*/ + keyboard_grabbed = true; + XGrabKeyboard( dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime ); + } if ( glConfig.isFullscreen ) { if ( !grabIt ) { Index: sys/linux/local.h =================================================================== --- sys/linux/local.h (revision 6521) +++ sys/linux/local.h (working copy) @@ -34,6 +34,8 @@ extern Display *dpy; extern Window win; +extern bool vidmode_nowmfullscreen; + // input.cpp extern bool dga_found; void Sys_XEvents(); tdm-linux-window-manager-key-fix-v2.diff (8,523 bytes)
Index: sys/linux/glimp.cpp =================================================================== --- sys/linux/glimp.cpp (revision 6527) +++ sys/linux/glimp.cpp (working copy) @@ -25,12 +25,20 @@ #include <fcntl.h> #include <unistd.h> +#include <X11/Xatom.h> + extern "C" { # include "libXNVCtrl/NVCtrlLib.h" } idCVar sys_videoRam( "sys_videoRam", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Texture memory on the video card (in megabytes) - 0: autodetect", 0, 512 ); +/** +If set and full screen is used, it will NOT notify the window manager that the window is full screen. It will tell X to manage the window directly and it will grab inputs for mouse and keyboard for itself. This will only check at screen initialization time. +This option is useful when the window manager doesn't understand, respect or play nicely with a full screen request. + */ +idCVar v_nowmfullscreen( "v_nowmfullscreen", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "Do not use the window manager for fullscreen. If this is set and full screen is used, it will not notify the window manager and will monopolize both mouse and keyboard inputs. Only used at screen initialization."); + Display *dpy = NULL; static int scrnum = 0; @@ -47,6 +55,11 @@ static int num_vidmodes; static bool vidmode_active = false; +/** Set true if fullscreen mode was set without asking the window manager to treat this window as full screen. +\see {v_nowmfullscreen} +*/ +bool vidmode_nowmfullscreen = false; + // backup gamma ramp static int save_rampsize = 0; static unsigned short *save_red, *save_green, *save_blue; @@ -476,13 +489,23 @@ attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); attr.event_mask = X_MASK; if (vidmode_active) { - mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | - CWEventMask | CWOverrideRedirect; - attr.override_redirect = True; + if(v_nowmfullscreen.GetBool()) { + /*We're not going to cooperate with any window managers, so the window will grab full control.*/ + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWOverrideRedirect | CWBorderPixel; + vidmode_nowmfullscreen = true; + attr.override_redirect = True; + } else { + /*Create a normal window and later inform the window manager that this is fullscreen.*/ + mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore | + CWEventMask | CWBorderPixel; + vidmode_nowmfullscreen = false; + } attr.backing_store = NotUseful; attr.save_under = False; } else { mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + vidmode_nowmfullscreen = false; } win = XCreateWindow(dpy, root, 0, 0, @@ -490,6 +513,16 @@ 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); + if(vidmode_active && !vidmode_nowmfullscreen) { + /*Tell the window manager that this is a full screen window.*/ + const Atom atoms[2] = { XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False), None }; + XChangeProperty( + dpy, + win, + XInternAtom(dpy, "_NET_WM_STATE", False), + XA_ATOM, 32, PropModeReplace, (const unsigned char *)atoms, 1 + ); + } XStoreName(dpy, win, GAME_NAME); // don't let the window be resized Index: sys/linux/input.cpp =================================================================== --- sys/linux/input.cpp (revision 6527) +++ sys/linux/input.cpp (working copy) @@ -19,6 +19,7 @@ #include "../../idlib/precompiled.h" #include "../posix/posix_public.h" #include "local.h" +#include "framework/KeyInput.h" #include <pthread.h> @@ -25,7 +26,17 @@ idCVar in_mouse( "in_mouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" ); idCVar in_dgamouse( "in_dgamouse", "1", CVAR_SYSTEM | CVAR_ARCHIVE, "" ); idCVar in_nograb( "in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "" ); +/** +When set, it grabs X's keyboard input so that it's exclusively handled by the game. Also while set, it prevents the window manager from handling keyboard input, which means that any fancy key combinations handlinded by the window manager can't be handled, preventing common functions like: alt-tabbing, work space switching, volume changing, etc. +Note: Even if set, grabbing of the keyboard is disabled under certain conditions, such as when the console is open. +*/ +idCVar in_grabkeyboard( "in_grabkeyboard", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "When set, the keyboard is grabbed, so input goes exclusively to this game. When cleared the window manager is allowed to handle input from the keyboard." ); +/** +When set, it grabs X's mouse input so that it's exclusively handled by the game. This will ensure the mouse is trapped to this winow. + */ +idCVar in_grabmouse( "in_grabmouse", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_NOCHEAT, "When set, the mouse is grabbed, so input goes exclusively to this game." ); + // have a working xkb extension static bool have_xkb = false; @@ -32,6 +43,11 @@ // toggled by grab calls - decides if we ignore MotionNotify events static bool mouse_active = false; +///Flags if the keyboard has been already been grabbed. +static bool keyboard_grabbed = false; +///Flags if the mouse has been already been grabbed. +static bool mouse_grabbed = false; + // non-DGA pointer-warping mouse input static int mwx, mwy; static int mx = 0, my = 0; @@ -138,15 +154,19 @@ XSync( dpy, False ); XDefineCursor( dpy, win, Sys_XCreateNullCursor( dpy, win ) ); + - XGrabPointer( dpy, win, - False, - MOUSE_MASK, - GrabModeAsync, GrabModeAsync, - win, - None, - CurrentTime ); - + if((vidmode_nowmfullscreen || in_grabmouse.GetBool()) && !mouse_grabbed) { + mouse_grabbed = true; + XGrabPointer( dpy, win, + False, + MOUSE_MASK, + GrabModeAsync, GrabModeAsync, + win, + None, + CurrentTime ); + } + XGetPointerControl( dpy, &mouse_accel_numerator, &mouse_accel_denominator, &mouse_threshold ); @@ -171,12 +191,16 @@ mwy = glConfig.vidHeight / 2; mx = my = 0; } + + /*Grab the keyboard if we're configured to grab it, or if we're doing full screen without the window manager.*/ + if((vidmode_nowmfullscreen || in_grabkeyboard.GetBool()) && !keyboard_grabbed) { + keyboard_grabbed = true; + XGrabKeyboard( dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime ); + } - XGrabKeyboard( dpy, win, - False, - GrabModeAsync, GrabModeAsync, - CurrentTime ); - XSync( dpy, False ); mouse_active = true; @@ -195,8 +219,14 @@ XChangePointerControl( dpy, true, true, mouse_accel_numerator, mouse_accel_denominator, mouse_threshold ); - XUngrabPointer( dpy, CurrentTime ); - XUngrabKeyboard( dpy, CurrentTime ); + if(mouse_grabbed) { + XUngrabPointer( dpy, CurrentTime ); + mouse_grabbed = false; + } + if(keyboard_grabbed) { + XUngrabKeyboard( dpy, CurrentTime ); + keyboard_grabbed = false; + } XWarpPointer( dpy, None, win, 0, 0, 0, 0, @@ -219,6 +249,33 @@ #endif return; } + /*Check if the keyboard should be grabbed or ungrabbed.*/ + if(!vidmode_nowmfullscreen && keyboard_grabbed && (!grabIt || !in_grabkeyboard.GetBool())) { + XUngrabKeyboard( dpy, CurrentTime ); + keyboard_grabbed = false; + } else if((vidmode_nowmfullscreen || (grabIt && in_grabkeyboard.GetBool())) && !keyboard_grabbed) { + /*Grab the keyboard if we're configured to grab it and set to grab, or if we're doing full screen without the window manager.*/ + keyboard_grabbed = true; + XGrabKeyboard( dpy, win, + False, + GrabModeAsync, GrabModeAsync, + CurrentTime ); + } + + /*Check if the mouse should be grabbed or ungrabbed.*/ + if(!vidmode_nowmfullscreen && mouse_grabbed && (!grabIt || !in_grabmouse.GetBool())) { + XUngrabPointer( dpy, CurrentTime ); + mouse_grabbed = false; + } else if((vidmode_nowmfullscreen || (grabIt && in_grabmouse.GetBool())) && !mouse_grabbed) { + mouse_grabbed = true; + XGrabPointer( dpy, win, + False, + MOUSE_MASK, + GrabModeAsync, GrabModeAsync, + win, + None, + CurrentTime ); + } if ( glConfig.isFullscreen ) { if ( !grabIt ) { Index: sys/linux/local.h =================================================================== --- sys/linux/local.h (revision 6527) +++ sys/linux/local.h (working copy) @@ -34,6 +34,8 @@ extern Display *dpy; extern Window win; +extern bool vidmode_nowmfullscreen; + // input.cpp extern bool dga_found; void Sys_XEvents(); | ||||
parent of | 0005293 | resolved | cabalistic | Mouse cursor is moved to the screen center when alt-tab switching (Linux) |
related to | 0003568 | resolved | duzenko | fullscreen windowed option |
related to | 0004288 | resolved | Minimizing TDM |
I've uploaded version 2 of the fix. This version adds the CVar 'in_grabmouse' to handle the mouse the same way the keyboard is handled. This will fix the bug where the mouse will be locked to the window until workspaces are switched. The proper fix to the bug is detect focus loss and gain, and ungrab and grab respectively, however, I haven't had time to investigate that solution properly. That said, this fix should work good enough for the bug, although it may have problems if the mouse moves fast enough to escape the window before being moved back to the center. |
|
I would like this please! I often alt + tab from fullscreen games to other windows on my system, but TDM doesn't currently allow this which is an annoyance. +1 to the patch. | |
Well... As I said, I cannot see how this patch affects a native Linux, since I have only a VM, and TDM works very weird in VM. So I'm afraid I won't be able to accept this patch (I cannot test it properly). |
|
I've pinged the Linux crew but thus far no takers. This may be moved to 2.07 | |
I only work in Windows, so I can't test it. Since it's been 3 months since NB's post, with no response from anyone who can test the patch, I'm moving it to 2.07. |
|
If there are no takers, assign it to me. I should be able to squeeze in some Linux dev time somewhere. | |
Assigned to you. If you integrate this feature and make sure it works well, it would be great! This is a long-awaited fix which most developers can't handle. |
|
Ping? Would this be possible before 2.07 beta is over? |
|
I'll try to get some Linux coding time in over the holidays. | |
I applied the patch and gave it a spin. Keyboard handling works fine as advertised, including alt-tabbing. The mouse test is a bit inconclusive. For me, the mouse was still kept trapped in the window, but the WM did allow me to move it when holding ALT+TAB, so during that hold I can move the mouse outside and then use other windows. Not ideal, but might be WM-specific, and it's usable. Since I got no clue how to improve it further, I committed it. | |
Just one minor rumble. It would have been better it you commit this change to trunk and discuss including it into release on forums. The "target version" setting is followed only until beta starts, later all except minor changes should be discussed. I'll built new Linux version and test it. I found that if I disable "mouse integration", then mouse works normally inside VM =) |
|
Sorry. nbohr specifically pinged me to include it into 2.07, and the change is relatively minor, so I didn't really think about it :-/ | |
For me in VirtualBox it looks like this: 1) Windowed mode has not changed: when TDM is active, everything is normal. When I Alt+Tab, the window borders disappear, but rendered viewport is still on top. It may or may not be VM-specific. 2) I did not use fullscreen mode before. With the change it seems to work, and I can Alt+Tab: then TDM becomes invisible. 3) After running TDM my mouse cursor disappears. Maybe this was happening before change too --- not sure. 4) With the change mouselook is disabled when I Alt+Tab. When I alt+tab back, mouse movements move player forward/backward/sideways instead of rotating camera. This continues until I press left Alt+Shift, after that it seems to work. I suppose these are Ubuntu hotkeys, which are no longer suppressed. Overally, I don't have positive impression from the change =) It is still clumsy, and far behind what Windows build does. Maybe it's VM. |
|
1) works fine on my Linux laptop. 3) not happening for me. 4) yes, I faced the same. I think the issue is that the game does register the ALT key press, but does not see the ALT release event because it loses focus in-between. So when you switch back to the game, it assumes that the ALT key is held and thus moves the player when moving the mouse. It was enough for me to press ALT key again. So yeah, not ideal. Linux is fantastic for work (at least certain kinds), but terrible as a multimedia/gaming OS. |
|
Date Modified | Username | Field | Change |
---|---|---|---|
30.07.2015 15:55 | NagaHuntress | New Issue | |
30.07.2015 15:55 | NagaHuntress | File Added: tdm-linux-window-manager-key-fix.diff | |
30.07.2015 16:48 | SteveL | Assigned To | => SteveL |
30.07.2015 16:48 | SteveL | Status | new => assigned |
30.07.2015 16:48 | SteveL | Status | assigned => acknowledged |
11.08.2015 09:49 | NagaHuntress | File Added: tdm-linux-window-manager-key-fix-v2.diff | |
11.08.2015 09:55 | NagaHuntress | Note Added: 0007712 | |
27.09.2015 00:23 | MirceaKitsune | Note Added: 0007804 | |
07.10.2016 04:21 | nbohr1more | Relationship added | related to 0003568 |
22.11.2016 18:43 | nbohr1more | Relationship added | related to 0004288 |
15.02.2017 04:37 | grayman | Assigned To | SteveL => |
15.02.2017 04:37 | grayman | Status | acknowledged => new |
21.09.2017 17:10 | nbohr1more | Assigned To | => stgatilov |
21.09.2017 17:10 | nbohr1more | Status | new => assigned |
21.09.2017 17:10 | nbohr1more | Product Version | => SVN |
21.09.2017 17:10 | nbohr1more | Target Version | => TDM 2.06 |
21.09.2017 17:18 | stgatilov | Assigned To | stgatilov => |
21.09.2017 17:19 | stgatilov | Note Added: 0009323 | |
21.09.2017 17:46 | nbohr1more | Note Added: 0009324 | |
21.09.2017 19:04 | nbohr1more | Status | assigned => confirmed |
14.12.2017 14:59 | grayman | Note Added: 0009783 | |
14.12.2017 14:59 | grayman | Target Version | TDM 2.06 => TDM 2.07 |
09.06.2018 18:49 | cabalistic | Note Added: 0010510 | |
10.06.2018 13:20 | stgatilov | Assigned To | => cabalistic |
10.06.2018 13:20 | stgatilov | Status | confirmed => assigned |
10.06.2018 13:25 | stgatilov | Note Added: 0010513 | |
19.12.2018 18:00 | nbohr1more | Note Added: 0010975 | |
19.12.2018 18:36 | cabalistic | Note Added: 0010979 | |
26.12.2018 08:07 | cabalistic | Note Added: 0011129 | |
26.12.2018 08:08 | cabalistic | Status | assigned => resolved |
26.12.2018 08:08 | cabalistic | Fixed in Version | => TDM 2.07 |
26.12.2018 08:08 | cabalistic | Resolution | open => fixed |
26.12.2018 14:04 | stgatilov | Note Added: 0011131 | |
26.12.2018 15:10 | cabalistic | Note Added: 0011132 | |
27.12.2018 16:49 | stgatilov | Note Added: 0011147 | |
27.12.2018 16:50 | stgatilov | Note Edited: 0011147 | |
27.12.2018 17:33 | cabalistic | Note Added: 0011149 | |
04.01.2021 05:06 | nbohr1more | Relationship added | parent of 0005293 |