1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_VIDEO_DRIVER_X11
24 
25 #include <limits.h> /* For INT_MAX */
26 
27 #include "SDL_events.h"
28 #include "SDL_x11video.h"
29 #include "SDL_timer.h"
30 
31 
32 /* If you don't support UTF-8, you might use XA_STRING here */
33 #ifdef X_HAVE_UTF8_STRING
34 #define TEXT_FORMAT X11_XInternAtom(display, "UTF8_STRING", False)
35 #else
36 #define TEXT_FORMAT XA_STRING
37 #endif
38 
39 /* Get any application owned window handle for clipboard association */
40 static Window
GetWindow(_THIS)41 GetWindow(_THIS)
42 {
43     SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
44 
45     /* We create an unmapped window that exists just to manage the clipboard,
46        since X11 selection data is tied to a specific window and dies with it.
47        We create the window on demand, so apps that don't use the clipboard
48        don't have to keep an unnecessary resource around. */
49     if (data->clipboard_window == None) {
50         Display *dpy = data->display;
51         Window parent = RootWindow(dpy, DefaultScreen(dpy));
52         XSetWindowAttributes xattr;
53         data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
54                                                    CopyFromParent, InputOnly,
55                                                    CopyFromParent, 0, &xattr);
56         X11_XFlush(data->display);
57     }
58 
59     return data->clipboard_window;
60 }
61 
62 /* We use our own cut-buffer for intermediate storage instead of
63    XA_CUT_BUFFER0 because their use isn't really defined for holding UTF8. */
64 Atom
X11_GetSDLCutBufferClipboardType(Display * display)65 X11_GetSDLCutBufferClipboardType(Display *display)
66 {
67     return X11_XInternAtom(display, "SDL_CUTBUFFER", False);
68 }
69 
70 int
X11_SetClipboardText(_THIS,const char * text)71 X11_SetClipboardText(_THIS, const char *text)
72 {
73     Display *display = ((SDL_VideoData *) _this->driverdata)->display;
74     Atom format;
75     Window window;
76     Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
77 
78     /* Get the SDL window that will own the selection */
79     window = GetWindow(_this);
80     if (window == None) {
81         return SDL_SetError("Couldn't find a window to own the selection");
82     }
83 
84     /* Save the selection on the root window */
85     format = TEXT_FORMAT;
86     X11_XChangeProperty(display, DefaultRootWindow(display),
87         X11_GetSDLCutBufferClipboardType(display), format, 8, PropModeReplace,
88         (const unsigned char *)text, SDL_strlen(text));
89 
90     if (XA_CLIPBOARD != None &&
91         X11_XGetSelectionOwner(display, XA_CLIPBOARD) != window) {
92         X11_XSetSelectionOwner(display, XA_CLIPBOARD, window, CurrentTime);
93     }
94 
95     if (X11_XGetSelectionOwner(display, XA_PRIMARY) != window) {
96         X11_XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime);
97     }
98     return 0;
99 }
100 
101 char *
X11_GetClipboardText(_THIS)102 X11_GetClipboardText(_THIS)
103 {
104     SDL_VideoData *videodata = (SDL_VideoData *) _this->driverdata;
105     Display *display = videodata->display;
106     Atom format;
107     Window window;
108     Window owner;
109     Atom selection;
110     Atom seln_type;
111     int seln_format;
112     unsigned long nbytes;
113     unsigned long overflow;
114     unsigned char *src;
115     char *text;
116     Uint32 waitStart;
117     Uint32 waitElapsed;
118     Atom XA_CLIPBOARD = X11_XInternAtom(display, "CLIPBOARD", 0);
119     if (XA_CLIPBOARD == None) {
120         SDL_SetError("Couldn't access X clipboard");
121         return SDL_strdup("");
122     }
123 
124     text = NULL;
125 
126     /* Get the window that holds the selection */
127     window = GetWindow(_this);
128     format = TEXT_FORMAT;
129     owner = X11_XGetSelectionOwner(display, XA_CLIPBOARD);
130     if (owner == None) {
131         /* Fall back to ancient X10 cut-buffers which do not support UTF8 strings*/
132         owner = DefaultRootWindow(display);
133         selection = XA_CUT_BUFFER0;
134         format = XA_STRING;
135     } else if (owner == window) {
136         owner = DefaultRootWindow(display);
137         selection = X11_GetSDLCutBufferClipboardType(display);
138     } else {
139         /* Request that the selection owner copy the data to our window */
140         owner = window;
141         selection = X11_XInternAtom(display, "SDL_SELECTION", False);
142         X11_XConvertSelection(display, XA_CLIPBOARD, format, selection, owner,
143             CurrentTime);
144 
145         /* When using synergy on Linux and when data has been put in the clipboard
146            on the remote (Windows anyway) machine then selection_waiting may never
147            be set to False. Time out after a while. */
148         waitStart = SDL_GetTicks();
149         videodata->selection_waiting = SDL_TRUE;
150         while (videodata->selection_waiting) {
151             SDL_PumpEvents();
152             waitElapsed = SDL_GetTicks() - waitStart;
153             /* Wait one second for a clipboard response. */
154             if (waitElapsed > 1000) {
155                 videodata->selection_waiting = SDL_FALSE;
156                 SDL_SetError("Clipboard timeout");
157                 /* We need to set the clipboard text so that next time we won't
158                    timeout, otherwise we will hang on every call to this function. */
159                 X11_SetClipboardText(_this, "");
160                 return SDL_strdup("");
161             }
162         }
163     }
164 
165     if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX/4, False,
166             format, &seln_type, &seln_format, &nbytes, &overflow, &src)
167             == Success) {
168         if (seln_type == format) {
169             text = (char *)SDL_malloc(nbytes+1);
170             if (text) {
171                 SDL_memcpy(text, src, nbytes);
172                 text[nbytes] = '\0';
173             }
174         }
175         X11_XFree(src);
176     }
177 
178     if (!text) {
179         text = SDL_strdup("");
180     }
181 
182     return text;
183 }
184 
185 SDL_bool
X11_HasClipboardText(_THIS)186 X11_HasClipboardText(_THIS)
187 {
188     SDL_bool result = SDL_FALSE;
189     char *text = X11_GetClipboardText(_this);
190     if (text) {
191         result = text[0] != '\0' ? SDL_TRUE : SDL_FALSE;
192         SDL_free(text);
193     }
194     return result;
195 }
196 
197 #endif /* SDL_VIDEO_DRIVER_X11 */
198 
199 /* vi: set ts=4 sw=4 expandtab: */
200