From 00417b2ea7ebf71deb523fbccf8f805ca8085a25 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Wed, 5 Jul 2023 23:34:54 -0500 Subject: [PATCH 1/7] Add clipboard support --- src/sdlwindow.cpp | 49 +++++++++++++++ src/sdlwindow.hpp | 6 ++ src/steamcompmgr.cpp | 147 +++++++++++++++++++++++++++++++++++++++++++ src/steamcompmgr.hpp | 1 + src/wlserver.cpp | 1 + src/xwayland_ctx.hpp | 4 ++ 6 files changed, 208 insertions(+) diff --git a/src/sdlwindow.cpp b/src/sdlwindow.cpp index d915ab835..16bea8e2a 100644 --- a/src/sdlwindow.cpp +++ b/src/sdlwindow.cpp @@ -1,10 +1,13 @@ // For the nested case, reads input from the SDL window and send to wayland +#include #include #include #include +#include "SDL_clipboard.h" +#include "SDL_events.h" #include "main.hpp" #include "wlserver.hpp" #include "sdlwindow.hpp" @@ -32,6 +35,9 @@ extern bool g_bFirstFrame; SDL_Window *g_SDLWindow; +std::string clipboard; +std::string primarySelection; + enum UserEvents { USER_EVENT_TITLE, @@ -59,6 +65,8 @@ static std::mutex g_SDLCursorLock; static SDLPendingCursor g_SDLPendingCursorData; static bool g_bUpdateSDLCursor = false; +static void set_gamescope_selections(); + //----------------------------------------------------------------------------- // Purpose: Convert from the remote scancode to a Linux event keycode //----------------------------------------------------------------------------- @@ -165,6 +173,9 @@ void inputSDLThreadRun( void ) switch( event.type ) { + case SDL_CLIPBOARDUPDATE: + set_gamescope_selections(); + break; case SDL_MOUSEMOTION: if ( bRelativeMouse ) { @@ -259,6 +270,9 @@ void inputSDLThreadRun( void ) event.type = g_unSDLUserEventID + USER_EVENT_TITLE; SDL_PushEvent( &event ); break; + case KEY_C: + set_gamescope_selections(); + break; default: handled = false; } @@ -469,6 +483,41 @@ void sdlwindow_title( std::shared_ptr title, std::shared_ptrctx.get(); + x11_set_selection(ctx, clipboard, CLIPBOARD); + x11_set_selection(ctx, primarySelection, PRIMARYSELECTION); + } +} + void sdlwindow_visible( bool bVisible ) { if ( !BIsSDLSession() ) diff --git a/src/sdlwindow.hpp b/src/sdlwindow.hpp index f8bdbc82e..d3533df96 100644 --- a/src/sdlwindow.hpp +++ b/src/sdlwindow.hpp @@ -5,10 +5,14 @@ #include #include +#define CLIPBOARD 0 +#define PRIMARYSELECTION 1 + bool sdlwindow_init( void ); void sdlwindow_update( void ); void sdlwindow_title( std::shared_ptr title, std::shared_ptr> icon ); +void sdlwindow_set_selection(std::string, int selection); // called from other threads with interesting things have happened with clients that might warrant updating the nested window void sdlwindow_visible( bool bVisible ); @@ -16,3 +20,5 @@ void sdlwindow_grab( bool bGrab ); void sdlwindow_cursor(std::shared_ptr> pixels, uint32_t width, uint32_t height, uint32_t xhot, uint32_t yhot); extern SDL_Window *g_SDLWindow; +extern std::string clipboard; +extern std::string primarySelection; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 4109f5842..2b7e911bc 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -29,7 +29,10 @@ * says above. Not that I can really do anything about it */ +#include "xwayland_ctx.hpp" +#include #include +#include #include #include #include @@ -4581,6 +4584,127 @@ handle_client_message(xwayland_ctx_t *ctx, XClientMessageEvent *ev) } } +void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget) +{ + if (!BIsNested()) + { + return; + } + + Atom target; + if (selectionTarget == CLIPBOARD) + { + target = ctx->atoms.clipboard; + } + else if (selectionTarget == PRIMARYSELECTION) + { + target = ctx->atoms.primarySelection; + } + else + { + return; + } + + XSetSelectionOwner(ctx->dpy, target, ctx->ourWindow, CurrentTime); + XChangeProperty(ctx->dpy, ctx->ourWindow, target, ctx->atoms.utf8StringAtom, 8, PropModeReplace, + (unsigned char *)contents.c_str(), contents.length()); + XFlush(ctx->dpy); +} + +static void +handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) +{ + if (!BIsNested()) + { + return; + } + + std::string *selection = ev->selection == ctx->atoms.primarySelection ? &primarySelection : &clipboard; + + const char *targetString = XGetAtomName(ctx->dpy, ev->target); + + XEvent response; + response.xselection.type = SelectionNotify; + response.xselection.selection = ev->selection; + response.xselection.requestor = ev->requestor; + response.xselection.time = ev->time; + response.xselection.property = None; + response.xselection.target = None; + + if (ev->requestor == ctx->ourWindow) + { + return; + } + + if (ev->target == ctx->atoms.targets) + { + Atom targetList[] = { + ctx->atoms.targets, + XA_STRING, + }; + + XChangeProperty(ctx->dpy, ev->requestor, ev->property, XA_ATOM, 32, PropModeReplace, + (unsigned char *)&targetList, 2); + response.xselection.property = ev->property; + response.xselection.target = ev->target; + } + else if (!strcmp(targetString, "text/plain;charset=utf-8") || + !strcmp(targetString, "text/plain") || + !strcmp(targetString, "TEXT") || + !strcmp(targetString, "UTF8_STRING") || + !strcmp(targetString, "STRING")) + { + XChangeProperty(ctx->dpy, ev->requestor, ev->property, ev->target, 8, PropModeReplace, + (unsigned char *)selection->c_str(), selection->length()); + response.xselection.property = ev->property; + response.xselection.target = ev->target; + } + else + { + xwm_log.debugf("Unsupported clipboard type: %s. Ignoring", targetString); + } + + XSendEvent(ctx->dpy, ev->requestor, False, NoEventMask, &response); + XFlush(ctx->dpy); +} + +static void +handle_selection_notify(xwayland_ctx_t *ctx, XSelectionEvent *ev) +{ + if (!BIsNested()) + { + return; + } + + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *data = NULL; + + XGetWindowProperty(ctx->dpy, ev->requestor, ev->property, 0, 0, False, AnyPropertyType, + &actual_type, &actual_format, &nitems, &bytes_after, &data); + if (data) { + XFree(data); + } + + if (actual_type == ctx->atoms.utf8StringAtom && actual_format == 8) { + XGetWindowProperty(ctx->dpy, ev->requestor, ev->property, 0, bytes_after, False, AnyPropertyType, + &actual_type, &actual_format, &nitems, &bytes_after, &data); + if (data) { + const char *contents = (const char *) data; + if (ev->selection == ctx->atoms.clipboard) + { + sdlwindow_set_selection(contents, CLIPBOARD); + } + else if (ev->selection == ctx->atoms.primarySelection) + { + sdlwindow_set_selection(contents, PRIMARYSELECTION); + } + XFree(data); + } + } +} template T bit_cast(const J& src) { @@ -5974,6 +6098,13 @@ spawn_client( char **argv ) waitThread.detach(); } +static void +handle_xfixes_selection_notify( xwayland_ctx_t *ctx, XFixesSelectionNotifyEvent *event ) +{ + XConvertSelection(ctx->dpy, event->selection, ctx->atoms.utf8StringAtom, event->selection, ctx->ourWindow, CurrentTime); + XFlush(ctx->dpy); +} + static void dispatch_x11( xwayland_ctx_t *ctx ) { @@ -6113,6 +6244,12 @@ dispatch_x11( xwayland_ctx_t *ctx ) bShouldResetCursor = true; } break; + case SelectionNotify: + handle_selection_notify(ctx, &ev.xselection); + break; + case SelectionRequest: + handle_selection_request(ctx, &ev.xselectionrequest); + break; default: if (ev.type == ctx->damage_event + XDamageNotify) { @@ -6122,6 +6259,10 @@ dispatch_x11( xwayland_ctx_t *ctx ) { cursor->setDirty(); } + else if (ev.type == ctx->xfixes_event + XFixesSelectionNotify) + { + handle_xfixes_selection_notify(ctx, (XFixesSelectionNotifyEvent *) &ev); + } break; } XFlush(ctx->dpy); @@ -6491,6 +6632,10 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ ctx->atoms.wineHwndStyle = XInternAtom( ctx->dpy, "_WINE_HWND_STYLE", false ); ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false ); + ctx->atoms.clipboard = XInternAtom(ctx->dpy, "CLIPBOARD", false); + ctx->atoms.primarySelection = XInternAtom(ctx->dpy, "PRIMARY", false); + ctx->atoms.targets = XInternAtom(ctx->dpy, "TARGETS", false); + ctx->root_width = DisplayWidth(ctx->dpy, ctx->scr); ctx->root_height = DisplayHeight(ctx->dpy, ctx->scr); @@ -6519,6 +6664,8 @@ void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_ PropertyChangeMask); XShapeSelectInput(ctx->dpy, ctx->root, ShapeNotifyMask); XFixesSelectCursorInput(ctx->dpy, ctx->root, XFixesDisplayCursorNotifyMask); + XFixesSelectSelectionInput(ctx->dpy, ctx->root, ctx->atoms.clipboard, XFixesSetSelectionOwnerNotifyMask); + XFixesSelectSelectionInput(ctx->dpy, ctx->root, ctx->atoms.primarySelection, XFixesSetSelectionOwnerNotifyMask); XQueryTree(ctx->dpy, ctx->root, &root_return, &parent_return, &children, &nchildren); for (uint32_t i = 0; i < nchildren; i++) add_win(ctx, children[i], i ? children[i-1] : None, 0); diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 2f5d2f2ed..10f9f2a43 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -141,5 +141,6 @@ extern uint64_t g_SteamCompMgrVBlankTime; extern pid_t focusWindow_pid; void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_server); +void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget); extern int g_nAsyncFlipsEnabled; diff --git a/src/wlserver.cpp b/src/wlserver.cpp index 379883159..08b96c41f 100644 --- a/src/wlserver.cpp +++ b/src/wlserver.cpp @@ -48,6 +48,7 @@ extern "C" { #include "log.hpp" #include "ime.hpp" #include "xwayland_ctx.hpp" +#include "sdlwindow.hpp" #if HAVE_PIPEWIRE #include "pipewire.hpp" diff --git a/src/xwayland_ctx.hpp b/src/xwayland_ctx.hpp index 75a8d0edd..63e5c2c84 100644 --- a/src/xwayland_ctx.hpp +++ b/src/xwayland_ctx.hpp @@ -207,5 +207,9 @@ struct xwayland_ctx_t Atom wineHwndStyle; Atom wineHwndStyleEx; + + Atom clipboard; + Atom primarySelection; + Atom targets; } atoms; }; From dbe3c2a47ddbe01ccfe33b5f401a4f6cf8197471 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Wed, 5 Jul 2023 23:40:10 -0500 Subject: [PATCH 2/7] Update documentation and --help --- README.md | 2 ++ src/main.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index 10e0730e9..bffbbdf73 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ meson install -C build/ --skip-subprojects * **Super + I** : Increase FSR sharpness by 1 * **Super + O** : Decrease FSR sharpness by 1 * **Super + S** : Take screenshot (currently goes to `/tmp/gamescope_$DATE.png`) +* **Super + G** : Toggle keyboard grab +* **Super + S** : Update clipboard ## Examples diff --git a/src/main.cpp b/src/main.cpp index d3f45316b..b1ff60d84 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -224,6 +224,7 @@ const char usage[] = " Super + O decrease FSR sharpness by 1\n" " Super + S take a screenshot\n" " Super + G toggle keyboard grab\n" + " Super + C update clipboard\n" ""; std::atomic< bool > g_bRun{true}; From 9b1d11955f7b309339ce821db29ed3ce85f0ac49 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Thu, 6 Jul 2023 07:57:04 -0500 Subject: [PATCH 3/7] Fix documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bffbbdf73..59568fba4 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ meson install -C build/ --skip-subprojects * **Super + O** : Decrease FSR sharpness by 1 * **Super + S** : Take screenshot (currently goes to `/tmp/gamescope_$DATE.png`) * **Super + G** : Toggle keyboard grab -* **Super + S** : Update clipboard +* **Super + C** : Update clipboard ## Examples From 07b3823f809d59a5b4b68d6f04e03f91aa6ce7c2 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 7 Jul 2023 15:16:34 -0500 Subject: [PATCH 4/7] Fix build issue Fixes the following error: error: control reaches end of non-void function [-Werror=return-type] --- src/main.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b1ff60d84..58730314d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -847,13 +847,7 @@ static bool initOutput( int preferredWidth, int preferredHeight, int preferredRe { return sdlwindow_init(); } - else - { - return true; - } - } - else - { - return init_drm( &g_DRM, preferredWidth, preferredHeight, preferredRefresh, s_bInitialWantsVRREnabled ); + return true; } + return init_drm( &g_DRM, preferredWidth, preferredHeight, preferredRefresh, s_bInitialWantsVRREnabled ); } From e631460bc5a1ce53af994cf7f0c9f9e96325a298 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Sat, 29 Jul 2023 20:28:30 -0500 Subject: [PATCH 5/7] Remove unnecessary key bind --- src/sdlwindow.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sdlwindow.cpp b/src/sdlwindow.cpp index 16bea8e2a..eeea7dacd 100644 --- a/src/sdlwindow.cpp +++ b/src/sdlwindow.cpp @@ -270,9 +270,6 @@ void inputSDLThreadRun( void ) event.type = g_unSDLUserEventID + USER_EVENT_TITLE; SDL_PushEvent( &event ); break; - case KEY_C: - set_gamescope_selections(); - break; default: handled = false; } From 8b064ed2a682ecf8085ca2d4e3c5db393d72db9f Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Sat, 29 Jul 2023 20:24:52 -0500 Subject: [PATCH 6/7] Improve clipboard handling This change properly syncs the primary selection and clipboard across all xwayland instances, even in embedded mode --- src/sdlwindow.cpp | 25 ++++------------ src/sdlwindow.hpp | 2 -- src/steamcompmgr.cpp | 69 ++++++++++++++++++++++++++++++++++---------- src/steamcompmgr.hpp | 2 +- 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/sdlwindow.cpp b/src/sdlwindow.cpp index eeea7dacd..7035c6248 100644 --- a/src/sdlwindow.cpp +++ b/src/sdlwindow.cpp @@ -35,9 +35,6 @@ extern bool g_bFirstFrame; SDL_Window *g_SDLWindow; -std::string clipboard; -std::string primarySelection; - enum UserEvents { USER_EVENT_TITLE, @@ -484,35 +481,25 @@ void sdlwindow_set_selection(std::string contents, int selection) { if (selection == CLIPBOARD) { - clipboard = contents; SDL_SetClipboardText(contents.c_str()); } else if (selection == PRIMARYSELECTION) { - primarySelection = contents; SDL_SetPrimarySelectionText(contents.c_str()); } } static void set_gamescope_selections() { - char *text; + char *_clipboard = SDL_GetClipboardText(); - text = SDL_GetClipboardText(); - clipboard = text; - SDL_free(text); + char *_primarySelection = SDL_GetPrimarySelectionText(); - text = SDL_GetPrimarySelectionText(); - primarySelection = text; - SDL_free(text); + gamescope_set_selection(_clipboard, CLIPBOARD); + gamescope_set_selection(_primarySelection, PRIMARYSELECTION); - for (int i = 0; i < g_nXWaylandCount; i++) - { - gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(0); - xwayland_ctx_t *ctx = server->ctx.get(); - x11_set_selection(ctx, clipboard, CLIPBOARD); - x11_set_selection(ctx, primarySelection, PRIMARYSELECTION); - } + SDL_free(_clipboard); + SDL_free(_primarySelection); } void sdlwindow_visible( bool bVisible ) diff --git a/src/sdlwindow.hpp b/src/sdlwindow.hpp index d3533df96..5223d06e2 100644 --- a/src/sdlwindow.hpp +++ b/src/sdlwindow.hpp @@ -20,5 +20,3 @@ void sdlwindow_grab( bool bGrab ); void sdlwindow_cursor(std::shared_ptr> pixels, uint32_t width, uint32_t height, uint32_t xhot, uint32_t yhot); extern SDL_Window *g_SDLWindow; -extern std::string clipboard; -extern std::string primarySelection; diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 2b7e911bc..26177358a 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -129,6 +129,9 @@ extern float g_flHDRItmTargetNits; extern std::atomic g_lastVblank; +std::string clipboard; +std::string primarySelection; + uint64_t timespec_to_nanos(struct timespec& spec) { return spec.tv_sec * 1'000'000'000ul + spec.tv_nsec; @@ -4584,13 +4587,8 @@ handle_client_message(xwayland_ctx_t *ctx, XClientMessageEvent *ev) } } -void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget) +static void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget) { - if (!BIsNested()) - { - return; - } - Atom target; if (selectionTarget == CLIPBOARD) { @@ -4611,14 +4609,28 @@ void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionT XFlush(ctx->dpy); } -static void -handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) +void gamescope_set_selection(std::string contents, int selection) { - if (!BIsNested()) + if (selection == CLIPBOARD) { - return; + clipboard = contents; + } + else if (selection == PRIMARYSELECTION) + { + primarySelection = contents; } + for (int i = 0; i < g_nXWaylandCount; i++) + { + gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(i); + xwayland_ctx_t *ctx = server->ctx.get(); + x11_set_selection(ctx, contents, selection); + } +} + +static void +handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) +{ std::string *selection = ev->selection == ctx->atoms.primarySelection ? &primarySelection : &clipboard; const char *targetString = XGetAtomName(ctx->dpy, ev->target); @@ -4654,6 +4666,7 @@ handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) !strcmp(targetString, "UTF8_STRING") || !strcmp(targetString, "STRING")) { + XChangeProperty(ctx->dpy, ev->requestor, ev->property, ev->target, 8, PropModeReplace, (unsigned char *)selection->c_str(), selection->length()); response.xselection.property = ev->property; @@ -4671,10 +4684,7 @@ handle_selection_request(xwayland_ctx_t *ctx, XSelectionRequestEvent *ev) static void handle_selection_notify(xwayland_ctx_t *ctx, XSelectionEvent *ev) { - if (!BIsNested()) - { - return; - } + int selection; Atom actual_type; int actual_format; @@ -4693,14 +4703,36 @@ handle_selection_notify(xwayland_ctx_t *ctx, XSelectionEvent *ev) &actual_type, &actual_format, &nitems, &bytes_after, &data); if (data) { const char *contents = (const char *) data; + if (ev->selection == ctx->atoms.clipboard) { - sdlwindow_set_selection(contents, CLIPBOARD); + selection = CLIPBOARD; } else if (ev->selection == ctx->atoms.primarySelection) { - sdlwindow_set_selection(contents, PRIMARYSELECTION); + selection = PRIMARYSELECTION; + } + else + { + xwm_log.errorf( "Selection '%s' not supported. Ignoring", XGetAtomName(ctx->dpy, ev->selection) ); + goto done; + } + + if (BIsNested()) + { + /* + * gamescope_set_selection() doesn't need to be called here. + * sdlwindow_set_selection triggers a clipboard update, which + * then indirectly ccalls gamescope_set_selection() + */ + sdlwindow_set_selection(contents, selection); } + else + { + gamescope_set_selection(contents, selection); + } + +done: XFree(data); } } @@ -6101,6 +6133,11 @@ spawn_client( char **argv ) static void handle_xfixes_selection_notify( xwayland_ctx_t *ctx, XFixesSelectionNotifyEvent *event ) { + if (event->owner == ctx->ourWindow) + { + return; + } + XConvertSelection(ctx->dpy, event->selection, ctx->atoms.utf8StringAtom, event->selection, ctx->ourWindow, CurrentTime); XFlush(ctx->dpy); } diff --git a/src/steamcompmgr.hpp b/src/steamcompmgr.hpp index 10f9f2a43..f3a6c775a 100644 --- a/src/steamcompmgr.hpp +++ b/src/steamcompmgr.hpp @@ -141,6 +141,6 @@ extern uint64_t g_SteamCompMgrVBlankTime; extern pid_t focusWindow_pid; void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_server); -void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget); +void gamescope_set_selection(std::string contents, int selection); extern int g_nAsyncFlipsEnabled; From 0168c6017f695c6b29fc9310f0a76dedcf5ae226 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Mon, 31 Jul 2023 18:24:17 -0500 Subject: [PATCH 7/7] Only SetSelectionOwner when syncing selections When a client becomes the selection owner, and sets the selection, an XFixes event is raised, and gamescope responds by retrieving the contents of the selection, and re-setting ourWindow as the owner of the selection. We don't need to call XChangeProperty when syncing across Xwayland instances, as that gets called when a client makes a SelectionRequest. --- src/steamcompmgr.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp index 26177358a..31e1e5b3c 100644 --- a/src/steamcompmgr.cpp +++ b/src/steamcompmgr.cpp @@ -4587,7 +4587,7 @@ handle_client_message(xwayland_ctx_t *ctx, XClientMessageEvent *ev) } } -static void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int selectionTarget) +static void x11_set_selection_owner(xwayland_ctx_t *ctx, std::string contents, int selectionTarget) { Atom target; if (selectionTarget == CLIPBOARD) @@ -4604,9 +4604,6 @@ static void x11_set_selection(xwayland_ctx_t *ctx, std::string contents, int sel } XSetSelectionOwner(ctx->dpy, target, ctx->ourWindow, CurrentTime); - XChangeProperty(ctx->dpy, ctx->ourWindow, target, ctx->atoms.utf8StringAtom, 8, PropModeReplace, - (unsigned char *)contents.c_str(), contents.length()); - XFlush(ctx->dpy); } void gamescope_set_selection(std::string contents, int selection) @@ -4620,11 +4617,10 @@ void gamescope_set_selection(std::string contents, int selection) primarySelection = contents; } - for (int i = 0; i < g_nXWaylandCount; i++) + gamescope_xwayland_server_t *server = NULL; + for (int i = 0; (server = wlserver_get_xwayland_server(i)); i++) { - gamescope_xwayland_server_t *server = wlserver_get_xwayland_server(i); - xwayland_ctx_t *ctx = server->ctx.get(); - x11_set_selection(ctx, contents, selection); + x11_set_selection_owner(server->ctx.get(), contents, selection); } }