[client] major restructure of platform specific code

This commit is contained in:
Geoffrey McRae
2021-01-15 20:30:03 +11:00
parent 062c4e32cb
commit 27a38294ea
23 changed files with 2120 additions and 2078 deletions

View File

@@ -36,10 +36,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <assert.h>
#include <stdatomic.h>
#if SDL_VIDEO_DRIVER_X11_XINPUT2
#include <X11/extensions/XInput2.h>
#endif
#include "common/debug.h"
#include "common/crash.h"
#include "common/KVMFR.h"
@@ -54,7 +50,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "utils.h"
#include "kb.h"
#include "ll.h"
#include "wm.h"
#define RESIZE_TIMEOUT (10 * 1000) // 10ms
@@ -71,7 +66,6 @@ static LGThread *t_cursor = NULL;
static LGThread *t_frame = NULL;
static SDL_Cursor *cursor = NULL;
static int g_XInputOp; // XInput Opcode
static Uint32 e_SDLEvent; // our SDL event
enum
@@ -101,12 +95,43 @@ static void lgInit(void)
g_cursor.guest.valid = false;
}
static bool inputEnabled(void)
bool app_getProp(LG_DSProperty prop, void * ret)
{
return g_state.ds->getProp(prop, ret);
}
SDL_Window * app_getWindow(void)
{
return g_state.window;
}
bool app_inputEnabled(void)
{
return params.useSpiceInput && !g_state.ignoreInput &&
((g_cursor.grab && params.captureInputOnly) || !params.captureInputOnly);
}
bool app_cursorInWindow(void)
{
return g_cursor.inWindow;
}
bool app_cursorIsGrabbed(void)
{
return g_cursor.grab;
}
bool app_cursorWantsRaw(void)
{
return params.rawMouse;
}
void app_updateCursorPos(double x, double y)
{
g_cursor.pos.x = x;
g_cursor.pos.y = y;
}
static void alignToGuest(void)
{
if (SDL_HasEvent(e_SDLEvent))
@@ -398,7 +423,7 @@ static int cursorThread(void * unused)
g_cursor.guest.valid = true;
// if the state just became valid
if (valid != true && inputEnabled())
if (valid != true && app_inputEnabled())
alignToGuest();
}
@@ -712,7 +737,7 @@ static SpiceDataType clipboard_type_to_spice_type(const LG_ClipboardData type)
}
}
void clipboardRelease(void)
void app_clipboardRelease(void)
{
if (!params.clipboardToVM)
return;
@@ -720,7 +745,7 @@ void clipboardRelease(void)
spice_clipboard_release();
}
void clipboardNotify(const LG_ClipboardData type, size_t size)
void app_clipboardNotify(const LG_ClipboardData type, size_t size)
{
if (!params.clipboardToVM)
return;
@@ -741,7 +766,7 @@ void clipboardNotify(const LG_ClipboardData type, size_t size)
spice_clipboard_data_start(g_state.cbType, size);
}
void clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size)
void app_clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size)
{
if (!params.clipboardToVM)
return;
@@ -759,7 +784,7 @@ void clipboardData(const LG_ClipboardData type, uint8_t * data, size_t size)
g_state.cbXfer -= size;
}
void clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque)
void app_clipboardRequest(const LG_ClipboardReplyFn replyFn, void * opaque)
{
if (!params.clipboardToLocal)
return;
@@ -779,11 +804,11 @@ void spiceClipboardNotice(const SpiceDataType type)
if (!params.clipboardToLocal)
return;
if (!g_state.lgc || !g_state.lgc->notice)
if (!g_state.cbAvailable)
return;
g_state.cbType = type;
g_state.lgc->notice(clipboardRequest, spice_type_to_clipboard_type(type));
g_state.ds->cbNotice(spice_type_to_clipboard_type(type));
}
void spiceClipboardData(const SpiceDataType type, uint8_t * buffer, uint32_t size)
@@ -822,8 +847,8 @@ void spiceClipboardRelease(void)
if (!params.clipboardToLocal)
return;
if (g_state.lgc && g_state.lgc->release)
g_state.lgc->release();
if (g_state.cbAvailable)
g_state.ds->cbRelease();
}
void spiceClipboardRequest(const SpiceDataType type)
@@ -831,22 +856,22 @@ void spiceClipboardRequest(const SpiceDataType type)
if (!params.clipboardToVM)
return;
if (g_state.lgc && g_state.lgc->request)
g_state.lgc->request(spice_type_to_clipboard_type(type));
if (g_state.cbAvailable)
g_state.ds->cbRequest(spice_type_to_clipboard_type(type));
}
static void warpMouse(int x, int y, bool disable)
static void warpMouse(int x, int y, bool exiting)
{
if (g_cursor.warpState == WARP_STATE_OFF)
return;
if (disable)
if (exiting)
g_cursor.warpState = WARP_STATE_OFF;
if (g_cursor.pos.x == x && g_cursor.pos.y == y)
return;
wmWarpMouse(x, y);
g_state.ds->warpMouse(x, y, exiting);
}
static bool isValidCursorLocation(int x, int y)
@@ -883,7 +908,7 @@ static void cursorToInt(double ex, double ey, int *x, int *y)
*y = (int)ey;
}
void handleMouseGrabbed(double ex, double ey)
void app_handleMouseGrabbed(double ex, double ey)
{
int x, y;
@@ -918,7 +943,7 @@ static void guestCurToLocal(struct DoublePoint *local)
// warp support. Instead, we attempt a best-effort emulation which works with a
// 1:1 mouse movement patch applied in the guest. For anything fancy, use
// capture mode.
static void handleMouseWayland(void)
static void app_handleMouseWayland(void)
{
if (g_cursor.guest.dpiScale == 0)
return;
@@ -940,19 +965,19 @@ static void handleMouseWayland(void)
DEBUG_ERROR("failed to send mouse motion message");
}
void handleMouseNormal(double ex, double ey)
void app_handleMouseNormal(double ex, double ey)
{
/* if we don't have the current cursor pos just send cursor movements */
if (!g_cursor.guest.valid)
{
if (g_cursor.grab)
handleMouseGrabbed(ex, ey);
app_handleMouseGrabbed(ex, ey);
return;
}
if (g_state.wminfo.subsystem == SDL_SYSWM_WAYLAND)
{
handleMouseWayland();
app_handleMouseWayland();
return;
}
@@ -987,7 +1012,7 @@ void handleMouseNormal(double ex, double ey)
g_cursor.redraw = true;
g_cursor.warpState = WARP_STATE_ON;
wmGrabPointer();
g_state.ds->grabPointer();
}
struct DoublePoint guest =
@@ -1053,7 +1078,7 @@ void handleMouseNormal(double ex, double ey)
g_cursor.inWindow = false;
/* ungrab the pointer and move the local cursor to the exit point */
wmUngrabPointer();
g_state.ds->ungrabPointer();
warpMouse(tx, ty, true);
return;
}
@@ -1093,7 +1118,13 @@ void handleMouseNormal(double ex, double ey)
DEBUG_ERROR("failed to send mouse motion message");
}
static void handleResizeEvent(unsigned int w, unsigned int h)
void app_updateWindowPos(int x, int y)
{
g_state.windowPos.x = x;
g_state.windowPos.y = y;
}
void app_handleResizeEvent(int w, int h)
{
SDL_GetWindowBordersSize(g_state.window,
&g_state.border.y,
@@ -1108,7 +1139,7 @@ static void handleResizeEvent(unsigned int w, unsigned int h)
g_state.windowCY = h / 2;
updatePositionInfo();
if (inputEnabled())
if (app_inputEnabled())
{
/* if the window is moved/resized causing a loss of focus while grabbed, it
* makes it impossible to re-focus the window, so we quietly re-enter
@@ -1122,12 +1153,12 @@ static void handleResizeEvent(unsigned int w, unsigned int h)
}
}
static void handleWindowLeave(void)
void app_handleWindowLeave(void)
{
g_cursor.inWindow = false;
g_cursor.inView = false;
if (!inputEnabled())
if (!app_inputEnabled())
return;
if (!params.alwaysShowCursor)
@@ -1136,10 +1167,10 @@ static void handleWindowLeave(void)
g_cursor.redraw = true;
}
static void handleWindowEnter(void)
void app_handleWindowEnter(void)
{
g_cursor.inWindow = true;
if (!inputEnabled())
if (!app_inputEnabled())
return;
g_cursor.draw = true;
@@ -1171,20 +1202,20 @@ static void setGrabQuiet(bool enable)
if (enable)
{
wmGrabPointer();
g_state.ds->grabPointer();
if (params.grabKeyboard)
wmGrabKeyboard();
g_state.ds->grabKeyboard();
}
else
{
if (params.grabKeyboard)
{
if (!g_state.focused || !params.grabKeyboardOnFocus)
wmUngrabKeyboard();
g_state.ds->ungrabKeyboard();
}
wmUngrabPointer();
g_state.ds->ungrabPointer();
}
// if exiting capture when input on capture only, we want to show the cursor
@@ -1197,6 +1228,9 @@ static void setGrabQuiet(bool enable)
int eventFilter(void * userdata, SDL_Event * event)
{
if (g_state.ds->eventFilter(event))
return 0;
if (event->type == e_SDLEvent)
{
switch(event->user.code)
@@ -1232,55 +1266,38 @@ int eventFilter(void * userdata, SDL_Event * event)
switch(event->window.event)
{
case SDL_WINDOWEVENT_ENTER:
if (g_state.wminfo.subsystem == SDL_SYSWM_WAYLAND)
g_cursor.inView = true;
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
handleWindowEnter();
app_handleWindowEnter();
break;
case SDL_WINDOWEVENT_LEAVE:
if (g_state.wminfo.subsystem == SDL_SYSWM_WAYLAND)
g_cursor.inView = false;
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
handleWindowLeave();
app_handleWindowLeave();
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
{
g_state.focused = true;
g_state.focused = true;
if (!inputEnabled())
break;
if (params.grabKeyboardOnFocus)
wmGrabKeyboard();
}
if (!app_inputEnabled())
break;
if (params.grabKeyboardOnFocus)
g_state.ds->grabKeyboard();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
{
g_state.focused = false;
g_state.focused = false;
if (!inputEnabled())
break;
if (params.grabKeyboardOnFocus)
wmUngrabKeyboard();
}
if (!app_inputEnabled())
break;
if (params.grabKeyboardOnFocus)
g_state.ds->ungrabKeyboard();
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
case SDL_WINDOWEVENT_RESIZED:
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
handleResizeEvent(event->window.data1, event->window.data2);
app_handleResizeEvent(event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_MOVED:
if (g_state.wminfo.subsystem != SDL_SYSWM_X11)
{
g_state.windowPos.x = event->window.data1;
g_state.windowPos.y = event->window.data2;
}
app_updateWindowPos(event->window.data1, event->window.data2);
break;
case SDL_WINDOWEVENT_CLOSE:
@@ -1291,201 +1308,8 @@ int eventFilter(void * userdata, SDL_Event * event)
return 0;
}
case SDL_SYSWMEVENT:
{
// When the window manager forces the window size after calling SDL_SetWindowSize, SDL
// ignores this update and caches the incorrect window size. As such all related details
// are incorect including mouse movement information as it clips to the old window size.
if (g_state.wminfo.subsystem == SDL_SYSWM_X11)
{
XEvent xe = event->syswm.msg->msg.x11.event;
switch(xe.type)
{
case ConfigureNotify:
{
/* the window may have been re-parented so we need to translate to
* ensure we get the screen top left position of the window */
Window child;
XTranslateCoordinates(g_state.wminfo.info.x11.display,
g_state.wminfo.info.x11.window,
DefaultRootWindow(g_state.wminfo.info.x11.display),
0, 0, &g_state.windowPos.x, &g_state.windowPos.y,
&child);
handleResizeEvent(xe.xconfigure.width, xe.xconfigure.height);
break;
}
#if SDL_VIDEO_DRIVER_X11_XINPUT2
/* support movements via XInput2 */
case GenericEvent:
{
if (!inputEnabled())
break;
XGenericEventCookie *cookie = (XGenericEventCookie*)&xe.xcookie;
if (cookie->extension != g_XInputOp)
break;
switch(cookie->evtype)
{
case XI_Motion:
{
if (!g_cursor.inWindow)
break;
XIDeviceEvent *device = cookie->data;
g_cursor.pos.x = device->event_x;
g_cursor.pos.y = device->event_y;
break;
}
case XI_RawMotion:
{
if (!g_cursor.inWindow)
break;
XIRawEvent *raw = cookie->data;
double raw_axis[2];
double axis[2];
/* select the active validators for the X & Y axis */
double *valuator = raw->valuators.values;
double *r_value = raw->raw_values;
int count = 0;
for(int i = 0; i < raw->valuators.mask_len * 8; ++i)
{
if (XIMaskIsSet(raw->valuators.mask, i))
{
raw_axis[count] = *r_value;
axis [count] = *valuator;
++count;
if (count == 2)
break;
++valuator;
++r_value;
}
}
/* filter out scroll wheel and other events */
if (count < 2)
break;
/* filter out duplicate events */
static Time prev_time = 0;
static double prev_axis[2] = {0};
if (raw->time == prev_time &&
axis[0] == prev_axis[0] &&
axis[1] == prev_axis[1])
break;
prev_time = raw->time;
prev_axis[0] = axis[0];
prev_axis[1] = axis[1];
if (g_cursor.grab)
{
if (params.rawMouse)
handleMouseGrabbed(raw_axis[0], raw_axis[1]);
else
handleMouseGrabbed(axis[0], axis[1]);
}
else
if (g_cursor.inWindow)
handleMouseNormal(axis[0], axis[1]);
break;
}
}
break;
}
#endif
case EnterNotify:
{
int x, y;
Window child;
XTranslateCoordinates(g_state.wminfo.info.x11.display,
DefaultRootWindow(g_state.wminfo.info.x11.display),
g_state.wminfo.info.x11.window,
xe.xcrossing.x_root, xe.xcrossing.y_root,
&x, &y,
&child);
g_cursor.pos.x = x;
g_cursor.pos.y = y;
handleWindowEnter();
break;
}
case LeaveNotify:
{
if (xe.xcrossing.mode != NotifyNormal)
break;
int x, y;
Window child;
XTranslateCoordinates(g_state.wminfo.info.x11.display,
DefaultRootWindow(g_state.wminfo.info.x11.display),
g_state.wminfo.info.x11.window,
xe.xcrossing.x_root, xe.xcrossing.y_root,
&x, &y,
&child);
g_cursor.pos.x = x;
g_cursor.pos.y = y;
handleWindowLeave();
break;
}
case FocusIn:
g_state.focused = true;
if (!inputEnabled())
break;
if (xe.xfocus.mode == NotifyNormal ||
xe.xfocus.mode == NotifyUngrab)
{
if (params.grabKeyboardOnFocus)
wmGrabKeyboard();
}
break;
case FocusOut:
g_state.focused = false;
if (!inputEnabled())
break;
if (xe.xfocus.mode == NotifyNormal ||
xe.xfocus.mode == NotifyWhileGrabbed)
{
if (g_cursor.grab)
setGrab(false);
else
{
if (params.grabKeyboardOnFocus)
wmUngrabKeyboard();
}
}
break;
}
}
if (params.useSpiceClipboard && g_state.lgc && g_state.lgc->wmevent)
g_state.lgc->wmevent(event->syswm.msg);
return 0;
}
case SDL_MOUSEMOTION:
{
if (g_state.wminfo.subsystem == SDL_SYSWM_X11)
break;
g_cursor.pos.x = event->motion.x;
g_cursor.pos.y = event->motion.y;
@@ -1495,10 +1319,10 @@ int eventFilter(void * userdata, SDL_Event * event)
if (g_cursor.grab)
{
if (g_state.wminfo.subsystem != SDL_SYSWM_WAYLAND)
handleMouseGrabbed(event->motion.xrel, event->motion.yrel);
app_handleMouseGrabbed(event->motion.xrel, event->motion.yrel);
}
else
handleMouseNormal(event->motion.xrel, event->motion.yrel);
app_handleMouseNormal(event->motion.xrel, event->motion.yrel);
}
break;
}
@@ -1519,7 +1343,7 @@ int eventFilter(void * userdata, SDL_Event * event)
break;
}
if (!inputEnabled())
if (!app_inputEnabled())
break;
if (params.ignoreWindowsKeys &&
@@ -1568,7 +1392,7 @@ int eventFilter(void * userdata, SDL_Event * event)
g_state.escapeActive = false;
}
if (!inputEnabled())
if (!app_inputEnabled())
break;
// avoid sending key up events when we didn't send a down
@@ -1594,7 +1418,7 @@ int eventFilter(void * userdata, SDL_Event * event)
}
case SDL_MOUSEWHEEL:
if (!inputEnabled() || !g_cursor.inView)
if (!app_inputEnabled() || !g_cursor.inView)
break;
if (
@@ -1609,7 +1433,7 @@ int eventFilter(void * userdata, SDL_Event * event)
case SDL_MOUSEBUTTONDOWN:
{
if (!inputEnabled() || !g_cursor.inView)
if (!app_inputEnabled() || !g_cursor.inView)
break;
int button = event->button.button;
@@ -1626,7 +1450,7 @@ int eventFilter(void * userdata, SDL_Event * event)
case SDL_MOUSEBUTTONUP:
{
if (!inputEnabled() || !g_cursor.inView)
if (!app_inputEnabled() || !g_cursor.inView)
break;
int button = event->button.button;
@@ -1966,6 +1790,45 @@ static int lg_run(void)
return 1;
}
SDL_VERSION(&g_state.wminfo.version);
if (!SDL_GetWindowWMInfo(g_state.window, &g_state.wminfo))
{
DEBUG_ERROR("Could not get SDL window information %s", SDL_GetError());
return -1;
}
// search for the best displayserver ops to use
for(int i = 0; i < LG_DISPLAYSERVER_COUNT; ++i)
if (LG_DisplayServers[i]->subsystem == g_state.wminfo.subsystem)
{
g_state.ds = LG_DisplayServers[i];
break;
}
if (!g_state.ds)
g_state.ds = LG_DisplayServers[0];
// set any null methods to the fallback
#define SET_FALLBACK(x) \
if (!g_state.ds->x) g_state.ds->x = LG_DisplayServers[0]->x;
SET_FALLBACK(getProp);
SET_FALLBACK(init);
SET_FALLBACK(startup);
SET_FALLBACK(shutdown);
SET_FALLBACK(free);
SET_FALLBACK(eventFilter);
SET_FALLBACK(grabPointer);
SET_FALLBACK(ungrabKeyboard);
SET_FALLBACK(warpMouse);
SET_FALLBACK(cbInit);
SET_FALLBACK(cbNotice);
SET_FALLBACK(cbRelease);
SET_FALLBACK(cbRequest);
#undef SET_FALLBACK
// init the subsystem
g_state.ds->init(&g_state.wminfo);
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
params.minimizeOnFocusLoss ? "1" : "0");
@@ -1997,42 +1860,6 @@ static int lg_run(void)
register_key_binds();
// set the compositor hint to bypass for low latency
SDL_VERSION(&g_state.wminfo.version);
if (SDL_GetWindowWMInfo(g_state.window, &g_state.wminfo))
{
if (g_state.wminfo.subsystem == SDL_SYSWM_X11)
{
int event, error;
// enable X11 events to work around SDL2 bugs
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
XQueryExtension(g_state.wminfo.info.x11.display, "XInputExtension",
&g_XInputOp, &event, &error);
Atom NETWM_BYPASS_COMPOSITOR = XInternAtom(
g_state.wminfo.info.x11.display,
"NETWM_BYPASS_COMPOSITOR",
False);
unsigned long value = 1;
XChangeProperty(
g_state.wminfo.info.x11.display,
g_state.wminfo.info.x11.window,
NETWM_BYPASS_COMPOSITOR,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&value,
1
);
}
} else {
DEBUG_ERROR("Could not get SDL window information %s", SDL_GetError());
return -1;
}
initSDLCursor();
if (params.hideMouse)
SDL_ShowCursor(SDL_DISABLE);
@@ -2066,22 +1893,10 @@ static int lg_run(void)
// the end of the output
lgWaitEvent(e_startup, TIMEOUT_INFINITE);
wmInit();
for (LG_Clipboard ** clipboard = LG_Clipboards; *clipboard; clipboard++)
if ((*clipboard)->init(&g_state.wminfo, clipboardRelease, clipboardNotify, clipboardData))
{
g_state.lgc = *clipboard;
break;
}
if (g_state.lgc)
{
DEBUG_INFO("Using Clipboard: %s", g_state.lgc->getName());
g_state.ds->startup();
g_state.cbAvailable = g_state.ds->cbInit && g_state.ds->cbInit();
if (g_state.cbAvailable)
g_state.cbRequestList = ll_new();
}
else
DEBUG_WARN("Failed to initialize the clipboard interface, continuing anyway");
LGMP_STATUS status;
@@ -2263,19 +2078,18 @@ static void lg_shutdown(void)
lgJoinThread(t_spice, NULL);
}
if (g_state.lgc)
{
g_state.lgc->free();
if (g_state.ds)
g_state.ds->shutdown();
struct CBRequest *cbr;
while(ll_shift(g_state.cbRequestList, (void **)&cbr))
free(cbr);
if (g_state.cbRequestList)
{
ll_free(g_state.cbRequestList);
g_state.cbRequestList = NULL;
}
if (g_state.window)
{
wmFree();
g_state.ds->free();
SDL_DestroyWindow(g_state.window);
}

View File

@@ -22,8 +22,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <SDL2/SDL.h>
#include "interface/app.h"
#include "dynamic/displayservers.h"
#include "dynamic/renderers.h"
#include "dynamic/clipboards.h"
#include "common/ivshmem.h"
#include "spice/spice.h"
@@ -38,7 +38,10 @@ enum RunState
struct AppState
{
enum RunState state;
enum RunState state;
struct LG_DisplayServerOps * ds;
bool stopVideo;
bool ignoreInput;
bool escapeActive;
@@ -59,7 +62,7 @@ struct AppState
void * lgrData;
atomic_int lgrResize;
const LG_Clipboard * lgc;
bool cbAvailable;
SpiceDataType cbType;
bool cbChunked;
size_t cbXfer;
@@ -236,6 +239,3 @@ struct CursorState
// forwards
extern struct AppState g_state;
extern struct AppParams params;
void handleMouseGrabbed(double, double);
void handleMouseNormal(double, double);

View File

@@ -1,495 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "wm.h"
#include "main.h"
#include <stdbool.h>
#include <unistd.h>
#include <SDL2/SDL.h>
#include <wayland-client.h>
#include "common/debug.h"
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
struct WMState g_wmState;
static void wmWaylandInit();
static void wmWaylandFree();
static void wmWaylandGrabKeyboard();
static void wmWaylandUngrabKeyboard();
static void wmWaylandGrabPointer();
static void wmWaylandUngrabPointer();
void wmInit(void)
{
switch (g_state.wminfo.subsystem)
{
case SDL_SYSWM_WAYLAND:
wmWaylandInit();
break;
default:
break;
}
}
void wmFree(void)
{
switch (g_state.wminfo.subsystem)
{
case SDL_SYSWM_WAYLAND:
wmWaylandFree();
break;
default:
break;
}
}
void wmGrabPointer(void)
{
switch(g_state.wminfo.subsystem)
{
case SDL_SYSWM_X11:
XGrabPointer(
g_state.wminfo.info.x11.display,
g_state.wminfo.info.x11.window,
true,
None,
GrabModeAsync,
GrabModeAsync,
g_state.wminfo.info.x11.window,
None,
CurrentTime);
break;
case SDL_SYSWM_WAYLAND:
wmWaylandGrabPointer();
break;
default:
SDL_SetWindowGrab(g_state.window, SDL_TRUE);
break;
}
g_wmState.pointerGrabbed = true;
}
void wmUngrabPointer(void)
{
switch(g_state.wminfo.subsystem)
{
case SDL_SYSWM_X11:
XUngrabPointer(g_state.wminfo.info.x11.display, CurrentTime);
break;
case SDL_SYSWM_WAYLAND:
wmWaylandUngrabPointer();
break;
default:
SDL_SetWindowGrab(g_state.window, SDL_FALSE);
break;
}
g_wmState.pointerGrabbed = false;
}
void wmGrabKeyboard(void)
{
switch(g_state.wminfo.subsystem)
{
case SDL_SYSWM_X11:
XGrabKeyboard(
g_state.wminfo.info.x11.display,
g_state.wminfo.info.x11.window,
true,
GrabModeAsync,
GrabModeAsync,
CurrentTime);
break;
case SDL_SYSWM_WAYLAND:
wmWaylandGrabKeyboard();
break;
default:
if (g_wmState.pointerGrabbed)
SDL_SetWindowGrab(g_state.window, SDL_FALSE);
else
{
DEBUG_WARN("SDL does not support grabbing only the keyboard, grabbing all");
g_wmState.pointerGrabbed = true;
}
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1");
SDL_SetWindowGrab(g_state.window, SDL_TRUE);
break;
}
g_wmState.keyboardGrabbed = true;
}
void wmUngrabKeyboard(void)
{
switch(g_state.wminfo.subsystem)
{
case SDL_SYSWM_X11:
XUngrabKeyboard(g_state.wminfo.info.x11.display, CurrentTime);
break;
case SDL_SYSWM_WAYLAND:
wmWaylandUngrabKeyboard();
break;
default:
SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
SDL_SetWindowGrab(g_state.window, SDL_FALSE);
if (g_wmState.pointerGrabbed)
SDL_SetWindowGrab(g_state.window, SDL_TRUE);
break;
}
g_wmState.keyboardGrabbed = false;
}
void wmGrabAll(void)
{
wmGrabPointer();
wmGrabKeyboard();
}
void wmUngrabAll(void)
{
wmUngrabPointer();
wmUngrabKeyboard();
}
void wmWarpMouse(int x, int y)
{
switch(g_state.wminfo.subsystem)
{
case SDL_SYSWM_X11:
XWarpPointer(
g_state.wminfo.info.x11.display,
None,
g_state.wminfo.info.x11.window,
0, 0, 0, 0,
x, y);
XSync(g_state.wminfo.info.x11.display, False);
break;
default:
SDL_WarpMouseInWindow(g_state.window, x, y);
break;
}
}
// Wayland support.
// Registry-handling listeners.
static void registryGlobalHandler(void * data, struct wl_registry * registry,
uint32_t name, const char * interface, uint32_t version)
{
struct WMDataWayland * wm = data;
if (!strcmp(interface, wl_seat_interface.name) && !wm->seat)
wm->seat = wl_registry_bind(wm->registry, name, &wl_seat_interface, 1);
else if (!strcmp(interface, zwp_relative_pointer_manager_v1_interface.name))
wm->relativePointerManager = wl_registry_bind(wm->registry, name,
&zwp_relative_pointer_manager_v1_interface, 1);
else if (!strcmp(interface, zwp_pointer_constraints_v1_interface.name))
wm->pointerConstraints = wl_registry_bind(wm->registry, name,
&zwp_pointer_constraints_v1_interface, 1);
else if (!strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name))
wm->keyboardInhibitManager = wl_registry_bind(wm->registry, name,
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
else if (!strcmp(interface, wl_data_device_manager_interface.name))
wm->dataDeviceManager = wl_registry_bind(wm->registry, name,
&wl_data_device_manager_interface, 3);
}
static void registryGlobalRemoveHandler(void * data,
struct wl_registry * registry, uint32_t name)
{
// Do nothing.
}
static const struct wl_registry_listener registryListener = {
.global = registryGlobalHandler,
.global_remove = registryGlobalRemoveHandler,
};
// Mouse-handling listeners.
static void pointerMotionHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, wl_fixed_t sxW, wl_fixed_t syW)
{
int sx = wl_fixed_to_int(sxW);
int sy = wl_fixed_to_int(syW);
handleMouseNormal(sx, sy);
}
static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, struct wl_surface * surface, wl_fixed_t sxW,
wl_fixed_t syW)
{
int sx = wl_fixed_to_int(sxW);
int sy = wl_fixed_to_int(syW);
handleMouseNormal(sx, sy);
}
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, struct wl_surface * surface)
{
// Do nothing.
}
static void pointerAxisHandler(void * data, struct wl_pointer * pointer,
uint32_t serial, uint32_t axis, wl_fixed_t value)
{
// Do nothing.
}
static void pointerButtonHandler(void *data, struct wl_pointer *pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t stateW)
{
// Do nothing.
}
static const struct wl_pointer_listener pointerListener = {
.enter = pointerEnterHandler,
.leave = pointerLeaveHandler,
.motion = pointerMotionHandler,
.button = pointerButtonHandler,
.axis = pointerAxisHandler,
};
// Keyboard-handling listeners.
static void keyboardKeymapHandler(void * data, struct wl_keyboard * keyboard,
uint32_t format, int fd, uint32_t size)
{
close(fd);
}
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{
struct WMDataWayland * wm = data;
wm->keyboardEnterSerial = serial;
}
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface)
{
// Do nothing.
}
static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
// Do nothing.
}
static void keyboardModifiersHandler(void * data,
struct wl_keyboard * keyboard, uint32_t serial, uint32_t modsDepressed,
uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
{
// Do nothing.
}
static const struct wl_keyboard_listener keyboardListener = {
.keymap = keyboardKeymapHandler,
.enter = keyboardEnterHandler,
.leave = keyboardLeaveHandler,
.key = keyboardKeyHandler,
.modifiers = keyboardModifiersHandler,
};
// Seat-handling listeners.
static void handlePointerCapability(struct WMDataWayland * wm,
uint32_t capabilities)
{
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
if (!hasPointer && wm->pointer)
{
wl_pointer_destroy(wm->pointer);
wm->pointer = NULL;
}
else if (hasPointer && !wm->pointer)
{
wm->pointer = wl_seat_get_pointer(wm->seat);
wl_pointer_add_listener(wm->pointer, &pointerListener, wm);
}
}
static void handleKeyboardCapability(struct WMDataWayland * wm,
uint32_t capabilities)
{
bool hasKeyboard = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;
if (!hasKeyboard && wm->keyboard)
{
wl_keyboard_destroy(wm->keyboard);
wm->keyboard = NULL;
}
else if (hasKeyboard && !wm->keyboard)
{
wm->keyboard = wl_seat_get_keyboard(wm->seat);
wl_keyboard_add_listener(wm->keyboard, &keyboardListener, wm);
}
}
static void seatCapabilitiesHandler(void * data, struct wl_seat * seat,
uint32_t capabilities)
{
struct WMDataWayland * wm = data;
wm->capabilities = capabilities;
handlePointerCapability(wm, capabilities);
handleKeyboardCapability(wm, capabilities);
}
static void seatNameHandler(void * data, struct wl_seat * seat,
const char * name)
{
// Do nothing.
}
static const struct wl_seat_listener seatListener = {
.capabilities = seatCapabilitiesHandler,
.name = seatNameHandler,
};
void wmWaylandInit(void)
{
struct WMDataWayland * wm = malloc(sizeof(struct WMDataWayland));
memset(wm, 0, sizeof(struct WMDataWayland));
wm->display = g_state.wminfo.info.wl.display;
wm->surface = g_state.wminfo.info.wl.surface;
wm->registry = wl_display_get_registry(wm->display);
wl_registry_add_listener(wm->registry, &registryListener, wm);
wl_display_roundtrip(wm->display);
wl_seat_add_listener(wm->seat, &seatListener, wm);
wl_display_roundtrip(wm->display);
wm->dataDevice = wl_data_device_manager_get_data_device(
wm->dataDeviceManager, wm->seat);
g_wmState.opaque = wm;
}
static void relativePointerMotionHandler(void * data,
struct zwp_relative_pointer_v1 *pointer, uint32_t timeHi, uint32_t timeLo,
wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW,
wl_fixed_t dyUnaccelW)
{
double dxUnaccel = wl_fixed_to_double(dxUnaccelW);
double dyUnaccel = wl_fixed_to_double(dyUnaccelW);
handleMouseGrabbed(dxUnaccel, dyUnaccel);
}
static const struct zwp_relative_pointer_v1_listener relativePointerListener = {
.relative_motion = relativePointerMotionHandler,
};
static void wmWaylandGrabPointer(void)
{
struct WMDataWayland * wm = g_wmState.opaque;
if (!wm->relativePointer)
{
wm->relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wm->relativePointerManager, wm->pointer);
zwp_relative_pointer_v1_add_listener(wm->relativePointer,
&relativePointerListener, wm);
}
if (!wm->confinedPointer)
{
wm->confinedPointer = zwp_pointer_constraints_v1_confine_pointer(
wm->pointerConstraints, wm->surface, wm->pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
}
}
static void wmWaylandUngrabPointer(void)
{
struct WMDataWayland * wm = g_wmState.opaque;
if (wm->relativePointer)
{
zwp_relative_pointer_v1_destroy(wm->relativePointer);
wm->relativePointer = NULL;
}
if (wm->confinedPointer)
{
zwp_confined_pointer_v1_destroy(wm->confinedPointer);
wm->confinedPointer = NULL;
}
}
static void wmWaylandGrabKeyboard(void)
{
struct WMDataWayland * wm = g_wmState.opaque;
if (wm->keyboardInhibitManager && !wm->keyboardInhibitor)
{
wm->keyboardInhibitor = zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
wm->keyboardInhibitManager, wm->surface, wm->seat);
}
}
static void wmWaylandUngrabKeyboard(void)
{
struct WMDataWayland * wm = g_wmState.opaque;
if (wm->keyboardInhibitor)
{
zwp_keyboard_shortcuts_inhibitor_v1_destroy(wm->keyboardInhibitor);
wm->keyboardInhibitor = NULL;
}
}
static void wmWaylandFree(void)
{
struct WMDataWayland * wm = g_wmState.opaque;
wmWaylandUngrabPointer();
// TODO: these also need to be freed, but are currently owned by SDL.
// wl_display_destroy(wm->display);
// wl_surface_destroy(wm->surface);
wl_pointer_destroy(wm->pointer);
wl_seat_destroy(wm->seat);
wl_registry_destroy(wm->registry);
free(g_wmState.opaque);
}

View File

@@ -1,65 +0,0 @@
/*
Looking Glass - KVM FrameRelay (KVMFR) Client
Copyright (C) 2017-2021 Geoffrey McRae <geoff@hostfission.com>
https://looking-glass.hostfission.com
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdbool.h>
#include <stdint.h>
struct WMState
{
bool pointerGrabbed;
bool keyboardGrabbed;
void * opaque;
};
struct WMDataWayland
{
struct wl_display * display;
struct wl_surface * surface;
struct wl_registry * registry;
struct wl_seat * seat;
struct wl_data_device_manager * dataDeviceManager;
struct wl_data_device * dataDevice;
uint32_t capabilities;
struct wl_keyboard * keyboard;
struct zwp_keyboard_shortcuts_inhibit_manager_v1 * keyboardInhibitManager;
struct zwp_keyboard_shortcuts_inhibitor_v1 * keyboardInhibitor;
uint32_t keyboardEnterSerial;
struct wl_pointer * pointer;
struct zwp_relative_pointer_manager_v1 * relativePointerManager;
struct zwp_pointer_constraints_v1 * pointerConstraints;
struct zwp_relative_pointer_v1 * relativePointer;
struct zwp_confined_pointer_v1 * confinedPointer;
};
extern struct WMState g_wmState;
void wmInit();
void wmFree();
void wmGrabPointer();
void wmUngrabPointer();
void wmGrabKeyboard();
void wmUngrabKeyboard();
void wmGrabAll();
void wmUngrabAll();
void wmWarpMouse(int x, int y);