1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 |
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 |
65 | X11_GetSDLCutBufferClipboardType(Display *display) |
66 | { |
67 | return X11_XInternAtom(display, "SDL_CUTBUFFER" , False); |
68 | } |
69 | |
70 | int |
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 * |
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 |
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 | |