Add support for system key capture in windowed mode on macOS

This commit is contained in:
Cameron Gutman 2021-02-26 21:43:47 -06:00
parent 28ecc6bcbf
commit b27ca993aa
6 changed files with 61 additions and 23 deletions

View File

@ -68,12 +68,6 @@ SystemProperties::SystemProperties()
hasDiscordIntegration = false; hasDiscordIntegration = false;
#endif #endif
#ifdef Q_OS_DARWIN
supportsWindowedSystemKeyCapture = false;
#else
supportsWindowedSystemKeyCapture = true;
#endif
unmappedGamepads = SdlInputHandler::getUnmappedGamepads(); unmappedGamepads = SdlInputHandler::getUnmappedGamepads();
// Populate data that requires talking to SDL. We do it all in one shot // Populate data that requires talking to SDL. We do it all in one shot

View File

@ -19,7 +19,6 @@ public:
Q_PROPERTY(bool hasWindowManager MEMBER hasWindowManager CONSTANT) Q_PROPERTY(bool hasWindowManager MEMBER hasWindowManager CONSTANT)
Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT) Q_PROPERTY(bool hasBrowser MEMBER hasBrowser CONSTANT)
Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT) Q_PROPERTY(bool hasDiscordIntegration MEMBER hasDiscordIntegration CONSTANT)
Q_PROPERTY(bool supportsWindowedSystemKeyCapture MEMBER supportsWindowedSystemKeyCapture CONSTANT)
Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged) Q_PROPERTY(QString unmappedGamepads MEMBER unmappedGamepads NOTIFY unmappedGamepadsChanged)
Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT) Q_PROPERTY(int maximumStreamingFrameRate MEMBER maximumStreamingFrameRate CONSTANT)
Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT) Q_PROPERTY(QSize maximumResolution MEMBER maximumResolution CONSTANT)
@ -44,7 +43,6 @@ private:
bool hasWindowManager; bool hasWindowManager;
bool hasBrowser; bool hasBrowser;
bool hasDiscordIntegration; bool hasDiscordIntegration;
bool supportsWindowedSystemKeyCapture;
QString unmappedGamepads; QString unmappedGamepads;
int maximumStreamingFrameRate; int maximumStreamingFrameRate;
QSize maximumResolution; QSize maximumResolution;

View File

@ -911,8 +911,7 @@ Flickable {
id: captureSysKeysCheck id: captureSysKeysCheck
hoverEnabled: true hoverEnabled: true
width: parent.width width: parent.width
text: qsTr("Capture system keyboard shortcuts") + (SystemProperties.supportsWindowedSystemKeyCapture ? text: qsTr("Capture system keyboard shortcuts")
"" : qsTr(" while streaming in fullscreen mode"))
font.pointSize: 12 font.pointSize: 12
enabled: SystemProperties.hasWindowManager enabled: SystemProperties.hasWindowManager
checked: StreamingPreferences.captureSysKeys && SystemProperties.hasWindowManager checked: StreamingPreferences.captureSysKeys && SystemProperties.hasWindowManager

View File

@ -171,6 +171,10 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s
} }
#endif #endif
#ifdef Q_OS_DARWIN
CGSGetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), &m_OldHotKeyMode);
#endif
// Initialize the gamepad mask with currently attached gamepads to avoid // Initialize the gamepad mask with currently attached gamepads to avoid
// causing gamepads to unexpectedly disappear and reappear on the host // causing gamepads to unexpectedly disappear and reappear on the host
// during stream startup as we detect currently attached gamepads one at a time. // during stream startup as we detect currently attached gamepads one at a time.
@ -240,6 +244,10 @@ SdlInputHandler::~SdlInputHandler()
SDL_DestroyCond(m_ClipboardHasData); SDL_DestroyCond(m_ClipboardHasData);
SDL_DestroyMutex(m_ClipboardLock); SDL_DestroyMutex(m_ClipboardLock);
#ifdef Q_OS_DARWIN
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), m_OldHotKeyMode);
#endif
#if !SDL_VERSION_ATLEAST(2, 0, 9) #if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_QuitSubSystem(SDL_INIT_HAPTIC); SDL_QuitSubSystem(SDL_INIT_HAPTIC);
SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC)); SDL_assert(!SDL_WasInit(SDL_INIT_HAPTIC));
@ -319,11 +327,28 @@ void SdlInputHandler::notifyFocusLost()
setCaptureActive(false); setCaptureActive(false);
} }
#ifdef Q_OS_DARWIN
if (m_CaptureSystemKeysEnabled) {
// Stop capturing system keys on focus loss
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), m_OldHotKeyMode);
}
#endif
// Raise all keys that are currently pressed. If we don't do this, certain keys // Raise all keys that are currently pressed. If we don't do this, certain keys
// used in shortcuts that cause focus loss (such as Alt+Tab) may get stuck down. // used in shortcuts that cause focus loss (such as Alt+Tab) may get stuck down.
raiseAllKeys(); raiseAllKeys();
} }
void SdlInputHandler::notifyFocusGained()
{
#ifdef Q_OS_DARWIN
if (m_CaptureSystemKeysEnabled) {
// Start capturing system keys again on focus gain
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), CGSGlobalHotKeyDisable);
}
#endif
}
bool SdlInputHandler::isCaptureActive() bool SdlInputHandler::isCaptureActive()
{ {
if (SDL_GetRelativeMouseMode()) { if (SDL_GetRelativeMouseMode()) {
@ -350,10 +375,6 @@ bool SdlInputHandler::isSystemKeyCaptureActive()
&& (windowFlags & SDL_WINDOW_KEYBOARD_GRABBED) && (windowFlags & SDL_WINDOW_KEYBOARD_GRABBED)
#else #else
&& (windowFlags & SDL_WINDOW_INPUT_GRABBED) && (windowFlags & SDL_WINDOW_INPUT_GRABBED)
#endif
#ifdef Q_OS_DARWIN
// Darwin only supports full-screen system key capture
&& (windowFlags & SDL_WINDOW_FULLSCREEN)
#endif #endif
; ;
} }
@ -383,6 +404,10 @@ void SdlInputHandler::setCaptureActive(bool active)
if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN) { if (SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN) {
SDL_SetWindowGrab(m_Window, SDL_TRUE); SDL_SetWindowGrab(m_Window, SDL_TRUE);
} }
#endif
#ifdef Q_OS_DARWIN
// SDL doesn't support this private macOS API
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), CGSGlobalHotKeyDisable);
#endif #endif
} }
@ -440,6 +465,11 @@ void SdlInputHandler::setCaptureActive(bool active)
// Allow the cursor to leave the bounds of our window again. // Allow the cursor to leave the bounds of our window again.
SDL_SetWindowGrab(m_Window, SDL_FALSE); SDL_SetWindowGrab(m_Window, SDL_FALSE);
#endif #endif
#ifdef Q_OS_DARWIN
// SDL doesn't support this private macOS API
CGSSetGlobalHotKeyOperatingMode(_CGSDefaultConnection(), m_OldHotKeyMode);
#endif
} }
} }

View File

@ -29,6 +29,25 @@ struct GamepadState {
unsigned char lt, rt; unsigned char lt, rt;
}; };
#ifdef Q_OS_DARWIN
#include <CoreGraphics/CGError.h>
extern "C" {
typedef int CGSConnection;
typedef enum {
CGSGlobalHotKeyEnable = 0,
CGSGlobalHotKeyDisable = 1,
} CGSGlobalHotKeyOperatingMode;
extern CGSConnection _CGSDefaultConnection(void);
extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection,
CGSGlobalHotKeyOperatingMode* mode);
extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection,
CGSGlobalHotKeyOperatingMode mode);
}
#endif
#define MAX_GAMEPADS 4 #define MAX_GAMEPADS 4
#define MAX_FINGERS 2 #define MAX_FINGERS 2
@ -79,6 +98,8 @@ public:
void notifyFocusLost(); void notifyFocusLost();
void notifyFocusGained();
bool isCaptureActive(); bool isCaptureActive();
bool isSystemKeyCaptureActive(); bool isSystemKeyCaptureActive();
@ -167,6 +188,10 @@ private:
bool m_CaptureSystemKeysEnabled; bool m_CaptureSystemKeysEnabled;
int m_MouseCursorCapturedVisibilityState; int m_MouseCursorCapturedVisibilityState;
#ifdef Q_OS_DARWIN
CGSGlobalHotKeyOperatingMode m_OldHotKeyMode;
#endif
struct { struct {
KeyCombo keyCombo; KeyCombo keyCombo;
SDL_Keycode keyCode; SDL_Keycode keyCode;

View File

@ -522,15 +522,6 @@ bool Session::initialize()
break; break;
} }
#ifdef Q_OS_DARWIN
// macOS behaves as if we're capturing system keys when
// we enter "real" full-screen mode, so use standard
// full-screen as our full-screen flag when capturing keys.
if (m_Preferences->captureSysKeys) {
m_FullScreenFlag = SDL_WINDOW_FULLSCREEN;
}
#endif
#if !SDL_VERSION_ATLEAST(2, 0, 11) #if !SDL_VERSION_ATLEAST(2, 0, 11)
// HACK: Using a full-screen window breaks mouse capture on the Pi's LXDE // HACK: Using a full-screen window breaks mouse capture on the Pi's LXDE
// GUI environment. Force the session to use windowed mode (which won't // GUI environment. Force the session to use windowed mode (which won't
@ -1345,6 +1336,7 @@ void Session::exec(int displayOriginX, int displayOriginY)
if (m_Preferences->muteOnFocusLoss) { if (m_Preferences->muteOnFocusLoss) {
m_AudioMuted = false; m_AudioMuted = false;
} }
m_InputHandler->notifyFocusGained();
break; break;
case SDL_WINDOWEVENT_LEAVE: case SDL_WINDOWEVENT_LEAVE:
m_InputHandler->notifyMouseLeave(); m_InputHandler->notifyMouseLeave();