diff options
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/x11')
45 files changed, 16068 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c new file mode 100644 index 0000000..5e33555 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.c | |||
| @@ -0,0 +1,329 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include <limits.h> // For INT_MAX | ||
| 26 | |||
| 27 | #include "SDL_x11video.h" | ||
| 28 | #include "SDL_x11clipboard.h" | ||
| 29 | #include "../SDL_clipboard_c.h" | ||
| 30 | #include "../../events/SDL_events_c.h" | ||
| 31 | |||
| 32 | static const char *text_mime_types[] = { | ||
| 33 | "UTF8_STRING", | ||
| 34 | "text/plain;charset=utf-8", | ||
| 35 | "text/plain", | ||
| 36 | "TEXT", | ||
| 37 | "STRING" | ||
| 38 | }; | ||
| 39 | |||
| 40 | // Get any application owned window handle for clipboard association | ||
| 41 | Window GetWindow(SDL_VideoDevice *_this) | ||
| 42 | { | ||
| 43 | SDL_VideoData *data = _this->internal; | ||
| 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 | |||
| 57 | X11_XSelectInput(dpy, data->clipboard_window, PropertyChangeMask); | ||
| 58 | X11_XFlush(data->display); | ||
| 59 | } | ||
| 60 | |||
| 61 | return data->clipboard_window; | ||
| 62 | } | ||
| 63 | |||
| 64 | static bool SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback, | ||
| 65 | void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence) | ||
| 66 | { | ||
| 67 | SDL_VideoData *videodata = _this->internal; | ||
| 68 | Display *display = videodata->display; | ||
| 69 | Window window; | ||
| 70 | SDLX11_ClipboardData *clipboard; | ||
| 71 | bool clipboard_owner = false; | ||
| 72 | |||
| 73 | window = GetWindow(_this); | ||
| 74 | if (window == None) { | ||
| 75 | return SDL_SetError("Couldn't find a window to own the selection"); | ||
| 76 | } | ||
| 77 | |||
| 78 | if (selection == XA_PRIMARY) { | ||
| 79 | clipboard = &videodata->primary_selection; | ||
| 80 | } else { | ||
| 81 | clipboard = &videodata->clipboard; | ||
| 82 | } | ||
| 83 | |||
| 84 | clipboard_owner = X11_XGetSelectionOwner(display, selection) == window; | ||
| 85 | |||
| 86 | // If we are canceling our own data we need to clean it up | ||
| 87 | if (clipboard_owner && clipboard->sequence == 0) { | ||
| 88 | SDL_free(clipboard->userdata); | ||
| 89 | } | ||
| 90 | |||
| 91 | clipboard->callback = callback; | ||
| 92 | clipboard->userdata = userdata; | ||
| 93 | clipboard->mime_types = mime_types; | ||
| 94 | clipboard->mime_count = mime_count; | ||
| 95 | clipboard->sequence = sequence; | ||
| 96 | |||
| 97 | X11_XSetSelectionOwner(display, selection, window, CurrentTime); | ||
| 98 | return true; | ||
| 99 | } | ||
| 100 | |||
| 101 | static void *CloneDataBuffer(const void *buffer, const size_t len) | ||
| 102 | { | ||
| 103 | void *clone = NULL; | ||
| 104 | if (len > 0 && buffer) { | ||
| 105 | clone = SDL_malloc(len + sizeof(Uint32)); | ||
| 106 | if (clone) { | ||
| 107 | SDL_memcpy(clone, buffer, len); | ||
| 108 | SDL_memset((Uint8 *)clone + len, 0, sizeof(Uint32)); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | return clone; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* | ||
| 115 | * original_buffer is considered unusable after the function is called. | ||
| 116 | */ | ||
| 117 | static void *AppendDataBuffer(void *original_buffer, const size_t old_len, const void *buffer, const size_t buffer_len) | ||
| 118 | { | ||
| 119 | void *resized_buffer; | ||
| 120 | |||
| 121 | if (buffer_len > 0 && buffer) { | ||
| 122 | resized_buffer = SDL_realloc(original_buffer, old_len + buffer_len + sizeof(Uint32)); | ||
| 123 | if (resized_buffer) { | ||
| 124 | SDL_memcpy((Uint8 *)resized_buffer + old_len, buffer, buffer_len); | ||
| 125 | SDL_memset((Uint8 *)resized_buffer + old_len + buffer_len, 0, sizeof(Uint32)); | ||
| 126 | } | ||
| 127 | |||
| 128 | return resized_buffer; | ||
| 129 | } else { | ||
| 130 | return original_buffer; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | static bool WaitForSelection(SDL_VideoDevice *_this, Atom selection_type, bool *flag) | ||
| 135 | { | ||
| 136 | Uint64 waitStart; | ||
| 137 | Uint64 waitElapsed; | ||
| 138 | |||
| 139 | waitStart = SDL_GetTicks(); | ||
| 140 | *flag = true; | ||
| 141 | while (*flag) { | ||
| 142 | SDL_PumpEvents(); | ||
| 143 | waitElapsed = SDL_GetTicks() - waitStart; | ||
| 144 | // Wait one second for a selection response. | ||
| 145 | if (waitElapsed > 1000) { | ||
| 146 | *flag = false; | ||
| 147 | SDL_SetError("Selection timeout"); | ||
| 148 | /* We need to set the selection text so that next time we won't | ||
| 149 | timeout, otherwise we will hang on every call to this function. */ | ||
| 150 | SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL, | ||
| 151 | text_mime_types, SDL_arraysize(text_mime_types), 0); | ||
| 152 | return false; | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | return true; | ||
| 157 | } | ||
| 158 | |||
| 159 | static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type, | ||
| 160 | const char *mime_type, size_t *length) | ||
| 161 | { | ||
| 162 | SDL_VideoData *videodata = _this->internal; | ||
| 163 | Display *display = videodata->display; | ||
| 164 | Window window; | ||
| 165 | Window owner; | ||
| 166 | Atom selection; | ||
| 167 | Atom seln_type; | ||
| 168 | int seln_format; | ||
| 169 | unsigned long count; | ||
| 170 | unsigned long overflow; | ||
| 171 | |||
| 172 | SDLX11_ClipboardData *clipboard; | ||
| 173 | void *data = NULL; | ||
| 174 | unsigned char *src = NULL; | ||
| 175 | bool incr_success = false; | ||
| 176 | Atom XA_MIME = X11_XInternAtom(display, mime_type, False); | ||
| 177 | |||
| 178 | *length = 0; | ||
| 179 | |||
| 180 | // Get the window that holds the selection | ||
| 181 | window = GetWindow(_this); | ||
| 182 | owner = X11_XGetSelectionOwner(display, selection_type); | ||
| 183 | if (owner == None) { | ||
| 184 | // This requires a fallback to ancient X10 cut-buffers. We will just skip those for now | ||
| 185 | data = NULL; | ||
| 186 | } else if (owner == window) { | ||
| 187 | owner = DefaultRootWindow(display); | ||
| 188 | if (selection_type == XA_PRIMARY) { | ||
| 189 | clipboard = &videodata->primary_selection; | ||
| 190 | } else { | ||
| 191 | clipboard = &videodata->clipboard; | ||
| 192 | } | ||
| 193 | |||
| 194 | if (clipboard->callback) { | ||
| 195 | const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length); | ||
| 196 | data = CloneDataBuffer(clipboard_data, *length); | ||
| 197 | } | ||
| 198 | } else { | ||
| 199 | // Request that the selection owner copy the data to our window | ||
| 200 | owner = window; | ||
| 201 | selection = videodata->atoms.SDL_SELECTION; | ||
| 202 | X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner, | ||
| 203 | CurrentTime); | ||
| 204 | |||
| 205 | if (WaitForSelection(_this, selection_type, &videodata->selection_waiting) == false) { | ||
| 206 | data = NULL; | ||
| 207 | *length = 0; | ||
| 208 | } | ||
| 209 | |||
| 210 | if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False, | ||
| 211 | XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) { | ||
| 212 | if (seln_type == XA_MIME) { | ||
| 213 | *length = (size_t)count; | ||
| 214 | data = CloneDataBuffer(src, count); | ||
| 215 | } else if (seln_type == videodata->atoms.INCR) { | ||
| 216 | while (1) { | ||
| 217 | // Only delete the property after being done with the previous "chunk". | ||
| 218 | X11_XDeleteProperty(display, owner, selection); | ||
| 219 | X11_XFlush(display); | ||
| 220 | |||
| 221 | if (WaitForSelection(_this, selection_type, &videodata->selection_incr_waiting) == false) { | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | |||
| 225 | X11_XFree(src); | ||
| 226 | if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False, | ||
| 227 | XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) != Success) { | ||
| 228 | break; | ||
| 229 | } | ||
| 230 | |||
| 231 | if (count == 0) { | ||
| 232 | incr_success = true; | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | |||
| 236 | if (*length == 0) { | ||
| 237 | *length = (size_t)count; | ||
| 238 | data = CloneDataBuffer(src, count); | ||
| 239 | } else { | ||
| 240 | data = AppendDataBuffer(data, *length, src, count); | ||
| 241 | *length += (size_t)count; | ||
| 242 | } | ||
| 243 | |||
| 244 | if (data == NULL) { | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | if (incr_success == false) { | ||
| 250 | SDL_free(data); | ||
| 251 | data = 0; | ||
| 252 | *length = 0; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | X11_XFree(src); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | return data; | ||
| 259 | } | ||
| 260 | |||
| 261 | const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types) | ||
| 262 | { | ||
| 263 | *num_mime_types = SDL_arraysize(text_mime_types); | ||
| 264 | return text_mime_types; | ||
| 265 | } | ||
| 266 | |||
| 267 | bool X11_SetClipboardData(SDL_VideoDevice *_this) | ||
| 268 | { | ||
| 269 | SDL_VideoData *videodata = _this->internal; | ||
| 270 | return SetSelectionData(_this, videodata->atoms.CLIPBOARD, _this->clipboard_callback, _this->clipboard_userdata, (const char **)_this->clipboard_mime_types, _this->num_clipboard_mime_types, _this->clipboard_sequence); | ||
| 271 | } | ||
| 272 | |||
| 273 | void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length) | ||
| 274 | { | ||
| 275 | SDL_VideoData *videodata = _this->internal; | ||
| 276 | return GetSelectionData(_this, videodata->atoms.CLIPBOARD, mime_type, length); | ||
| 277 | } | ||
| 278 | |||
| 279 | bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type) | ||
| 280 | { | ||
| 281 | size_t length; | ||
| 282 | void *data; | ||
| 283 | data = X11_GetClipboardData(_this, mime_type, &length); | ||
| 284 | if (data) { | ||
| 285 | SDL_free(data); | ||
| 286 | } | ||
| 287 | return length > 0; | ||
| 288 | } | ||
| 289 | |||
| 290 | bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text) | ||
| 291 | { | ||
| 292 | return SetSelectionData(_this, XA_PRIMARY, SDL_ClipboardTextCallback, SDL_strdup(text), text_mime_types, SDL_arraysize(text_mime_types), 0); | ||
| 293 | } | ||
| 294 | |||
| 295 | char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this) | ||
| 296 | { | ||
| 297 | size_t length; | ||
| 298 | char *text = GetSelectionData(_this, XA_PRIMARY, text_mime_types[0], &length); | ||
| 299 | if (!text) { | ||
| 300 | text = SDL_strdup(""); | ||
| 301 | } | ||
| 302 | return text; | ||
| 303 | } | ||
| 304 | |||
| 305 | bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this) | ||
| 306 | { | ||
| 307 | bool result = false; | ||
| 308 | char *text = X11_GetPrimarySelectionText(_this); | ||
| 309 | if (text) { | ||
| 310 | if (text[0] != '\0') { | ||
| 311 | result = true; | ||
| 312 | } | ||
| 313 | SDL_free(text); | ||
| 314 | } | ||
| 315 | return result; | ||
| 316 | } | ||
| 317 | |||
| 318 | void X11_QuitClipboard(SDL_VideoDevice *_this) | ||
| 319 | { | ||
| 320 | SDL_VideoData *data = _this->internal; | ||
| 321 | if (data->primary_selection.sequence == 0) { | ||
| 322 | SDL_free(data->primary_selection.userdata); | ||
| 323 | } | ||
| 324 | if (data->clipboard.sequence == 0) { | ||
| 325 | SDL_free(data->clipboard.userdata); | ||
| 326 | } | ||
| 327 | } | ||
| 328 | |||
| 329 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h new file mode 100644 index 0000000..da5990a --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11clipboard.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11clipboard_h_ | ||
| 24 | #define SDL_x11clipboard_h_ | ||
| 25 | |||
| 26 | #include <X11/Xlib.h> | ||
| 27 | |||
| 28 | typedef struct X11_ClipboardData { | ||
| 29 | SDL_ClipboardDataCallback callback; | ||
| 30 | void *userdata; | ||
| 31 | const char **mime_types; | ||
| 32 | size_t mime_count; | ||
| 33 | Uint32 sequence; | ||
| 34 | } SDLX11_ClipboardData; | ||
| 35 | |||
| 36 | extern const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types); | ||
| 37 | extern bool X11_SetClipboardData(SDL_VideoDevice *_this); | ||
| 38 | extern void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length); | ||
| 39 | extern bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type); | ||
| 40 | extern bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text); | ||
| 41 | extern char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this); | ||
| 42 | extern bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this); | ||
| 43 | extern void X11_QuitClipboard(SDL_VideoDevice *_this); | ||
| 44 | Window GetWindow(SDL_VideoDevice *_this); | ||
| 45 | |||
| 46 | #endif // SDL_x11clipboard_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c new file mode 100644 index 0000000..7c48ed5 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.c | |||
| @@ -0,0 +1,211 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #define DEBUG_DYNAMIC_X11 0 | ||
| 26 | |||
| 27 | #include "SDL_x11dyn.h" | ||
| 28 | |||
| 29 | #if DEBUG_DYNAMIC_X11 | ||
| 30 | #include <stdio.h> | ||
| 31 | #endif | ||
| 32 | |||
| 33 | #ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC | ||
| 34 | |||
| 35 | typedef struct | ||
| 36 | { | ||
| 37 | SDL_SharedObject *lib; | ||
| 38 | const char *libname; | ||
| 39 | } x11dynlib; | ||
| 40 | |||
| 41 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT | ||
| 42 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT NULL | ||
| 43 | #endif | ||
| 44 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR | ||
| 45 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR NULL | ||
| 46 | #endif | ||
| 47 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 | ||
| 48 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 NULL | ||
| 49 | #endif | ||
| 50 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES | ||
| 51 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES NULL | ||
| 52 | #endif | ||
| 53 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR | ||
| 54 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR NULL | ||
| 55 | #endif | ||
| 56 | #ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS | ||
| 57 | #define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS NULL | ||
| 58 | #endif | ||
| 59 | |||
| 60 | static x11dynlib x11libs[] = { | ||
| 61 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC }, | ||
| 62 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT }, | ||
| 63 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR }, | ||
| 64 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 }, | ||
| 65 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES }, | ||
| 66 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR }, | ||
| 67 | { NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS } | ||
| 68 | }; | ||
| 69 | |||
| 70 | static void *X11_GetSym(const char *fnname, int *pHasModule) | ||
| 71 | { | ||
| 72 | int i; | ||
| 73 | void *fn = NULL; | ||
| 74 | for (i = 0; i < SDL_arraysize(x11libs); i++) { | ||
| 75 | if (x11libs[i].lib) { | ||
| 76 | fn = SDL_LoadFunction(x11libs[i].lib, fnname); | ||
| 77 | if (fn) { | ||
| 78 | break; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | #if DEBUG_DYNAMIC_X11 | ||
| 84 | if (fn) | ||
| 85 | printf("X11: Found '%s' in %s (%p)\n", fnname, x11libs[i].libname, fn); | ||
| 86 | else | ||
| 87 | printf("X11: Symbol '%s' NOT FOUND!\n", fnname); | ||
| 88 | #endif | ||
| 89 | |||
| 90 | if (!fn) { | ||
| 91 | *pHasModule = 0; // kill this module. | ||
| 92 | } | ||
| 93 | |||
| 94 | return fn; | ||
| 95 | } | ||
| 96 | |||
| 97 | #endif // SDL_VIDEO_DRIVER_X11_DYNAMIC | ||
| 98 | |||
| 99 | // Define all the function pointers and wrappers... | ||
| 100 | #define SDL_X11_SYM(rc, fn, params, args, ret) SDL_DYNX11FN_##fn X11_##fn = NULL; | ||
| 101 | #include "SDL_x11sym.h" | ||
| 102 | |||
| 103 | // Annoying varargs entry point... | ||
| 104 | #ifdef X_HAVE_UTF8_STRING | ||
| 105 | SDL_DYNX11FN_XCreateIC X11_XCreateIC = NULL; | ||
| 106 | SDL_DYNX11FN_XGetICValues X11_XGetICValues = NULL; | ||
| 107 | SDL_DYNX11FN_XSetICValues X11_XSetICValues = NULL; | ||
| 108 | SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList = NULL; | ||
| 109 | #endif | ||
| 110 | |||
| 111 | /* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */ | ||
| 112 | #define SDL_X11_MODULE(modname) int SDL_X11_HAVE_##modname = 0; | ||
| 113 | #include "SDL_x11sym.h" | ||
| 114 | |||
| 115 | static int x11_load_refcount = 0; | ||
| 116 | |||
| 117 | void SDL_X11_UnloadSymbols(void) | ||
| 118 | { | ||
| 119 | // Don't actually unload if more than one module is using the libs... | ||
| 120 | if (x11_load_refcount > 0) { | ||
| 121 | if (--x11_load_refcount == 0) { | ||
| 122 | #ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC | ||
| 123 | int i; | ||
| 124 | #endif | ||
| 125 | |||
| 126 | // set all the function pointers to NULL. | ||
| 127 | #define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 0; | ||
| 128 | #define SDL_X11_SYM(rc, fn, params, args, ret) X11_##fn = NULL; | ||
| 129 | #include "SDL_x11sym.h" | ||
| 130 | |||
| 131 | #ifdef X_HAVE_UTF8_STRING | ||
| 132 | X11_XCreateIC = NULL; | ||
| 133 | X11_XGetICValues = NULL; | ||
| 134 | X11_XSetICValues = NULL; | ||
| 135 | X11_XVaCreateNestedList = NULL; | ||
| 136 | #endif | ||
| 137 | |||
| 138 | #ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC | ||
| 139 | for (i = 0; i < SDL_arraysize(x11libs); i++) { | ||
| 140 | if (x11libs[i].lib) { | ||
| 141 | SDL_UnloadObject(x11libs[i].lib); | ||
| 142 | x11libs[i].lib = NULL; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | #endif | ||
| 146 | } | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | // returns non-zero if all needed symbols were loaded. | ||
| 151 | bool SDL_X11_LoadSymbols(void) | ||
| 152 | { | ||
| 153 | bool result = true; // always succeed if not using Dynamic X11 stuff. | ||
| 154 | |||
| 155 | // deal with multiple modules (dga, x11, etc) needing these symbols... | ||
| 156 | if (x11_load_refcount++ == 0) { | ||
| 157 | #ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC | ||
| 158 | int i; | ||
| 159 | int *thismod = NULL; | ||
| 160 | for (i = 0; i < SDL_arraysize(x11libs); i++) { | ||
| 161 | if (x11libs[i].libname) { | ||
| 162 | x11libs[i].lib = SDL_LoadObject(x11libs[i].libname); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | #define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes | ||
| 167 | #include "SDL_x11sym.h" | ||
| 168 | |||
| 169 | #define SDL_X11_MODULE(modname) thismod = &SDL_X11_HAVE_##modname; | ||
| 170 | #define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)X11_GetSym(#fn, thismod); | ||
| 171 | #include "SDL_x11sym.h" | ||
| 172 | |||
| 173 | #ifdef X_HAVE_UTF8_STRING | ||
| 174 | X11_XCreateIC = (SDL_DYNX11FN_XCreateIC) | ||
| 175 | X11_GetSym("XCreateIC", &SDL_X11_HAVE_UTF8); | ||
| 176 | X11_XGetICValues = (SDL_DYNX11FN_XGetICValues) | ||
| 177 | X11_GetSym("XGetICValues", &SDL_X11_HAVE_UTF8); | ||
| 178 | X11_XSetICValues = (SDL_DYNX11FN_XSetICValues) | ||
| 179 | X11_GetSym("XSetICValues", &SDL_X11_HAVE_UTF8); | ||
| 180 | X11_XVaCreateNestedList = (SDL_DYNX11FN_XVaCreateNestedList) | ||
| 181 | X11_GetSym("XVaCreateNestedList", &SDL_X11_HAVE_UTF8); | ||
| 182 | #endif | ||
| 183 | |||
| 184 | if (SDL_X11_HAVE_BASEXLIB) { | ||
| 185 | // all required symbols loaded. | ||
| 186 | SDL_ClearError(); | ||
| 187 | } else { | ||
| 188 | // in case something got loaded... | ||
| 189 | SDL_X11_UnloadSymbols(); | ||
| 190 | result = false; | ||
| 191 | } | ||
| 192 | |||
| 193 | #else // no dynamic X11 | ||
| 194 | |||
| 195 | #define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes | ||
| 196 | #define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)fn; | ||
| 197 | #include "SDL_x11sym.h" | ||
| 198 | |||
| 199 | #ifdef X_HAVE_UTF8_STRING | ||
| 200 | X11_XCreateIC = XCreateIC; | ||
| 201 | X11_XGetICValues = XGetICValues; | ||
| 202 | X11_XSetICValues = XSetICValues; | ||
| 203 | X11_XVaCreateNestedList = XVaCreateNestedList; | ||
| 204 | #endif | ||
| 205 | #endif | ||
| 206 | } | ||
| 207 | |||
| 208 | return result; | ||
| 209 | } | ||
| 210 | |||
| 211 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h new file mode 100644 index 0000000..e9831fc --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11dyn.h | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11dyn_h_ | ||
| 24 | #define SDL_x11dyn_h_ | ||
| 25 | |||
| 26 | #include <X11/Xlib.h> | ||
| 27 | #include <X11/Xutil.h> | ||
| 28 | #include <X11/Xatom.h> | ||
| 29 | #include <X11/Xresource.h> | ||
| 30 | |||
| 31 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 32 | #include <X11/XKBlib.h> | ||
| 33 | #endif | ||
| 34 | |||
| 35 | // Apparently some X11 systems can't include this multiple times... | ||
| 36 | #ifndef SDL_INCLUDED_XLIBINT_H | ||
| 37 | #define SDL_INCLUDED_XLIBINT_H 1 | ||
| 38 | #include <X11/Xlibint.h> | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #include <X11/Xproto.h> | ||
| 42 | #include <X11/extensions/Xext.h> | ||
| 43 | |||
| 44 | #ifndef NO_SHARED_MEMORY | ||
| 45 | #include <sys/ipc.h> | ||
| 46 | #include <sys/shm.h> | ||
| 47 | #include <X11/extensions/XShm.h> | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 51 | #include <X11/Xcursor/Xcursor.h> | ||
| 52 | #endif | ||
| 53 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 54 | #include <X11/extensions/Xdbe.h> | ||
| 55 | #endif | ||
| 56 | #if defined(SDL_VIDEO_DRIVER_X11_XINPUT2) || defined(SDL_VIDEO_DRIVER_X11_XFIXES) | ||
| 57 | #include <X11/extensions/XInput2.h> | ||
| 58 | #endif | ||
| 59 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 60 | #include <X11/extensions/Xfixes.h> | ||
| 61 | #endif | ||
| 62 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 63 | #include <X11/extensions/sync.h> | ||
| 64 | #endif | ||
| 65 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 66 | #include <X11/extensions/Xrandr.h> | ||
| 67 | #endif | ||
| 68 | #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER | ||
| 69 | #include <X11/extensions/scrnsaver.h> | ||
| 70 | #endif | ||
| 71 | #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 72 | #include <X11/extensions/shape.h> | ||
| 73 | #endif | ||
| 74 | |||
| 75 | #ifdef __cplusplus | ||
| 76 | extern "C" { | ||
| 77 | #endif | ||
| 78 | |||
| 79 | // evil function signatures... | ||
| 80 | typedef Bool (*SDL_X11_XESetWireToEventRetType)(Display *, XEvent *, xEvent *); | ||
| 81 | typedef int (*SDL_X11_XSynchronizeRetType)(Display *); | ||
| 82 | typedef Status (*SDL_X11_XESetEventToWireRetType)(Display *, XEvent *, xEvent *); | ||
| 83 | |||
| 84 | extern bool SDL_X11_LoadSymbols(void); | ||
| 85 | extern void SDL_X11_UnloadSymbols(void); | ||
| 86 | |||
| 87 | // Declare all the function pointers and wrappers... | ||
| 88 | #define SDL_X11_SYM(rc, fn, params, args, ret) \ | ||
| 89 | typedef rc(*SDL_DYNX11FN_##fn) params; \ | ||
| 90 | extern SDL_DYNX11FN_##fn X11_##fn; | ||
| 91 | #include "SDL_x11sym.h" | ||
| 92 | |||
| 93 | // Annoying varargs entry point... | ||
| 94 | #ifdef X_HAVE_UTF8_STRING | ||
| 95 | typedef XIC (*SDL_DYNX11FN_XCreateIC)(XIM, ...); | ||
| 96 | typedef char *(*SDL_DYNX11FN_XGetICValues)(XIC, ...); | ||
| 97 | typedef char *(*SDL_DYNX11FN_XSetICValues)(XIC, ...); | ||
| 98 | typedef XVaNestedList (*SDL_DYNX11FN_XVaCreateNestedList)(int, ...); | ||
| 99 | extern SDL_DYNX11FN_XCreateIC X11_XCreateIC; | ||
| 100 | extern SDL_DYNX11FN_XGetICValues X11_XGetICValues; | ||
| 101 | extern SDL_DYNX11FN_XSetICValues X11_XSetICValues; | ||
| 102 | extern SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList; | ||
| 103 | #endif | ||
| 104 | |||
| 105 | /* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */ | ||
| 106 | #define SDL_X11_MODULE(modname) extern int SDL_X11_HAVE_##modname; | ||
| 107 | #include "SDL_x11sym.h" | ||
| 108 | |||
| 109 | #ifdef __cplusplus | ||
| 110 | } | ||
| 111 | #endif | ||
| 112 | |||
| 113 | #endif // !defined SDL_x11dyn_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c new file mode 100644 index 0000000..02c2d90 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.c | |||
| @@ -0,0 +1,2205 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include <sys/types.h> | ||
| 26 | #include <sys/time.h> | ||
| 27 | #include <signal.h> | ||
| 28 | #include <unistd.h> | ||
| 29 | #include <limits.h> // For INT_MAX | ||
| 30 | |||
| 31 | #include "SDL_x11video.h" | ||
| 32 | #include "SDL_x11pen.h" | ||
| 33 | #include "SDL_x11touch.h" | ||
| 34 | #include "SDL_x11xinput2.h" | ||
| 35 | #include "SDL_x11xfixes.h" | ||
| 36 | #include "SDL_x11settings.h" | ||
| 37 | #include "../SDL_clipboard_c.h" | ||
| 38 | #include "SDL_x11xsync.h" | ||
| 39 | #include "../../core/unix/SDL_poll.h" | ||
| 40 | #include "../../events/SDL_events_c.h" | ||
| 41 | #include "../../events/SDL_mouse_c.h" | ||
| 42 | #include "../../events/SDL_touch_c.h" | ||
| 43 | #include "../../core/linux/SDL_system_theme.h" | ||
| 44 | #include "../SDL_sysvideo.h" | ||
| 45 | |||
| 46 | #include <stdio.h> | ||
| 47 | |||
| 48 | #if 0 | ||
| 49 | #define DEBUG_XEVENTS | ||
| 50 | #endif | ||
| 51 | |||
| 52 | #ifndef _NET_WM_MOVERESIZE_SIZE_TOPLEFT | ||
| 53 | #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 | ||
| 54 | #endif | ||
| 55 | |||
| 56 | #ifndef _NET_WM_MOVERESIZE_SIZE_TOP | ||
| 57 | #define _NET_WM_MOVERESIZE_SIZE_TOP 1 | ||
| 58 | #endif | ||
| 59 | |||
| 60 | #ifndef _NET_WM_MOVERESIZE_SIZE_TOPRIGHT | ||
| 61 | #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 | ||
| 62 | #endif | ||
| 63 | |||
| 64 | #ifndef _NET_WM_MOVERESIZE_SIZE_RIGHT | ||
| 65 | #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 | ||
| 66 | #endif | ||
| 67 | |||
| 68 | #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT | ||
| 69 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 | ||
| 70 | #endif | ||
| 71 | |||
| 72 | #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOM | ||
| 73 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 | ||
| 74 | #endif | ||
| 75 | |||
| 76 | #ifndef _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT | ||
| 77 | #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 | ||
| 78 | #endif | ||
| 79 | |||
| 80 | #ifndef _NET_WM_MOVERESIZE_SIZE_LEFT | ||
| 81 | #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 | ||
| 82 | #endif | ||
| 83 | |||
| 84 | #ifndef _NET_WM_MOVERESIZE_MOVE | ||
| 85 | #define _NET_WM_MOVERESIZE_MOVE 8 | ||
| 86 | #endif | ||
| 87 | |||
| 88 | typedef struct | ||
| 89 | { | ||
| 90 | unsigned char *data; | ||
| 91 | int format, count; | ||
| 92 | Atom type; | ||
| 93 | } SDL_x11Prop; | ||
| 94 | |||
| 95 | /* Reads property | ||
| 96 | Must call X11_XFree on results | ||
| 97 | */ | ||
| 98 | static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) | ||
| 99 | { | ||
| 100 | unsigned char *ret = NULL; | ||
| 101 | Atom type; | ||
| 102 | int fmt; | ||
| 103 | unsigned long count; | ||
| 104 | unsigned long bytes_left; | ||
| 105 | int bytes_fetch = 0; | ||
| 106 | |||
| 107 | do { | ||
| 108 | if (ret) { | ||
| 109 | X11_XFree(ret); | ||
| 110 | } | ||
| 111 | X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); | ||
| 112 | bytes_fetch += bytes_left; | ||
| 113 | } while (bytes_left != 0); | ||
| 114 | |||
| 115 | p->data = ret; | ||
| 116 | p->format = fmt; | ||
| 117 | p->count = count; | ||
| 118 | p->type = type; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Find text-uri-list in a list of targets and return it's atom | ||
| 122 | if available, else return None */ | ||
| 123 | static Atom X11_PickTarget(Display *disp, Atom list[], int list_count) | ||
| 124 | { | ||
| 125 | Atom request = None; | ||
| 126 | char *name; | ||
| 127 | int i; | ||
| 128 | for (i = 0; i < list_count && request == None; i++) { | ||
| 129 | name = X11_XGetAtomName(disp, list[i]); | ||
| 130 | // Preferred MIME targets | ||
| 131 | if ((SDL_strcmp("text/uri-list", name) == 0) || | ||
| 132 | (SDL_strcmp("text/plain;charset=utf-8", name) == 0) || | ||
| 133 | (SDL_strcmp("UTF8_STRING", name) == 0)) { | ||
| 134 | request = list[i]; | ||
| 135 | } | ||
| 136 | // Fallback MIME targets | ||
| 137 | if ((SDL_strcmp("text/plain", name) == 0) || | ||
| 138 | (SDL_strcmp("TEXT", name) == 0)) { | ||
| 139 | if (request == None) { | ||
| 140 | request = list[i]; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | X11_XFree(name); | ||
| 144 | } | ||
| 145 | return request; | ||
| 146 | } | ||
| 147 | |||
| 148 | /* Wrapper for X11_PickTarget for a maximum of three targets, a special | ||
| 149 | case in the Xdnd protocol */ | ||
| 150 | static Atom X11_PickTargetFromAtoms(Display *disp, Atom a0, Atom a1, Atom a2) | ||
| 151 | { | ||
| 152 | int count = 0; | ||
| 153 | Atom atom[3]; | ||
| 154 | if (a0 != None) { | ||
| 155 | atom[count++] = a0; | ||
| 156 | } | ||
| 157 | if (a1 != None) { | ||
| 158 | atom[count++] = a1; | ||
| 159 | } | ||
| 160 | if (a2 != None) { | ||
| 161 | atom[count++] = a2; | ||
| 162 | } | ||
| 163 | return X11_PickTarget(disp, atom, count); | ||
| 164 | } | ||
| 165 | |||
| 166 | struct KeyRepeatCheckData | ||
| 167 | { | ||
| 168 | XEvent *event; | ||
| 169 | bool found; | ||
| 170 | }; | ||
| 171 | |||
| 172 | static Bool X11_KeyRepeatCheckIfEvent(Display *display, XEvent *chkev, | ||
| 173 | XPointer arg) | ||
| 174 | { | ||
| 175 | struct KeyRepeatCheckData *d = (struct KeyRepeatCheckData *)arg; | ||
| 176 | if (chkev->type == KeyPress && chkev->xkey.keycode == d->event->xkey.keycode && chkev->xkey.time - d->event->xkey.time < 2) { | ||
| 177 | d->found = true; | ||
| 178 | } | ||
| 179 | return False; | ||
| 180 | } | ||
| 181 | |||
| 182 | /* Check to see if this is a repeated key. | ||
| 183 | (idea shamelessly lifted from GII -- thanks guys! :) | ||
| 184 | */ | ||
| 185 | static bool X11_KeyRepeat(Display *display, XEvent *event) | ||
| 186 | { | ||
| 187 | XEvent dummyev; | ||
| 188 | struct KeyRepeatCheckData d; | ||
| 189 | d.event = event; | ||
| 190 | d.found = false; | ||
| 191 | if (X11_XPending(display)) { | ||
| 192 | X11_XCheckIfEvent(display, &dummyev, X11_KeyRepeatCheckIfEvent, (XPointer)&d); | ||
| 193 | } | ||
| 194 | return d.found; | ||
| 195 | } | ||
| 196 | |||
| 197 | static bool X11_IsWheelEvent(Display *display, int button, int *xticks, int *yticks) | ||
| 198 | { | ||
| 199 | /* according to the xlib docs, no specific mouse wheel events exist. | ||
| 200 | However, the defacto standard is that the vertical wheel is X buttons | ||
| 201 | 4 (up) and 5 (down) and a horizontal wheel is 6 (left) and 7 (right). */ | ||
| 202 | |||
| 203 | // Xlib defines "Button1" through 5, so we just use literals here. | ||
| 204 | switch (button) { | ||
| 205 | case 4: | ||
| 206 | *yticks = 1; | ||
| 207 | return true; | ||
| 208 | case 5: | ||
| 209 | *yticks = -1; | ||
| 210 | return true; | ||
| 211 | case 6: | ||
| 212 | *xticks = 1; | ||
| 213 | return true; | ||
| 214 | case 7: | ||
| 215 | *xticks = -1; | ||
| 216 | return true; | ||
| 217 | default: | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | return false; | ||
| 221 | } | ||
| 222 | |||
| 223 | // An X11 event hook | ||
| 224 | static SDL_X11EventHook g_X11EventHook = NULL; | ||
| 225 | static void *g_X11EventHookData = NULL; | ||
| 226 | |||
| 227 | void SDL_SetX11EventHook(SDL_X11EventHook callback, void *userdata) | ||
| 228 | { | ||
| 229 | g_X11EventHook = callback; | ||
| 230 | g_X11EventHookData = userdata; | ||
| 231 | } | ||
| 232 | |||
| 233 | #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS | ||
| 234 | static void X11_HandleGenericEvent(SDL_VideoDevice *_this, XEvent *xev) | ||
| 235 | { | ||
| 236 | SDL_VideoData *videodata = _this->internal; | ||
| 237 | |||
| 238 | // event is a union, so cookie == &event, but this is type safe. | ||
| 239 | XGenericEventCookie *cookie = &xev->xcookie; | ||
| 240 | if (X11_XGetEventData(videodata->display, cookie)) { | ||
| 241 | if (!g_X11EventHook || g_X11EventHook(g_X11EventHookData, xev)) { | ||
| 242 | X11_HandleXinput2Event(_this, cookie); | ||
| 243 | } | ||
| 244 | X11_XFreeEventData(videodata->display, cookie); | ||
| 245 | } | ||
| 246 | } | ||
| 247 | #endif // SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS | ||
| 248 | |||
| 249 | static void X11_UpdateSystemKeyModifiers(SDL_VideoData *viddata) | ||
| 250 | { | ||
| 251 | Window junk_window; | ||
| 252 | int x, y; | ||
| 253 | |||
| 254 | X11_XQueryPointer(viddata->display, DefaultRootWindow(viddata->display), &junk_window, &junk_window, &x, &y, &x, &y, &viddata->xkb.xkb_modifiers); | ||
| 255 | } | ||
| 256 | |||
| 257 | static void X11_ReconcileModifiers(SDL_VideoData *viddata) | ||
| 258 | { | ||
| 259 | const Uint32 xk_modifiers = viddata->xkb.xkb_modifiers; | ||
| 260 | |||
| 261 | /* If a modifier was activated by a keypress, it will be tied to the | ||
| 262 | * specific left/right key that initiated it. Otherwise, the ambiguous | ||
| 263 | * left/right combo is used. | ||
| 264 | */ | ||
| 265 | if (xk_modifiers & ShiftMask) { | ||
| 266 | if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_SHIFT)) { | ||
| 267 | viddata->xkb.sdl_modifiers |= SDL_KMOD_SHIFT; | ||
| 268 | } | ||
| 269 | } else { | ||
| 270 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SHIFT; | ||
| 271 | } | ||
| 272 | |||
| 273 | if (xk_modifiers & ControlMask) { | ||
| 274 | if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_CTRL)) { | ||
| 275 | viddata->xkb.sdl_modifiers |= SDL_KMOD_CTRL; | ||
| 276 | } | ||
| 277 | } else { | ||
| 278 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CTRL; | ||
| 279 | } | ||
| 280 | |||
| 281 | // Mod1 is used for the Alt keys | ||
| 282 | if (xk_modifiers & Mod1Mask) { | ||
| 283 | if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_ALT)) { | ||
| 284 | viddata->xkb.sdl_modifiers |= SDL_KMOD_ALT; | ||
| 285 | } | ||
| 286 | } else { | ||
| 287 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_ALT; | ||
| 288 | } | ||
| 289 | |||
| 290 | // Mod4 is used for the Super (aka GUI/Logo) keys. | ||
| 291 | if (xk_modifiers & Mod4Mask) { | ||
| 292 | if (!(viddata->xkb.sdl_modifiers & SDL_KMOD_GUI)) { | ||
| 293 | viddata->xkb.sdl_modifiers |= SDL_KMOD_GUI; | ||
| 294 | } | ||
| 295 | } else { | ||
| 296 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_GUI; | ||
| 297 | } | ||
| 298 | |||
| 299 | // Mod3 is typically Level 5 shift. | ||
| 300 | if (xk_modifiers & Mod3Mask) { | ||
| 301 | viddata->xkb.sdl_modifiers |= SDL_KMOD_LEVEL5; | ||
| 302 | } else { | ||
| 303 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_LEVEL5; | ||
| 304 | } | ||
| 305 | |||
| 306 | // Mod5 is typically Level 3 shift (aka AltGr). | ||
| 307 | if (xk_modifiers & Mod5Mask) { | ||
| 308 | viddata->xkb.sdl_modifiers |= SDL_KMOD_MODE; | ||
| 309 | } else { | ||
| 310 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_MODE; | ||
| 311 | } | ||
| 312 | |||
| 313 | if (xk_modifiers & LockMask) { | ||
| 314 | viddata->xkb.sdl_modifiers |= SDL_KMOD_CAPS; | ||
| 315 | } else { | ||
| 316 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_CAPS; | ||
| 317 | } | ||
| 318 | |||
| 319 | if (xk_modifiers & viddata->xkb.numlock_mask) { | ||
| 320 | viddata->xkb.sdl_modifiers |= SDL_KMOD_NUM; | ||
| 321 | } else { | ||
| 322 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_NUM; | ||
| 323 | } | ||
| 324 | |||
| 325 | if (xk_modifiers & viddata->xkb.scrolllock_mask) { | ||
| 326 | viddata->xkb.sdl_modifiers |= SDL_KMOD_SCROLL; | ||
| 327 | } else { | ||
| 328 | viddata->xkb.sdl_modifiers &= ~SDL_KMOD_SCROLL; | ||
| 329 | } | ||
| 330 | |||
| 331 | SDL_SetModState(viddata->xkb.sdl_modifiers); | ||
| 332 | } | ||
| 333 | |||
| 334 | static void X11_HandleModifierKeys(SDL_VideoData *viddata, SDL_Scancode scancode, bool pressed, bool allow_reconciliation) | ||
| 335 | { | ||
| 336 | const SDL_Keycode keycode = SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false); | ||
| 337 | SDL_Keymod mod = SDL_KMOD_NONE; | ||
| 338 | bool reconcile = false; | ||
| 339 | |||
| 340 | /* SDL clients expect modifier state to be activated at the same time as the | ||
| 341 | * source keypress, so we set pressed modifier state with the usual modifier | ||
| 342 | * keys here, as the explicit modifier event won't arrive until after the | ||
| 343 | * keypress event. If this is wrong, it will be corrected when the explicit | ||
| 344 | * modifier state is checked. | ||
| 345 | */ | ||
| 346 | switch (keycode) { | ||
| 347 | case SDLK_LSHIFT: | ||
| 348 | mod = SDL_KMOD_LSHIFT; | ||
| 349 | break; | ||
| 350 | case SDLK_RSHIFT: | ||
| 351 | mod = SDL_KMOD_RSHIFT; | ||
| 352 | break; | ||
| 353 | case SDLK_LCTRL: | ||
| 354 | mod = SDL_KMOD_LCTRL; | ||
| 355 | break; | ||
| 356 | case SDLK_RCTRL: | ||
| 357 | mod = SDL_KMOD_RCTRL; | ||
| 358 | break; | ||
| 359 | case SDLK_LALT: | ||
| 360 | mod = SDL_KMOD_LALT; | ||
| 361 | break; | ||
| 362 | case SDLK_RALT: | ||
| 363 | mod = SDL_KMOD_RALT; | ||
| 364 | break; | ||
| 365 | case SDLK_LGUI: | ||
| 366 | mod = SDL_KMOD_LGUI; | ||
| 367 | break; | ||
| 368 | case SDLK_RGUI: | ||
| 369 | mod = SDL_KMOD_RGUI; | ||
| 370 | break; | ||
| 371 | case SDLK_MODE: | ||
| 372 | mod = SDL_KMOD_MODE; | ||
| 373 | break; | ||
| 374 | case SDLK_LEVEL5_SHIFT: | ||
| 375 | mod = SDL_KMOD_LEVEL5; | ||
| 376 | break; | ||
| 377 | case SDLK_CAPSLOCK: | ||
| 378 | case SDLK_NUMLOCKCLEAR: | ||
| 379 | case SDLK_SCROLLLOCK: | ||
| 380 | { | ||
| 381 | /* For locking modifier keys, query the lock state directly, or we may have to wait until the next | ||
| 382 | * key press event to know if a lock was actually activated from the key event. | ||
| 383 | */ | ||
| 384 | unsigned int cur_mask = viddata->xkb.xkb_modifiers; | ||
| 385 | X11_UpdateSystemKeyModifiers(viddata); | ||
| 386 | |||
| 387 | if (viddata->xkb.xkb_modifiers & LockMask) { | ||
| 388 | cur_mask |= LockMask; | ||
| 389 | } else { | ||
| 390 | cur_mask &= ~LockMask; | ||
| 391 | } | ||
| 392 | if (viddata->xkb.xkb_modifiers & viddata->xkb.numlock_mask) { | ||
| 393 | cur_mask |= viddata->xkb.numlock_mask; | ||
| 394 | } else { | ||
| 395 | cur_mask &= ~viddata->xkb.numlock_mask; | ||
| 396 | } | ||
| 397 | if (viddata->xkb.xkb_modifiers & viddata->xkb.scrolllock_mask) { | ||
| 398 | cur_mask |= viddata->xkb.scrolllock_mask; | ||
| 399 | } else { | ||
| 400 | cur_mask &= ~viddata->xkb.scrolllock_mask; | ||
| 401 | } | ||
| 402 | |||
| 403 | viddata->xkb.xkb_modifiers = cur_mask; | ||
| 404 | } SDL_FALLTHROUGH; | ||
| 405 | default: | ||
| 406 | reconcile = true; | ||
| 407 | break; | ||
| 408 | } | ||
| 409 | |||
| 410 | if (pressed) { | ||
| 411 | viddata->xkb.sdl_modifiers |= mod; | ||
| 412 | } else { | ||
| 413 | viddata->xkb.sdl_modifiers &= ~mod; | ||
| 414 | } | ||
| 415 | |||
| 416 | if (allow_reconciliation) { | ||
| 417 | if (reconcile) { | ||
| 418 | X11_ReconcileModifiers(viddata); | ||
| 419 | } else { | ||
| 420 | SDL_SetModState(viddata->xkb.sdl_modifiers); | ||
| 421 | } | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | void X11_ReconcileKeyboardState(SDL_VideoDevice *_this) | ||
| 426 | { | ||
| 427 | SDL_VideoData *videodata = _this->internal; | ||
| 428 | Display *display = videodata->display; | ||
| 429 | char keys[32]; | ||
| 430 | int keycode; | ||
| 431 | const bool *keyboardState; | ||
| 432 | |||
| 433 | X11_XQueryKeymap(display, keys); | ||
| 434 | |||
| 435 | keyboardState = SDL_GetKeyboardState(0); | ||
| 436 | for (keycode = 0; keycode < SDL_arraysize(videodata->key_layout); ++keycode) { | ||
| 437 | SDL_Scancode scancode = videodata->key_layout[keycode]; | ||
| 438 | bool x11KeyPressed = (keys[keycode / 8] & (1 << (keycode % 8))) != 0; | ||
| 439 | bool sdlKeyPressed = keyboardState[scancode]; | ||
| 440 | |||
| 441 | if (x11KeyPressed && !sdlKeyPressed) { | ||
| 442 | // Only update modifier state for keys that are pressed in another application | ||
| 443 | switch (SDL_GetKeyFromScancode(scancode, SDL_KMOD_NONE, false)) { | ||
| 444 | case SDLK_LCTRL: | ||
| 445 | case SDLK_RCTRL: | ||
| 446 | case SDLK_LSHIFT: | ||
| 447 | case SDLK_RSHIFT: | ||
| 448 | case SDLK_LALT: | ||
| 449 | case SDLK_RALT: | ||
| 450 | case SDLK_LGUI: | ||
| 451 | case SDLK_RGUI: | ||
| 452 | case SDLK_MODE: | ||
| 453 | case SDLK_LEVEL5_SHIFT: | ||
| 454 | X11_HandleModifierKeys(videodata, scancode, true, false); | ||
| 455 | SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, true); | ||
| 456 | break; | ||
| 457 | default: | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | } else if (!x11KeyPressed && sdlKeyPressed) { | ||
| 461 | X11_HandleModifierKeys(videodata, scancode, false, false); | ||
| 462 | SDL_SendKeyboardKeyIgnoreModifiers(0, SDL_GLOBAL_KEYBOARD_ID, keycode, scancode, false); | ||
| 463 | } | ||
| 464 | } | ||
| 465 | |||
| 466 | X11_UpdateSystemKeyModifiers(videodata); | ||
| 467 | X11_ReconcileModifiers(videodata); | ||
| 468 | } | ||
| 469 | |||
| 470 | static void X11_DispatchFocusIn(SDL_VideoDevice *_this, SDL_WindowData *data) | ||
| 471 | { | ||
| 472 | #ifdef DEBUG_XEVENTS | ||
| 473 | SDL_Log("window 0x%lx: Dispatching FocusIn", data->xwindow); | ||
| 474 | #endif | ||
| 475 | SDL_SetKeyboardFocus(data->window); | ||
| 476 | X11_ReconcileKeyboardState(_this); | ||
| 477 | #ifdef X_HAVE_UTF8_STRING | ||
| 478 | if (data->ic) { | ||
| 479 | X11_XSetICFocus(data->ic); | ||
| 480 | } | ||
| 481 | #endif | ||
| 482 | if (data->flashing_window) { | ||
| 483 | X11_FlashWindow(_this, data->window, SDL_FLASH_CANCEL); | ||
| 484 | } | ||
| 485 | } | ||
| 486 | |||
| 487 | static void X11_DispatchFocusOut(SDL_VideoDevice *_this, SDL_WindowData *data) | ||
| 488 | { | ||
| 489 | #ifdef DEBUG_XEVENTS | ||
| 490 | SDL_Log("window 0x%lx: Dispatching FocusOut", data->xwindow); | ||
| 491 | #endif | ||
| 492 | /* If another window has already processed a focus in, then don't try to | ||
| 493 | * remove focus here. Doing so will incorrectly remove focus from that | ||
| 494 | * window, and the focus lost event for this window will have already | ||
| 495 | * been dispatched anyway. */ | ||
| 496 | if (data->window == SDL_GetKeyboardFocus()) { | ||
| 497 | SDL_SetKeyboardFocus(NULL); | ||
| 498 | } | ||
| 499 | #ifdef X_HAVE_UTF8_STRING | ||
| 500 | if (data->ic) { | ||
| 501 | X11_XUnsetICFocus(data->ic); | ||
| 502 | } | ||
| 503 | #endif | ||
| 504 | } | ||
| 505 | |||
| 506 | static void X11_DispatchMapNotify(SDL_WindowData *data) | ||
| 507 | { | ||
| 508 | SDL_Window *window = data->window; | ||
| 509 | SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESTORED, 0, 0); | ||
| 510 | SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SHOWN, 0, 0); | ||
| 511 | if (!(window->flags & SDL_WINDOW_HIDDEN) && (window->flags & SDL_WINDOW_INPUT_FOCUS)) { | ||
| 512 | SDL_UpdateWindowGrab(window); | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | static void X11_DispatchUnmapNotify(SDL_WindowData *data) | ||
| 517 | { | ||
| 518 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_HIDDEN, 0, 0); | ||
| 519 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); | ||
| 520 | } | ||
| 521 | |||
| 522 | static void DispatchWindowMove(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point) | ||
| 523 | { | ||
| 524 | SDL_VideoData *videodata = _this->internal; | ||
| 525 | SDL_Window *window = data->window; | ||
| 526 | Display *display = videodata->display; | ||
| 527 | XEvent evt; | ||
| 528 | |||
| 529 | // !!! FIXME: we need to regrab this if necessary when the drag is done. | ||
| 530 | X11_XUngrabPointer(display, 0L); | ||
| 531 | X11_XFlush(display); | ||
| 532 | |||
| 533 | evt.xclient.type = ClientMessage; | ||
| 534 | evt.xclient.window = data->xwindow; | ||
| 535 | evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; | ||
| 536 | evt.xclient.format = 32; | ||
| 537 | evt.xclient.data.l[0] = (size_t)window->x + point->x; | ||
| 538 | evt.xclient.data.l[1] = (size_t)window->y + point->y; | ||
| 539 | evt.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE; | ||
| 540 | evt.xclient.data.l[3] = Button1; | ||
| 541 | evt.xclient.data.l[4] = 0; | ||
| 542 | X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); | ||
| 543 | |||
| 544 | X11_XSync(display, 0); | ||
| 545 | } | ||
| 546 | |||
| 547 | static void ScheduleWindowMove(SDL_VideoDevice *_this, SDL_WindowData *data, const SDL_Point *point) | ||
| 548 | { | ||
| 549 | data->pending_move = true; | ||
| 550 | data->pending_move_point = *point; | ||
| 551 | } | ||
| 552 | |||
| 553 | static void InitiateWindowResize(SDL_VideoDevice *_this, const SDL_WindowData *data, const SDL_Point *point, int direction) | ||
| 554 | { | ||
| 555 | SDL_VideoData *videodata = _this->internal; | ||
| 556 | SDL_Window *window = data->window; | ||
| 557 | Display *display = videodata->display; | ||
| 558 | XEvent evt; | ||
| 559 | |||
| 560 | if (direction < _NET_WM_MOVERESIZE_SIZE_TOPLEFT || direction > _NET_WM_MOVERESIZE_SIZE_LEFT) { | ||
| 561 | return; | ||
| 562 | } | ||
| 563 | |||
| 564 | // !!! FIXME: we need to regrab this if necessary when the drag is done. | ||
| 565 | X11_XUngrabPointer(display, 0L); | ||
| 566 | X11_XFlush(display); | ||
| 567 | |||
| 568 | evt.xclient.type = ClientMessage; | ||
| 569 | evt.xclient.window = data->xwindow; | ||
| 570 | evt.xclient.message_type = videodata->atoms._NET_WM_MOVERESIZE; | ||
| 571 | evt.xclient.format = 32; | ||
| 572 | evt.xclient.data.l[0] = (size_t)window->x + point->x; | ||
| 573 | evt.xclient.data.l[1] = (size_t)window->y + point->y; | ||
| 574 | evt.xclient.data.l[2] = direction; | ||
| 575 | evt.xclient.data.l[3] = Button1; | ||
| 576 | evt.xclient.data.l[4] = 0; | ||
| 577 | X11_XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &evt); | ||
| 578 | |||
| 579 | X11_XSync(display, 0); | ||
| 580 | } | ||
| 581 | |||
| 582 | bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result) | ||
| 583 | { | ||
| 584 | SDL_Window *window = data->window; | ||
| 585 | if (!window->hit_test) return false; | ||
| 586 | const SDL_Point point = { (int)x, (int)y }; | ||
| 587 | SDL_HitTestResult rc = window->hit_test(window, &point, window->hit_test_data); | ||
| 588 | if (!force_new_result && rc == data->hit_test_result) { | ||
| 589 | return true; | ||
| 590 | } | ||
| 591 | X11_SetHitTestCursor(rc); | ||
| 592 | data->hit_test_result = rc; | ||
| 593 | return true; | ||
| 594 | } | ||
| 595 | |||
| 596 | bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y) | ||
| 597 | { | ||
| 598 | SDL_Window *window = data->window; | ||
| 599 | |||
| 600 | if (window->hit_test) { | ||
| 601 | const SDL_Point point = { (int)x, (int)y }; | ||
| 602 | static const int directions[] = { | ||
| 603 | _NET_WM_MOVERESIZE_SIZE_TOPLEFT, _NET_WM_MOVERESIZE_SIZE_TOP, | ||
| 604 | _NET_WM_MOVERESIZE_SIZE_TOPRIGHT, _NET_WM_MOVERESIZE_SIZE_RIGHT, | ||
| 605 | _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, _NET_WM_MOVERESIZE_SIZE_BOTTOM, | ||
| 606 | _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT, _NET_WM_MOVERESIZE_SIZE_LEFT | ||
| 607 | }; | ||
| 608 | |||
| 609 | switch (data->hit_test_result) { | ||
| 610 | case SDL_HITTEST_DRAGGABLE: | ||
| 611 | /* Some window managers get in a bad state when a move event starts while input is transitioning | ||
| 612 | to the SDL window. This can happen when clicking on a drag region of an unfocused window | ||
| 613 | where the same mouse down event will trigger a drag event and a window activate. */ | ||
| 614 | if (data->window->flags & SDL_WINDOW_INPUT_FOCUS) { | ||
| 615 | DispatchWindowMove(_this, data, &point); | ||
| 616 | } else { | ||
| 617 | ScheduleWindowMove(_this, data, &point); | ||
| 618 | } | ||
| 619 | return true; | ||
| 620 | |||
| 621 | case SDL_HITTEST_RESIZE_TOPLEFT: | ||
| 622 | case SDL_HITTEST_RESIZE_TOP: | ||
| 623 | case SDL_HITTEST_RESIZE_TOPRIGHT: | ||
| 624 | case SDL_HITTEST_RESIZE_RIGHT: | ||
| 625 | case SDL_HITTEST_RESIZE_BOTTOMRIGHT: | ||
| 626 | case SDL_HITTEST_RESIZE_BOTTOM: | ||
| 627 | case SDL_HITTEST_RESIZE_BOTTOMLEFT: | ||
| 628 | case SDL_HITTEST_RESIZE_LEFT: | ||
| 629 | InitiateWindowResize(_this, data, &point, directions[data->hit_test_result - SDL_HITTEST_RESIZE_TOPLEFT]); | ||
| 630 | return true; | ||
| 631 | |||
| 632 | default: | ||
| 633 | return false; | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | return false; | ||
| 638 | } | ||
| 639 | |||
| 640 | static void X11_UpdateUserTime(SDL_WindowData *data, const unsigned long latest) | ||
| 641 | { | ||
| 642 | if (latest && (latest != data->user_time)) { | ||
| 643 | SDL_VideoData *videodata = data->videodata; | ||
| 644 | Display *display = videodata->display; | ||
| 645 | X11_XChangeProperty(display, data->xwindow, videodata->atoms._NET_WM_USER_TIME, | ||
| 646 | XA_CARDINAL, 32, PropModeReplace, | ||
| 647 | (const unsigned char *)&latest, 1); | ||
| 648 | #ifdef DEBUG_XEVENTS | ||
| 649 | SDL_Log("window 0x%lx: updating _NET_WM_USER_TIME to %lu", data->xwindow, latest); | ||
| 650 | #endif | ||
| 651 | data->user_time = latest; | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | static void X11_HandleClipboardEvent(SDL_VideoDevice *_this, const XEvent *xevent) | ||
| 656 | { | ||
| 657 | int i; | ||
| 658 | SDL_VideoData *videodata = _this->internal; | ||
| 659 | Display *display = videodata->display; | ||
| 660 | |||
| 661 | SDL_assert(videodata->clipboard_window != None); | ||
| 662 | SDL_assert(xevent->xany.window == videodata->clipboard_window); | ||
| 663 | |||
| 664 | switch (xevent->type) { | ||
| 665 | // Copy the selection from our own CUTBUFFER to the requested property | ||
| 666 | case SelectionRequest: | ||
| 667 | { | ||
| 668 | const XSelectionRequestEvent *req = &xevent->xselectionrequest; | ||
| 669 | XEvent sevent; | ||
| 670 | int mime_formats; | ||
| 671 | unsigned char *seln_data; | ||
| 672 | size_t seln_length = 0; | ||
| 673 | Atom XA_TARGETS = videodata->atoms.TARGETS; | ||
| 674 | SDLX11_ClipboardData *clipboard; | ||
| 675 | |||
| 676 | #ifdef DEBUG_XEVENTS | ||
| 677 | char *atom_name; | ||
| 678 | atom_name = X11_XGetAtomName(display, req->target); | ||
| 679 | SDL_Log("window CLIPBOARD: SelectionRequest (requestor = 0x%lx, target = 0x%lx, mime_type = %s)", | ||
| 680 | req->requestor, req->target, atom_name); | ||
| 681 | if (atom_name) { | ||
| 682 | X11_XFree(atom_name); | ||
| 683 | } | ||
| 684 | #endif | ||
| 685 | |||
| 686 | if (req->selection == XA_PRIMARY) { | ||
| 687 | clipboard = &videodata->primary_selection; | ||
| 688 | } else { | ||
| 689 | clipboard = &videodata->clipboard; | ||
| 690 | } | ||
| 691 | |||
| 692 | SDL_zero(sevent); | ||
| 693 | sevent.xany.type = SelectionNotify; | ||
| 694 | sevent.xselection.selection = req->selection; | ||
| 695 | sevent.xselection.target = None; | ||
| 696 | sevent.xselection.property = None; // tell them no by default | ||
| 697 | sevent.xselection.requestor = req->requestor; | ||
| 698 | sevent.xselection.time = req->time; | ||
| 699 | |||
| 700 | /* !!! FIXME: We were probably storing this on the root window | ||
| 701 | because an SDL window might go away...? but we don't have to do | ||
| 702 | this now (or ever, really). */ | ||
| 703 | |||
| 704 | if (req->target == XA_TARGETS) { | ||
| 705 | Atom *supportedFormats; | ||
| 706 | supportedFormats = SDL_malloc((clipboard->mime_count + 1) * sizeof(Atom)); | ||
| 707 | supportedFormats[0] = XA_TARGETS; | ||
| 708 | mime_formats = 1; | ||
| 709 | for (i = 0; i < clipboard->mime_count; ++i) { | ||
| 710 | supportedFormats[mime_formats++] = X11_XInternAtom(display, clipboard->mime_types[i], False); | ||
| 711 | } | ||
| 712 | X11_XChangeProperty(display, req->requestor, req->property, | ||
| 713 | XA_ATOM, 32, PropModeReplace, | ||
| 714 | (unsigned char *)supportedFormats, | ||
| 715 | mime_formats); | ||
| 716 | sevent.xselection.property = req->property; | ||
| 717 | sevent.xselection.target = XA_TARGETS; | ||
| 718 | SDL_free(supportedFormats); | ||
| 719 | } else { | ||
| 720 | if (clipboard->callback) { | ||
| 721 | for (i = 0; i < clipboard->mime_count; ++i) { | ||
| 722 | const char *mime_type = clipboard->mime_types[i]; | ||
| 723 | if (X11_XInternAtom(display, mime_type, False) != req->target) { | ||
| 724 | continue; | ||
| 725 | } | ||
| 726 | |||
| 727 | // FIXME: We don't support the X11 INCR protocol for large clipboards. Do we want that? - Yes, yes we do. | ||
| 728 | // This is a safe cast, XChangeProperty() doesn't take a const value, but it doesn't modify the data | ||
| 729 | seln_data = (unsigned char *)clipboard->callback(clipboard->userdata, mime_type, &seln_length); | ||
| 730 | if (seln_data) { | ||
| 731 | X11_XChangeProperty(display, req->requestor, req->property, | ||
| 732 | req->target, 8, PropModeReplace, | ||
| 733 | seln_data, seln_length); | ||
| 734 | sevent.xselection.property = req->property; | ||
| 735 | sevent.xselection.target = req->target; | ||
| 736 | } | ||
| 737 | break; | ||
| 738 | } | ||
| 739 | } | ||
| 740 | } | ||
| 741 | X11_XSendEvent(display, req->requestor, False, 0, &sevent); | ||
| 742 | X11_XSync(display, False); | ||
| 743 | } break; | ||
| 744 | |||
| 745 | case SelectionNotify: | ||
| 746 | { | ||
| 747 | const XSelectionEvent *xsel = &xevent->xselection; | ||
| 748 | #ifdef DEBUG_XEVENTS | ||
| 749 | const char *propName = xsel->property ? X11_XGetAtomName(display, xsel->property) : "None"; | ||
| 750 | const char *targetName = xsel->target ? X11_XGetAtomName(display, xsel->target) : "None"; | ||
| 751 | |||
| 752 | SDL_Log("window CLIPBOARD: SelectionNotify (requestor = 0x%lx, target = %s, property = %s)", | ||
| 753 | xsel->requestor, targetName, propName); | ||
| 754 | #endif | ||
| 755 | if (xsel->target == videodata->atoms.TARGETS && xsel->property == videodata->atoms.SDL_FORMATS) { | ||
| 756 | /* the new mime formats are the SDL_FORMATS property as an array of Atoms */ | ||
| 757 | Atom atom = None; | ||
| 758 | Atom *patom; | ||
| 759 | unsigned char* data = NULL; | ||
| 760 | int format_property = 0; | ||
| 761 | unsigned long length = 0; | ||
| 762 | unsigned long bytes_left = 0; | ||
| 763 | int j; | ||
| 764 | |||
| 765 | X11_XGetWindowProperty(display, GetWindow(_this), videodata->atoms.SDL_FORMATS, 0, 200, | ||
| 766 | 0, XA_ATOM, &atom, &format_property, &length, &bytes_left, &data); | ||
| 767 | |||
| 768 | int allocationsize = (length + 1) * sizeof(char*); | ||
| 769 | for (j = 0, patom = (Atom*)data; j < length; j++, patom++) { | ||
| 770 | char *atomStr = X11_XGetAtomName(display, *patom); | ||
| 771 | allocationsize += SDL_strlen(atomStr) + 1; | ||
| 772 | X11_XFree(atomStr); | ||
| 773 | } | ||
| 774 | |||
| 775 | char **new_mime_types = SDL_AllocateTemporaryMemory(allocationsize); | ||
| 776 | if (new_mime_types) { | ||
| 777 | char *strPtr = (char *)(new_mime_types + length + 1); | ||
| 778 | |||
| 779 | for (j = 0, patom = (Atom*)data; j < length; j++, patom++) { | ||
| 780 | char *atomStr = X11_XGetAtomName(display, *patom); | ||
| 781 | new_mime_types[j] = strPtr; | ||
| 782 | strPtr = stpcpy(strPtr, atomStr) + 1; | ||
| 783 | X11_XFree(atomStr); | ||
| 784 | } | ||
| 785 | new_mime_types[length] = NULL; | ||
| 786 | |||
| 787 | SDL_SendClipboardUpdate(false, new_mime_types, length); | ||
| 788 | } | ||
| 789 | |||
| 790 | if (data) { | ||
| 791 | X11_XFree(data); | ||
| 792 | } | ||
| 793 | } | ||
| 794 | |||
| 795 | videodata->selection_waiting = false; | ||
| 796 | } break; | ||
| 797 | |||
| 798 | case SelectionClear: | ||
| 799 | { | ||
| 800 | Atom XA_CLIPBOARD = videodata->atoms.CLIPBOARD; | ||
| 801 | SDLX11_ClipboardData *clipboard = NULL; | ||
| 802 | |||
| 803 | #ifdef DEBUG_XEVENTS | ||
| 804 | SDL_Log("window CLIPBOARD: SelectionClear (requestor = 0x%lx, target = 0x%lx)", | ||
| 805 | xevent->xselection.requestor, xevent->xselection.target); | ||
| 806 | #endif | ||
| 807 | |||
| 808 | if (xevent->xselectionclear.selection == XA_PRIMARY) { | ||
| 809 | clipboard = &videodata->primary_selection; | ||
| 810 | } else if (XA_CLIPBOARD != None && xevent->xselectionclear.selection == XA_CLIPBOARD) { | ||
| 811 | clipboard = &videodata->clipboard; | ||
| 812 | } | ||
| 813 | if (clipboard && clipboard->callback) { | ||
| 814 | if (clipboard->sequence) { | ||
| 815 | SDL_CancelClipboardData(clipboard->sequence); | ||
| 816 | } else { | ||
| 817 | SDL_free(clipboard->userdata); | ||
| 818 | } | ||
| 819 | SDL_zerop(clipboard); | ||
| 820 | } | ||
| 821 | } break; | ||
| 822 | |||
| 823 | case PropertyNotify: | ||
| 824 | { | ||
| 825 | char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); | ||
| 826 | |||
| 827 | if (SDL_strncmp(name_of_atom, "SDL_SELECTION", sizeof("SDL_SELECTION") - 1) == 0 && xevent->xproperty.state == PropertyNewValue) { | ||
| 828 | videodata->selection_incr_waiting = false; | ||
| 829 | } | ||
| 830 | |||
| 831 | if (name_of_atom) { | ||
| 832 | X11_XFree(name_of_atom); | ||
| 833 | } | ||
| 834 | } break; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | |||
| 838 | static void X11_HandleSettingsEvent(SDL_VideoDevice *_this, const XEvent *xevent) | ||
| 839 | { | ||
| 840 | SDL_VideoData *videodata = _this->internal; | ||
| 841 | |||
| 842 | SDL_assert(videodata->xsettings_window != None); | ||
| 843 | SDL_assert(xevent->xany.window == videodata->xsettings_window); | ||
| 844 | |||
| 845 | X11_HandleXsettings(_this, xevent); | ||
| 846 | } | ||
| 847 | |||
| 848 | static Bool isMapNotify(Display *display, XEvent *ev, XPointer arg) | ||
| 849 | { | ||
| 850 | XUnmapEvent *unmap; | ||
| 851 | |||
| 852 | unmap = (XUnmapEvent *)arg; | ||
| 853 | |||
| 854 | return ev->type == MapNotify && | ||
| 855 | ev->xmap.window == unmap->window && | ||
| 856 | ev->xmap.serial == unmap->serial; | ||
| 857 | } | ||
| 858 | |||
| 859 | static Bool isReparentNotify(Display *display, XEvent *ev, XPointer arg) | ||
| 860 | { | ||
| 861 | XUnmapEvent *unmap; | ||
| 862 | |||
| 863 | unmap = (XUnmapEvent *)arg; | ||
| 864 | |||
| 865 | return ev->type == ReparentNotify && | ||
| 866 | ev->xreparent.window == unmap->window && | ||
| 867 | ev->xreparent.serial == unmap->serial; | ||
| 868 | } | ||
| 869 | |||
| 870 | static bool IsHighLatin1(const char *string, int length) | ||
| 871 | { | ||
| 872 | while (length-- > 0) { | ||
| 873 | Uint8 ch = (Uint8)*string; | ||
| 874 | if (ch >= 0x80) { | ||
| 875 | return true; | ||
| 876 | } | ||
| 877 | ++string; | ||
| 878 | } | ||
| 879 | return false; | ||
| 880 | } | ||
| 881 | |||
| 882 | static int XLookupStringAsUTF8(XKeyEvent *event_struct, char *buffer_return, int bytes_buffer, KeySym *keysym_return, XComposeStatus *status_in_out) | ||
| 883 | { | ||
| 884 | int result = X11_XLookupString(event_struct, buffer_return, bytes_buffer, keysym_return, status_in_out); | ||
| 885 | if (IsHighLatin1(buffer_return, result)) { | ||
| 886 | char *utf8_text = SDL_iconv_string("UTF-8", "ISO-8859-1", buffer_return, result + 1); | ||
| 887 | if (utf8_text) { | ||
| 888 | SDL_strlcpy(buffer_return, utf8_text, bytes_buffer); | ||
| 889 | SDL_free(utf8_text); | ||
| 890 | return SDL_strlen(buffer_return); | ||
| 891 | } else { | ||
| 892 | return 0; | ||
| 893 | } | ||
| 894 | } | ||
| 895 | return result; | ||
| 896 | } | ||
| 897 | |||
| 898 | SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window) | ||
| 899 | { | ||
| 900 | const SDL_VideoData *videodata = _this->internal; | ||
| 901 | int i; | ||
| 902 | |||
| 903 | if (videodata && videodata->windowlist) { | ||
| 904 | for (i = 0; i < videodata->numwindows; ++i) { | ||
| 905 | if ((videodata->windowlist[i] != NULL) && | ||
| 906 | (videodata->windowlist[i]->xwindow == window)) { | ||
| 907 | return videodata->windowlist[i]; | ||
| 908 | } | ||
| 909 | } | ||
| 910 | } | ||
| 911 | return NULL; | ||
| 912 | } | ||
| 913 | |||
| 914 | Uint64 X11_GetEventTimestamp(unsigned long time) | ||
| 915 | { | ||
| 916 | // FIXME: Get the event time in the SDL tick time base | ||
| 917 | return SDL_GetTicksNS(); | ||
| 918 | } | ||
| 919 | |||
| 920 | void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent) | ||
| 921 | { | ||
| 922 | SDL_VideoData *videodata = _this->internal; | ||
| 923 | Display *display = videodata->display; | ||
| 924 | KeyCode keycode = xevent->xkey.keycode; | ||
| 925 | KeySym keysym = NoSymbol; | ||
| 926 | int text_length = 0; | ||
| 927 | char text[64]; | ||
| 928 | Status status = 0; | ||
| 929 | bool handled_by_ime = false; | ||
| 930 | bool pressed = (xevent->type == KeyPress); | ||
| 931 | SDL_Scancode scancode = videodata->key_layout[keycode]; | ||
| 932 | Uint64 timestamp = X11_GetEventTimestamp(xevent->xkey.time); | ||
| 933 | |||
| 934 | #ifdef DEBUG_XEVENTS | ||
| 935 | SDL_Log("window 0x%lx %s (X11 keycode = 0x%X)", xevent->xany.window, (xevent->type == KeyPress ? "KeyPress" : "KeyRelease"), xevent->xkey.keycode); | ||
| 936 | #endif | ||
| 937 | #ifdef DEBUG_SCANCODES | ||
| 938 | if (scancode == SDL_SCANCODE_UNKNOWN && keycode) { | ||
| 939 | int min_keycode, max_keycode; | ||
| 940 | X11_XDisplayKeycodes(display, &min_keycode, &max_keycode); | ||
| 941 | keysym = X11_KeyCodeToSym(_this, keycode, xevent->xkey.state >> 13); | ||
| 942 | SDL_Log("The key you just pressed is not recognized by SDL. To help get this fixed, please report this to the SDL forums/mailing list <https://discourse.libsdl.org/> X11 KeyCode %d (%d), X11 KeySym 0x%lX (%s).", | ||
| 943 | keycode, keycode - min_keycode, keysym, | ||
| 944 | X11_XKeysymToString(keysym)); | ||
| 945 | } | ||
| 946 | #endif // DEBUG SCANCODES | ||
| 947 | |||
| 948 | text[0] = '\0'; | ||
| 949 | videodata->xkb.xkb_modifiers = xevent->xkey.state; | ||
| 950 | |||
| 951 | if (SDL_TextInputActive(windowdata->window)) { | ||
| 952 | // filter events catches XIM events and sends them to the correct handler | ||
| 953 | if (X11_XFilterEvent(xevent, None)) { | ||
| 954 | #ifdef DEBUG_XEVENTS | ||
| 955 | SDL_Log("Filtered event type = %d display = %p window = 0x%lx", | ||
| 956 | xevent->type, xevent->xany.display, xevent->xany.window); | ||
| 957 | #endif | ||
| 958 | handled_by_ime = true; | ||
| 959 | } | ||
| 960 | |||
| 961 | if (!handled_by_ime) { | ||
| 962 | #ifdef X_HAVE_UTF8_STRING | ||
| 963 | if (windowdata->ic && xevent->type == KeyPress) { | ||
| 964 | text_length = X11_Xutf8LookupString(windowdata->ic, &xevent->xkey, text, sizeof(text) - 1, | ||
| 965 | &keysym, &status); | ||
| 966 | } else { | ||
| 967 | text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); | ||
| 968 | } | ||
| 969 | #else | ||
| 970 | text_length = XLookupStringAsUTF8(&xevent->xkey, text, sizeof(text) - 1, &keysym, NULL); | ||
| 971 | #endif | ||
| 972 | } | ||
| 973 | } | ||
| 974 | |||
| 975 | if (!handled_by_ime) { | ||
| 976 | if (pressed) { | ||
| 977 | X11_HandleModifierKeys(videodata, scancode, true, true); | ||
| 978 | SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, true); | ||
| 979 | |||
| 980 | if (*text) { | ||
| 981 | text[text_length] = '\0'; | ||
| 982 | X11_ClearComposition(windowdata); | ||
| 983 | SDL_SendKeyboardText(text); | ||
| 984 | } | ||
| 985 | } else { | ||
| 986 | if (X11_KeyRepeat(display, xevent)) { | ||
| 987 | // We're about to get a repeated key down, ignore the key up | ||
| 988 | return; | ||
| 989 | } | ||
| 990 | |||
| 991 | X11_HandleModifierKeys(videodata, scancode, false, true); | ||
| 992 | SDL_SendKeyboardKeyIgnoreModifiers(timestamp, keyboardID, keycode, scancode, false); | ||
| 993 | } | ||
| 994 | } | ||
| 995 | |||
| 996 | if (pressed) { | ||
| 997 | X11_UpdateUserTime(windowdata, xevent->xkey.time); | ||
| 998 | } | ||
| 999 | } | ||
| 1000 | |||
| 1001 | void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time) | ||
| 1002 | { | ||
| 1003 | SDL_Window *window = windowdata->window; | ||
| 1004 | const SDL_VideoData *videodata = _this->internal; | ||
| 1005 | Display *display = videodata->display; | ||
| 1006 | int xticks = 0, yticks = 0; | ||
| 1007 | Uint64 timestamp = X11_GetEventTimestamp(time); | ||
| 1008 | |||
| 1009 | #ifdef DEBUG_XEVENTS | ||
| 1010 | SDL_Log("window 0x%lx: ButtonPress (X11 button = %d)", windowdata->xwindow, button); | ||
| 1011 | #endif | ||
| 1012 | |||
| 1013 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 1014 | if (!mouse->relative_mode && (x != mouse->x || y != mouse->y)) { | ||
| 1015 | X11_ProcessHitTest(_this, windowdata, x, y, false); | ||
| 1016 | SDL_SendMouseMotion(timestamp, window, mouseID, false, x, y); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | if (X11_IsWheelEvent(display, button, &xticks, &yticks)) { | ||
| 1020 | SDL_SendMouseWheel(timestamp, window, mouseID, (float)-xticks, (float)yticks, SDL_MOUSEWHEEL_NORMAL); | ||
| 1021 | } else { | ||
| 1022 | bool ignore_click = false; | ||
| 1023 | if (button > 7) { | ||
| 1024 | /* X button values 4-7 are used for scrolling, so X1 is 8, X2 is 9, ... | ||
| 1025 | => subtract (8-SDL_BUTTON_X1) to get value SDL expects */ | ||
| 1026 | button -= (8 - SDL_BUTTON_X1); | ||
| 1027 | } | ||
| 1028 | if (button == Button1) { | ||
| 1029 | if (X11_TriggerHitTestAction(_this, windowdata, x, y)) { | ||
| 1030 | SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_HIT_TEST, 0, 0); | ||
| 1031 | return; // don't pass this event on to app. | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | if (windowdata->last_focus_event_time) { | ||
| 1035 | const int X11_FOCUS_CLICK_TIMEOUT = 10; | ||
| 1036 | if (SDL_GetTicks() < (windowdata->last_focus_event_time + X11_FOCUS_CLICK_TIMEOUT)) { | ||
| 1037 | ignore_click = !SDL_GetHintBoolean(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, false); | ||
| 1038 | } | ||
| 1039 | windowdata->last_focus_event_time = 0; | ||
| 1040 | } | ||
| 1041 | if (!ignore_click) { | ||
| 1042 | SDL_SendMouseButton(timestamp, window, mouseID, button, true); | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | X11_UpdateUserTime(windowdata, time); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time) | ||
| 1049 | { | ||
| 1050 | SDL_Window *window = windowdata->window; | ||
| 1051 | const SDL_VideoData *videodata = _this->internal; | ||
| 1052 | Display *display = videodata->display; | ||
| 1053 | // The X server sends a Release event for each Press for wheels. Ignore them. | ||
| 1054 | int xticks = 0, yticks = 0; | ||
| 1055 | Uint64 timestamp = X11_GetEventTimestamp(time); | ||
| 1056 | |||
| 1057 | #ifdef DEBUG_XEVENTS | ||
| 1058 | SDL_Log("window 0x%lx: ButtonRelease (X11 button = %d)", windowdata->xwindow, button); | ||
| 1059 | #endif | ||
| 1060 | if (!X11_IsWheelEvent(display, button, &xticks, &yticks)) { | ||
| 1061 | if (button > 7) { | ||
| 1062 | // see explanation at case ButtonPress | ||
| 1063 | button -= (8 - SDL_BUTTON_X1); | ||
| 1064 | } | ||
| 1065 | SDL_SendMouseButton(timestamp, window, mouseID, button, false); | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | void X11_GetBorderValues(SDL_WindowData *data) | ||
| 1070 | { | ||
| 1071 | SDL_VideoData *videodata = data->videodata; | ||
| 1072 | Display *display = videodata->display; | ||
| 1073 | |||
| 1074 | Atom type; | ||
| 1075 | int format; | ||
| 1076 | unsigned long nitems, bytes_after; | ||
| 1077 | unsigned char *property; | ||
| 1078 | |||
| 1079 | // Some compositors will send extents even when the border hint is turned off. Ignore them in this case. | ||
| 1080 | if (!(data->window->flags & SDL_WINDOW_BORDERLESS)) { | ||
| 1081 | if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms._NET_FRAME_EXTENTS, 0, 16, 0, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &property) == Success) { | ||
| 1082 | if (type != None && nitems == 4) { | ||
| 1083 | data->border_left = (int)((long *)property)[0]; | ||
| 1084 | data->border_right = (int)((long *)property)[1]; | ||
| 1085 | data->border_top = (int)((long *)property)[2]; | ||
| 1086 | data->border_bottom = (int)((long *)property)[3]; | ||
| 1087 | } | ||
| 1088 | X11_XFree(property); | ||
| 1089 | |||
| 1090 | #ifdef DEBUG_XEVENTS | ||
| 1091 | SDL_Log("New _NET_FRAME_EXTENTS: left=%d right=%d, top=%d, bottom=%d", data->border_left, data->border_right, data->border_top, data->border_bottom); | ||
| 1092 | #endif | ||
| 1093 | } | ||
| 1094 | } else { | ||
| 1095 | data->border_left = data->border_top = data->border_right = data->border_bottom = 0; | ||
| 1096 | } | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) | ||
| 1100 | { | ||
| 1101 | SDL_VideoData *videodata = _this->internal; | ||
| 1102 | Display *display; | ||
| 1103 | SDL_WindowData *data; | ||
| 1104 | XClientMessageEvent m; | ||
| 1105 | int i; | ||
| 1106 | |||
| 1107 | SDL_assert(videodata != NULL); | ||
| 1108 | display = videodata->display; | ||
| 1109 | |||
| 1110 | // filter events catches XIM events and sends them to the correct handler | ||
| 1111 | // Key press/release events are filtered in X11_HandleKeyEvent() | ||
| 1112 | if (xevent->type != KeyPress && xevent->type != KeyRelease) { | ||
| 1113 | if (X11_XFilterEvent(xevent, None)) { | ||
| 1114 | #ifdef DEBUG_XEVENTS | ||
| 1115 | SDL_Log("Filtered event type = %d display = %p window = 0x%lx", | ||
| 1116 | xevent->type, xevent->xany.display, xevent->xany.window); | ||
| 1117 | #endif | ||
| 1118 | return; | ||
| 1119 | } | ||
| 1120 | } | ||
| 1121 | |||
| 1122 | #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS | ||
| 1123 | if (xevent->type == GenericEvent) { | ||
| 1124 | X11_HandleGenericEvent(_this, xevent); | ||
| 1125 | return; | ||
| 1126 | } | ||
| 1127 | #endif | ||
| 1128 | |||
| 1129 | // Calling the event hook for generic events happens in X11_HandleGenericEvent(), where the event data is available | ||
| 1130 | if (g_X11EventHook) { | ||
| 1131 | if (!g_X11EventHook(g_X11EventHookData, xevent)) { | ||
| 1132 | return; | ||
| 1133 | } | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 1137 | if (videodata->xrandr_event_base && (xevent->type == (videodata->xrandr_event_base + RRNotify))) { | ||
| 1138 | X11_HandleXRandREvent(_this, xevent); | ||
| 1139 | } | ||
| 1140 | #endif | ||
| 1141 | |||
| 1142 | #ifdef DEBUG_XEVENTS | ||
| 1143 | SDL_Log("X11 event type = %d display = %p window = 0x%lx", | ||
| 1144 | xevent->type, xevent->xany.display, xevent->xany.window); | ||
| 1145 | #endif | ||
| 1146 | |||
| 1147 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1148 | if (SDL_X11_HAVE_XFIXES && | ||
| 1149 | xevent->type == X11_GetXFixesSelectionNotifyEvent()) { | ||
| 1150 | XFixesSelectionNotifyEvent *ev = (XFixesSelectionNotifyEvent *)xevent; | ||
| 1151 | |||
| 1152 | #ifdef DEBUG_XEVENTS | ||
| 1153 | SDL_Log("window CLIPBOARD: XFixesSelectionNotify (selection = %s)", | ||
| 1154 | X11_XGetAtomName(display, ev->selection)); | ||
| 1155 | #endif | ||
| 1156 | |||
| 1157 | if (ev->subtype == XFixesSetSelectionOwnerNotify) | ||
| 1158 | { | ||
| 1159 | if (ev->selection != videodata->atoms.CLIPBOARD) | ||
| 1160 | return; | ||
| 1161 | |||
| 1162 | if (X11_XGetSelectionOwner(display, ev->selection) == videodata->clipboard_window) | ||
| 1163 | return; | ||
| 1164 | |||
| 1165 | /* when here we're notified that the clipboard had an external change, we request the | ||
| 1166 | * available mime types by asking for a conversion to the TARGETS format. We should get a | ||
| 1167 | * SelectionNotify event later, and when treating these results, we will push a ClipboardUpdated | ||
| 1168 | * event | ||
| 1169 | */ | ||
| 1170 | |||
| 1171 | X11_XConvertSelection(display, videodata->atoms.CLIPBOARD, videodata->atoms.TARGETS, | ||
| 1172 | videodata->atoms.SDL_FORMATS, GetWindow(_this), CurrentTime); | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | return; | ||
| 1176 | } | ||
| 1177 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1178 | |||
| 1179 | if ((videodata->clipboard_window != None) && | ||
| 1180 | (videodata->clipboard_window == xevent->xany.window)) { | ||
| 1181 | X11_HandleClipboardEvent(_this, xevent); | ||
| 1182 | return; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | if ((videodata->xsettings_window != None) && | ||
| 1186 | (videodata->xsettings_window == xevent->xany.window)) { | ||
| 1187 | X11_HandleSettingsEvent(_this, xevent); | ||
| 1188 | return; | ||
| 1189 | } | ||
| 1190 | |||
| 1191 | data = X11_FindWindow(_this, xevent->xany.window); | ||
| 1192 | |||
| 1193 | if (!data) { | ||
| 1194 | // The window for KeymapNotify, etc events is 0 | ||
| 1195 | if (xevent->type == KeymapNotify) { | ||
| 1196 | #ifdef DEBUG_XEVENTS | ||
| 1197 | SDL_Log("window 0x%lx: KeymapNotify!", xevent->xany.window); | ||
| 1198 | #endif | ||
| 1199 | if (SDL_GetKeyboardFocus() != NULL) { | ||
| 1200 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 1201 | if (videodata->xkb.desc_ptr) { | ||
| 1202 | XkbStateRec state; | ||
| 1203 | if (X11_XkbGetState(videodata->display, XkbUseCoreKbd, &state) == Success) { | ||
| 1204 | if (state.group != videodata->xkb.current_group) { | ||
| 1205 | // Only rebuild the keymap if the layout has changed. | ||
| 1206 | videodata->xkb.current_group = state.group; | ||
| 1207 | X11_UpdateKeymap(_this, true); | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | } | ||
| 1211 | #endif | ||
| 1212 | X11_ReconcileKeyboardState(_this); | ||
| 1213 | } | ||
| 1214 | } else if (xevent->type == MappingNotify) { | ||
| 1215 | // Has the keyboard layout changed? | ||
| 1216 | const int request = xevent->xmapping.request; | ||
| 1217 | |||
| 1218 | #ifdef DEBUG_XEVENTS | ||
| 1219 | SDL_Log("window 0x%lx: MappingNotify!", xevent->xany.window); | ||
| 1220 | #endif | ||
| 1221 | if ((request == MappingKeyboard) || (request == MappingModifier)) { | ||
| 1222 | X11_XRefreshKeyboardMapping(&xevent->xmapping); | ||
| 1223 | } | ||
| 1224 | |||
| 1225 | X11_UpdateKeymap(_this, true); | ||
| 1226 | } else if (xevent->type == PropertyNotify && videodata && videodata->windowlist) { | ||
| 1227 | char *name_of_atom = X11_XGetAtomName(display, xevent->xproperty.atom); | ||
| 1228 | |||
| 1229 | if (SDL_strncmp(name_of_atom, "_ICC_PROFILE", sizeof("_ICC_PROFILE") - 1) == 0) { | ||
| 1230 | XWindowAttributes attrib; | ||
| 1231 | int screennum; | ||
| 1232 | for (i = 0; i < videodata->numwindows; ++i) { | ||
| 1233 | if (videodata->windowlist[i] != NULL) { | ||
| 1234 | data = videodata->windowlist[i]; | ||
| 1235 | X11_XGetWindowAttributes(display, data->xwindow, &attrib); | ||
| 1236 | screennum = X11_XScreenNumberOfScreen(attrib.screen); | ||
| 1237 | if (screennum == 0 && SDL_strcmp(name_of_atom, "_ICC_PROFILE") == 0) { | ||
| 1238 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); | ||
| 1239 | } else if (SDL_strncmp(name_of_atom, "_ICC_PROFILE_", sizeof("_ICC_PROFILE_") - 1) == 0 && SDL_strlen(name_of_atom) > sizeof("_ICC_PROFILE_") - 1) { | ||
| 1240 | int iccscreennum = SDL_atoi(&name_of_atom[sizeof("_ICC_PROFILE_") - 1]); | ||
| 1241 | |||
| 1242 | if (screennum == iccscreennum) { | ||
| 1243 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ICCPROF_CHANGED, 0, 0); | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | if (name_of_atom) { | ||
| 1251 | X11_XFree(name_of_atom); | ||
| 1252 | } | ||
| 1253 | } | ||
| 1254 | return; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | switch (xevent->type) { | ||
| 1258 | |||
| 1259 | // Gaining mouse coverage? | ||
| 1260 | case EnterNotify: | ||
| 1261 | { | ||
| 1262 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 1263 | #ifdef DEBUG_XEVENTS | ||
| 1264 | SDL_Log("window 0x%lx: EnterNotify! (%d,%d,%d)", xevent->xany.window, | ||
| 1265 | xevent->xcrossing.x, | ||
| 1266 | xevent->xcrossing.y, | ||
| 1267 | xevent->xcrossing.mode); | ||
| 1268 | if (xevent->xcrossing.mode == NotifyGrab) { | ||
| 1269 | SDL_Log("Mode: NotifyGrab"); | ||
| 1270 | } | ||
| 1271 | if (xevent->xcrossing.mode == NotifyUngrab) { | ||
| 1272 | SDL_Log("Mode: NotifyUngrab"); | ||
| 1273 | } | ||
| 1274 | #endif | ||
| 1275 | SDL_SetMouseFocus(data->window); | ||
| 1276 | |||
| 1277 | mouse->last_x = xevent->xcrossing.x; | ||
| 1278 | mouse->last_y = xevent->xcrossing.y; | ||
| 1279 | |||
| 1280 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1281 | { | ||
| 1282 | // Only create the barriers if we have input focus | ||
| 1283 | SDL_WindowData *windowdata = data->window->internal; | ||
| 1284 | if ((data->pointer_barrier_active == true) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) { | ||
| 1285 | X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | #endif | ||
| 1289 | |||
| 1290 | if (!mouse->relative_mode) { | ||
| 1291 | SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); | ||
| 1292 | } | ||
| 1293 | |||
| 1294 | // We ungrab in LeaveNotify, so we may need to grab again here | ||
| 1295 | SDL_UpdateWindowGrab(data->window); | ||
| 1296 | |||
| 1297 | X11_ProcessHitTest(_this, data, mouse->last_x, mouse->last_y, true); | ||
| 1298 | } break; | ||
| 1299 | // Losing mouse coverage? | ||
| 1300 | case LeaveNotify: | ||
| 1301 | { | ||
| 1302 | #ifdef DEBUG_XEVENTS | ||
| 1303 | SDL_Log("window 0x%lx: LeaveNotify! (%d,%d,%d)", xevent->xany.window, | ||
| 1304 | xevent->xcrossing.x, | ||
| 1305 | xevent->xcrossing.y, | ||
| 1306 | xevent->xcrossing.mode); | ||
| 1307 | if (xevent->xcrossing.mode == NotifyGrab) { | ||
| 1308 | SDL_Log("Mode: NotifyGrab"); | ||
| 1309 | } | ||
| 1310 | if (xevent->xcrossing.mode == NotifyUngrab) { | ||
| 1311 | SDL_Log("Mode: NotifyUngrab"); | ||
| 1312 | } | ||
| 1313 | #endif | ||
| 1314 | if (!SDL_GetMouse()->relative_mode) { | ||
| 1315 | SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xcrossing.x, (float)xevent->xcrossing.y); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | if (xevent->xcrossing.mode != NotifyGrab && | ||
| 1319 | xevent->xcrossing.mode != NotifyUngrab && | ||
| 1320 | xevent->xcrossing.detail != NotifyInferior) { | ||
| 1321 | |||
| 1322 | /* In order for interaction with the window decorations and menu to work properly | ||
| 1323 | on Mutter, we need to ungrab the keyboard when the the mouse leaves. */ | ||
| 1324 | if (!(data->window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1325 | X11_SetWindowKeyboardGrab(_this, data->window, false); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | SDL_SetMouseFocus(NULL); | ||
| 1329 | } | ||
| 1330 | } break; | ||
| 1331 | |||
| 1332 | // Gaining input focus? | ||
| 1333 | case FocusIn: | ||
| 1334 | { | ||
| 1335 | if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { | ||
| 1336 | // Someone is handling a global hotkey, ignore it | ||
| 1337 | #ifdef DEBUG_XEVENTS | ||
| 1338 | SDL_Log("window 0x%lx: FocusIn (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); | ||
| 1339 | #endif | ||
| 1340 | break; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { | ||
| 1344 | #ifdef DEBUG_XEVENTS | ||
| 1345 | SDL_Log("window 0x%lx: FocusIn (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); | ||
| 1346 | #endif | ||
| 1347 | break; | ||
| 1348 | } | ||
| 1349 | #ifdef DEBUG_XEVENTS | ||
| 1350 | SDL_Log("window 0x%lx: FocusIn!", xevent->xany.window); | ||
| 1351 | #endif | ||
| 1352 | if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { | ||
| 1353 | data->pending_focus = PENDING_FOCUS_NONE; | ||
| 1354 | data->pending_focus_time = 0; | ||
| 1355 | X11_DispatchFocusIn(_this, data); | ||
| 1356 | } else { | ||
| 1357 | data->pending_focus = PENDING_FOCUS_IN; | ||
| 1358 | data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; | ||
| 1359 | } | ||
| 1360 | data->last_focus_event_time = SDL_GetTicks(); | ||
| 1361 | } break; | ||
| 1362 | |||
| 1363 | // Losing input focus? | ||
| 1364 | case FocusOut: | ||
| 1365 | { | ||
| 1366 | if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) { | ||
| 1367 | // Someone is handling a global hotkey, ignore it | ||
| 1368 | #ifdef DEBUG_XEVENTS | ||
| 1369 | SDL_Log("window 0x%lx: FocusOut (NotifyGrab/NotifyUngrab, ignoring)", xevent->xany.window); | ||
| 1370 | #endif | ||
| 1371 | break; | ||
| 1372 | } | ||
| 1373 | if (xevent->xfocus.detail == NotifyInferior || xevent->xfocus.detail == NotifyPointer) { | ||
| 1374 | /* We still have focus if a child gets focus. We also don't | ||
| 1375 | care about the position of the pointer when the keyboard | ||
| 1376 | focus changed. */ | ||
| 1377 | #ifdef DEBUG_XEVENTS | ||
| 1378 | SDL_Log("window 0x%lx: FocusOut (NotifyInferior/NotifyPointer, ignoring)", xevent->xany.window); | ||
| 1379 | #endif | ||
| 1380 | break; | ||
| 1381 | } | ||
| 1382 | #ifdef DEBUG_XEVENTS | ||
| 1383 | SDL_Log("window 0x%lx: FocusOut!", xevent->xany.window); | ||
| 1384 | #endif | ||
| 1385 | if (!videodata->last_mode_change_deadline) /* no recent mode changes */ { | ||
| 1386 | data->pending_focus = PENDING_FOCUS_NONE; | ||
| 1387 | data->pending_focus_time = 0; | ||
| 1388 | X11_DispatchFocusOut(_this, data); | ||
| 1389 | } else { | ||
| 1390 | data->pending_focus = PENDING_FOCUS_OUT; | ||
| 1391 | data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1395 | // Disable confinement if it is activated. | ||
| 1396 | if (data->pointer_barrier_active == true) { | ||
| 1397 | X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); | ||
| 1398 | } | ||
| 1399 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1400 | } break; | ||
| 1401 | |||
| 1402 | |||
| 1403 | // Have we been iconified? | ||
| 1404 | case UnmapNotify: | ||
| 1405 | { | ||
| 1406 | XEvent ev; | ||
| 1407 | |||
| 1408 | #ifdef DEBUG_XEVENTS | ||
| 1409 | SDL_Log("window 0x%lx: UnmapNotify!", xevent->xany.window); | ||
| 1410 | #endif | ||
| 1411 | |||
| 1412 | if (X11_XCheckIfEvent(display, &ev, &isReparentNotify, (XPointer)&xevent->xunmap)) { | ||
| 1413 | X11_XCheckIfEvent(display, &ev, &isMapNotify, (XPointer)&xevent->xunmap); | ||
| 1414 | } else { | ||
| 1415 | X11_DispatchUnmapNotify(data); | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1419 | // Disable confinement if the window gets hidden. | ||
| 1420 | if (data->pointer_barrier_active == true) { | ||
| 1421 | X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT); | ||
| 1422 | } | ||
| 1423 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1424 | } break; | ||
| 1425 | |||
| 1426 | // Have we been restored? | ||
| 1427 | case MapNotify: | ||
| 1428 | { | ||
| 1429 | #ifdef DEBUG_XEVENTS | ||
| 1430 | SDL_Log("window 0x%lx: MapNotify!", xevent->xany.window); | ||
| 1431 | #endif | ||
| 1432 | X11_DispatchMapNotify(data); | ||
| 1433 | |||
| 1434 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1435 | // Enable confinement if it was activated. | ||
| 1436 | if (data->pointer_barrier_active == true) { | ||
| 1437 | X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT); | ||
| 1438 | } | ||
| 1439 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 1440 | } break; | ||
| 1441 | |||
| 1442 | // Have we been resized or moved? | ||
| 1443 | case ConfigureNotify: | ||
| 1444 | { | ||
| 1445 | #ifdef DEBUG_XEVENTS | ||
| 1446 | SDL_Log("window 0x%lx: ConfigureNotify! (position: %d,%d, size: %dx%d)", xevent->xany.window, | ||
| 1447 | xevent->xconfigure.x, xevent->xconfigure.y, | ||
| 1448 | xevent->xconfigure.width, xevent->xconfigure.height); | ||
| 1449 | #endif | ||
| 1450 | // Real configure notify events are relative to the parent, synthetic events are absolute. | ||
| 1451 | if (!xevent->xconfigure.send_event) | ||
| 1452 | { | ||
| 1453 | unsigned int NumChildren; | ||
| 1454 | Window ChildReturn, Root, Parent; | ||
| 1455 | Window *Children; | ||
| 1456 | // Translate these coordinates back to relative to root | ||
| 1457 | X11_XQueryTree(data->videodata->display, xevent->xconfigure.window, &Root, &Parent, &Children, &NumChildren); | ||
| 1458 | X11_XTranslateCoordinates(xevent->xconfigure.display, | ||
| 1459 | Parent, DefaultRootWindow(xevent->xconfigure.display), | ||
| 1460 | xevent->xconfigure.x, xevent->xconfigure.y, | ||
| 1461 | &xevent->xconfigure.x, &xevent->xconfigure.y, | ||
| 1462 | &ChildReturn); | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | if (xevent->xconfigure.x != data->last_xconfigure.x || | ||
| 1466 | xevent->xconfigure.y != data->last_xconfigure.y) { | ||
| 1467 | if (!data->size_move_event_flags) { | ||
| 1468 | SDL_Window *w; | ||
| 1469 | int x = xevent->xconfigure.x; | ||
| 1470 | int y = xevent->xconfigure.y; | ||
| 1471 | |||
| 1472 | data->pending_operation &= ~X11_PENDING_OP_MOVE; | ||
| 1473 | SDL_GlobalToRelativeForWindow(data->window, x, y, &x, &y); | ||
| 1474 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MOVED, x, y); | ||
| 1475 | |||
| 1476 | for (w = data->window->first_child; w; w = w->next_sibling) { | ||
| 1477 | // Don't update hidden child popup windows, their relative position doesn't change | ||
| 1478 | if (SDL_WINDOW_IS_POPUP(w) && !(w->flags & SDL_WINDOW_HIDDEN)) { | ||
| 1479 | X11_UpdateWindowPosition(w, true); | ||
| 1480 | } | ||
| 1481 | } | ||
| 1482 | } | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 1486 | X11_HandleConfigure(data->window, &xevent->xconfigure); | ||
| 1487 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 1488 | |||
| 1489 | if (xevent->xconfigure.width != data->last_xconfigure.width || | ||
| 1490 | xevent->xconfigure.height != data->last_xconfigure.height) { | ||
| 1491 | if (!data->size_move_event_flags) { | ||
| 1492 | data->pending_operation &= ~X11_PENDING_OP_RESIZE; | ||
| 1493 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESIZED, | ||
| 1494 | xevent->xconfigure.width, | ||
| 1495 | xevent->xconfigure.height); | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | |||
| 1499 | data->last_xconfigure = xevent->xconfigure; | ||
| 1500 | } break; | ||
| 1501 | |||
| 1502 | // Have we been requested to quit (or another client message?) | ||
| 1503 | case ClientMessage: | ||
| 1504 | { | ||
| 1505 | static int xdnd_version = 0; | ||
| 1506 | |||
| 1507 | if (xevent->xclient.message_type == videodata->atoms.XdndEnter) { | ||
| 1508 | |||
| 1509 | bool use_list = xevent->xclient.data.l[1] & 1; | ||
| 1510 | data->xdnd_source = xevent->xclient.data.l[0]; | ||
| 1511 | xdnd_version = (xevent->xclient.data.l[1] >> 24); | ||
| 1512 | #ifdef DEBUG_XEVENTS | ||
| 1513 | SDL_Log("XID of source window : 0x%lx", data->xdnd_source); | ||
| 1514 | SDL_Log("Protocol version to use : %d", xdnd_version); | ||
| 1515 | SDL_Log("More then 3 data types : %d", (int)use_list); | ||
| 1516 | #endif | ||
| 1517 | |||
| 1518 | if (use_list) { | ||
| 1519 | // fetch conversion targets | ||
| 1520 | SDL_x11Prop p; | ||
| 1521 | X11_ReadProperty(&p, display, data->xdnd_source, videodata->atoms.XdndTypeList); | ||
| 1522 | // pick one | ||
| 1523 | data->xdnd_req = X11_PickTarget(display, (Atom *)p.data, p.count); | ||
| 1524 | X11_XFree(p.data); | ||
| 1525 | } else { | ||
| 1526 | // pick from list of three | ||
| 1527 | data->xdnd_req = X11_PickTargetFromAtoms(display, xevent->xclient.data.l[2], xevent->xclient.data.l[3], xevent->xclient.data.l[4]); | ||
| 1528 | } | ||
| 1529 | } else if (xevent->xclient.message_type == videodata->atoms.XdndLeave) { | ||
| 1530 | #ifdef DEBUG_XEVENTS | ||
| 1531 | SDL_Log("XID of source window : 0x%lx", xevent->xclient.data.l[0]); | ||
| 1532 | #endif | ||
| 1533 | SDL_SendDropComplete(data->window); | ||
| 1534 | } else if (xevent->xclient.message_type == videodata->atoms.XdndPosition) { | ||
| 1535 | |||
| 1536 | #ifdef DEBUG_XEVENTS | ||
| 1537 | Atom act = videodata->atoms.XdndActionCopy; | ||
| 1538 | if (xdnd_version >= 2) { | ||
| 1539 | act = xevent->xclient.data.l[4]; | ||
| 1540 | } | ||
| 1541 | SDL_Log("Action requested by user is : %s", X11_XGetAtomName(display, act)); | ||
| 1542 | #endif | ||
| 1543 | { | ||
| 1544 | // Drag and Drop position | ||
| 1545 | int root_x, root_y, window_x, window_y; | ||
| 1546 | Window ChildReturn; | ||
| 1547 | root_x = xevent->xclient.data.l[2] >> 16; | ||
| 1548 | root_y = xevent->xclient.data.l[2] & 0xffff; | ||
| 1549 | // Translate from root to current window position | ||
| 1550 | X11_XTranslateCoordinates(display, DefaultRootWindow(display), data->xwindow, | ||
| 1551 | root_x, root_y, &window_x, &window_y, &ChildReturn); | ||
| 1552 | |||
| 1553 | SDL_SendDropPosition(data->window, (float)window_x, (float)window_y); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | // reply with status | ||
| 1557 | SDL_memset(&m, 0, sizeof(XClientMessageEvent)); | ||
| 1558 | m.type = ClientMessage; | ||
| 1559 | m.display = xevent->xclient.display; | ||
| 1560 | m.window = xevent->xclient.data.l[0]; | ||
| 1561 | m.message_type = videodata->atoms.XdndStatus; | ||
| 1562 | m.format = 32; | ||
| 1563 | m.data.l[0] = data->xwindow; | ||
| 1564 | m.data.l[1] = (data->xdnd_req != None); | ||
| 1565 | m.data.l[2] = 0; // specify an empty rectangle | ||
| 1566 | m.data.l[3] = 0; | ||
| 1567 | m.data.l[4] = videodata->atoms.XdndActionCopy; // we only accept copying anyway | ||
| 1568 | |||
| 1569 | X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); | ||
| 1570 | X11_XFlush(display); | ||
| 1571 | } else if (xevent->xclient.message_type == videodata->atoms.XdndDrop) { | ||
| 1572 | if (data->xdnd_req == None) { | ||
| 1573 | // say again - not interested! | ||
| 1574 | SDL_memset(&m, 0, sizeof(XClientMessageEvent)); | ||
| 1575 | m.type = ClientMessage; | ||
| 1576 | m.display = xevent->xclient.display; | ||
| 1577 | m.window = xevent->xclient.data.l[0]; | ||
| 1578 | m.message_type = videodata->atoms.XdndFinished; | ||
| 1579 | m.format = 32; | ||
| 1580 | m.data.l[0] = data->xwindow; | ||
| 1581 | m.data.l[1] = 0; | ||
| 1582 | m.data.l[2] = None; // fail! | ||
| 1583 | X11_XSendEvent(display, xevent->xclient.data.l[0], False, NoEventMask, (XEvent *)&m); | ||
| 1584 | } else { | ||
| 1585 | // convert | ||
| 1586 | if (xdnd_version >= 1) { | ||
| 1587 | X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, xevent->xclient.data.l[2]); | ||
| 1588 | } else { | ||
| 1589 | X11_XConvertSelection(display, videodata->atoms.XdndSelection, data->xdnd_req, videodata->atoms.PRIMARY, data->xwindow, CurrentTime); | ||
| 1590 | } | ||
| 1591 | } | ||
| 1592 | } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && | ||
| 1593 | (xevent->xclient.format == 32) && | ||
| 1594 | (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_PING)) { | ||
| 1595 | Window root = DefaultRootWindow(display); | ||
| 1596 | |||
| 1597 | #ifdef DEBUG_XEVENTS | ||
| 1598 | SDL_Log("window 0x%lx: _NET_WM_PING", xevent->xany.window); | ||
| 1599 | #endif | ||
| 1600 | xevent->xclient.window = root; | ||
| 1601 | X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, xevent); | ||
| 1602 | break; | ||
| 1603 | } | ||
| 1604 | |||
| 1605 | else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && | ||
| 1606 | (xevent->xclient.format == 32) && | ||
| 1607 | (xevent->xclient.data.l[0] == videodata->atoms.WM_DELETE_WINDOW)) { | ||
| 1608 | |||
| 1609 | #ifdef DEBUG_XEVENTS | ||
| 1610 | SDL_Log("window 0x%lx: WM_DELETE_WINDOW", xevent->xany.window); | ||
| 1611 | #endif | ||
| 1612 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_CLOSE_REQUESTED, 0, 0); | ||
| 1613 | break; | ||
| 1614 | } else if ((xevent->xclient.message_type == videodata->atoms.WM_PROTOCOLS) && | ||
| 1615 | (xevent->xclient.format == 32) && | ||
| 1616 | (xevent->xclient.data.l[0] == videodata->atoms._NET_WM_SYNC_REQUEST)) { | ||
| 1617 | |||
| 1618 | #ifdef DEBUG_XEVENTS | ||
| 1619 | printf("window %p: _NET_WM_SYNC_REQUEST\n", data); | ||
| 1620 | #endif | ||
| 1621 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 1622 | X11_HandleSyncRequest(data->window, &xevent->xclient); | ||
| 1623 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 1624 | break; | ||
| 1625 | } | ||
| 1626 | } break; | ||
| 1627 | |||
| 1628 | // Do we need to refresh ourselves? | ||
| 1629 | case Expose: | ||
| 1630 | { | ||
| 1631 | #ifdef DEBUG_XEVENTS | ||
| 1632 | SDL_Log("window 0x%lx: Expose (count = %d)", xevent->xany.window, xevent->xexpose.count); | ||
| 1633 | #endif | ||
| 1634 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0); | ||
| 1635 | } break; | ||
| 1636 | |||
| 1637 | /* Use XInput2 instead of the xevents API if possible, for: | ||
| 1638 | - KeyPress | ||
| 1639 | - KeyRelease | ||
| 1640 | - MotionNotify | ||
| 1641 | - ButtonPress | ||
| 1642 | - ButtonRelease | ||
| 1643 | XInput2 has more precise information, e.g., to distinguish different input devices. */ | ||
| 1644 | case KeyPress: | ||
| 1645 | case KeyRelease: | ||
| 1646 | { | ||
| 1647 | if (data->xinput2_keyboard_enabled) { | ||
| 1648 | // This input is being handled by XInput2 | ||
| 1649 | break; | ||
| 1650 | } | ||
| 1651 | |||
| 1652 | X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent); | ||
| 1653 | } break; | ||
| 1654 | |||
| 1655 | case MotionNotify: | ||
| 1656 | { | ||
| 1657 | if (data->xinput2_mouse_enabled && !data->mouse_grabbed) { | ||
| 1658 | // This input is being handled by XInput2 | ||
| 1659 | break; | ||
| 1660 | } | ||
| 1661 | |||
| 1662 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 1663 | if (!mouse->relative_mode) { | ||
| 1664 | #ifdef DEBUG_MOTION | ||
| 1665 | SDL_Log("window 0x%lx: X11 motion: %d,%d", xevent->xany.window, xevent->xmotion.x, xevent->xmotion.y); | ||
| 1666 | #endif | ||
| 1667 | |||
| 1668 | X11_ProcessHitTest(_this, data, (float)xevent->xmotion.x, (float)xevent->xmotion.y, false); | ||
| 1669 | SDL_SendMouseMotion(0, data->window, SDL_GLOBAL_MOUSE_ID, false, (float)xevent->xmotion.x, (float)xevent->xmotion.y); | ||
| 1670 | } | ||
| 1671 | } break; | ||
| 1672 | |||
| 1673 | case ButtonPress: | ||
| 1674 | { | ||
| 1675 | if (data->xinput2_mouse_enabled) { | ||
| 1676 | // This input is being handled by XInput2 | ||
| 1677 | break; | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | X11_HandleButtonPress(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, | ||
| 1681 | xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time); | ||
| 1682 | } break; | ||
| 1683 | |||
| 1684 | case ButtonRelease: | ||
| 1685 | { | ||
| 1686 | if (data->xinput2_mouse_enabled) { | ||
| 1687 | // This input is being handled by XInput2 | ||
| 1688 | break; | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | X11_HandleButtonRelease(_this, data, SDL_GLOBAL_MOUSE_ID, xevent->xbutton.button, xevent->xbutton.time); | ||
| 1692 | } break; | ||
| 1693 | |||
| 1694 | case PropertyNotify: | ||
| 1695 | { | ||
| 1696 | #ifdef DEBUG_XEVENTS | ||
| 1697 | unsigned char *propdata; | ||
| 1698 | int status, real_format; | ||
| 1699 | Atom real_type; | ||
| 1700 | unsigned long items_read, items_left; | ||
| 1701 | |||
| 1702 | char *name = X11_XGetAtomName(display, xevent->xproperty.atom); | ||
| 1703 | if (name) { | ||
| 1704 | SDL_Log("window 0x%lx: PropertyNotify: %s %s time=%lu", xevent->xany.window, name, (xevent->xproperty.state == PropertyDelete) ? "deleted" : "changed", xevent->xproperty.time); | ||
| 1705 | X11_XFree(name); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | status = X11_XGetWindowProperty(display, data->xwindow, xevent->xproperty.atom, 0L, 8192L, False, AnyPropertyType, &real_type, &real_format, &items_read, &items_left, &propdata); | ||
| 1709 | if (status == Success && items_read > 0) { | ||
| 1710 | if (real_type == XA_INTEGER) { | ||
| 1711 | int *values = (int *)propdata; | ||
| 1712 | |||
| 1713 | SDL_Log("{"); | ||
| 1714 | for (i = 0; i < items_read; i++) { | ||
| 1715 | SDL_Log(" %d", values[i]); | ||
| 1716 | } | ||
| 1717 | SDL_Log(" }"); | ||
| 1718 | } else if (real_type == XA_CARDINAL) { | ||
| 1719 | if (real_format == 32) { | ||
| 1720 | Uint32 *values = (Uint32 *)propdata; | ||
| 1721 | |||
| 1722 | SDL_Log("{"); | ||
| 1723 | for (i = 0; i < items_read; i++) { | ||
| 1724 | SDL_Log(" %d", values[i]); | ||
| 1725 | } | ||
| 1726 | SDL_Log(" }"); | ||
| 1727 | } else if (real_format == 16) { | ||
| 1728 | Uint16 *values = (Uint16 *)propdata; | ||
| 1729 | |||
| 1730 | SDL_Log("{"); | ||
| 1731 | for (i = 0; i < items_read; i++) { | ||
| 1732 | SDL_Log(" %d", values[i]); | ||
| 1733 | } | ||
| 1734 | SDL_Log(" }"); | ||
| 1735 | } else if (real_format == 8) { | ||
| 1736 | Uint8 *values = (Uint8 *)propdata; | ||
| 1737 | |||
| 1738 | SDL_Log("{"); | ||
| 1739 | for (i = 0; i < items_read; i++) { | ||
| 1740 | SDL_Log(" %d", values[i]); | ||
| 1741 | } | ||
| 1742 | SDL_Log(" }"); | ||
| 1743 | } | ||
| 1744 | } else if (real_type == XA_STRING || | ||
| 1745 | real_type == videodata->atoms.UTF8_STRING) { | ||
| 1746 | SDL_Log("{ \"%s\" }", propdata); | ||
| 1747 | } else if (real_type == XA_ATOM) { | ||
| 1748 | Atom *atoms = (Atom *)propdata; | ||
| 1749 | |||
| 1750 | SDL_Log("{"); | ||
| 1751 | for (i = 0; i < items_read; i++) { | ||
| 1752 | char *atomname = X11_XGetAtomName(display, atoms[i]); | ||
| 1753 | if (atomname) { | ||
| 1754 | SDL_Log(" %s", atomname); | ||
| 1755 | X11_XFree(atomname); | ||
| 1756 | } | ||
| 1757 | } | ||
| 1758 | SDL_Log(" }"); | ||
| 1759 | } else { | ||
| 1760 | char *atomname = X11_XGetAtomName(display, real_type); | ||
| 1761 | SDL_Log("Unknown type: 0x%lx (%s)", real_type, atomname ? atomname : "UNKNOWN"); | ||
| 1762 | if (atomname) { | ||
| 1763 | X11_XFree(atomname); | ||
| 1764 | } | ||
| 1765 | } | ||
| 1766 | } | ||
| 1767 | if (status == Success) { | ||
| 1768 | X11_XFree(propdata); | ||
| 1769 | } | ||
| 1770 | #endif // DEBUG_XEVENTS | ||
| 1771 | |||
| 1772 | /* Take advantage of this moment to make sure user_time has a | ||
| 1773 | valid timestamp from the X server, so if we later try to | ||
| 1774 | raise/restore this window, _NET_ACTIVE_WINDOW can have a | ||
| 1775 | non-zero timestamp, even if there's never been a mouse or | ||
| 1776 | key press to this window so far. Note that we don't try to | ||
| 1777 | set _NET_WM_USER_TIME here, though. That's only for legit | ||
| 1778 | user interaction with the window. */ | ||
| 1779 | if (!data->user_time) { | ||
| 1780 | data->user_time = xevent->xproperty.time; | ||
| 1781 | } | ||
| 1782 | |||
| 1783 | if (xevent->xproperty.atom == data->videodata->atoms._NET_WM_STATE) { | ||
| 1784 | /* Get the new state from the window manager. | ||
| 1785 | Compositing window managers can alter visibility of windows | ||
| 1786 | without ever mapping / unmapping them, so we handle that here, | ||
| 1787 | because they use the NETWM protocol to notify us of changes. | ||
| 1788 | */ | ||
| 1789 | const SDL_WindowFlags flags = X11_GetNetWMState(_this, data->window, xevent->xproperty.window); | ||
| 1790 | const SDL_WindowFlags changed = flags ^ data->window->flags; | ||
| 1791 | |||
| 1792 | if ((changed & (SDL_WINDOW_HIDDEN | SDL_WINDOW_FULLSCREEN)) != 0) { | ||
| 1793 | if (flags & SDL_WINDOW_HIDDEN) { | ||
| 1794 | X11_DispatchUnmapNotify(data); | ||
| 1795 | } else { | ||
| 1796 | X11_DispatchMapNotify(data); | ||
| 1797 | } | ||
| 1798 | } | ||
| 1799 | |||
| 1800 | if (!SDL_WINDOW_IS_POPUP(data->window)) { | ||
| 1801 | if (changed & SDL_WINDOW_FULLSCREEN) { | ||
| 1802 | data->pending_operation &= ~X11_PENDING_OP_FULLSCREEN; | ||
| 1803 | |||
| 1804 | if (flags & SDL_WINDOW_FULLSCREEN) { | ||
| 1805 | if (!(flags & SDL_WINDOW_MINIMIZED)) { | ||
| 1806 | const bool commit = SDL_memcmp(&data->window->current_fullscreen_mode, &data->requested_fullscreen_mode, sizeof(SDL_DisplayMode)) != 0; | ||
| 1807 | |||
| 1808 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_ENTER_FULLSCREEN, 0, 0); | ||
| 1809 | if (commit) { | ||
| 1810 | /* This was initiated by the compositor, or the mode was changed between the request and the window | ||
| 1811 | * becoming fullscreen. Switch to the application requested mode if necessary. | ||
| 1812 | */ | ||
| 1813 | SDL_copyp(&data->window->current_fullscreen_mode, &data->window->requested_fullscreen_mode); | ||
| 1814 | SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_UPDATE, true); | ||
| 1815 | } else { | ||
| 1816 | SDL_UpdateFullscreenMode(data->window, SDL_FULLSCREEN_OP_ENTER, false); | ||
| 1817 | } | ||
| 1818 | } | ||
| 1819 | } else { | ||
| 1820 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, 0, 0); | ||
| 1821 | SDL_UpdateFullscreenMode(data->window, false, false); | ||
| 1822 | |||
| 1823 | SDL_zero(data->requested_fullscreen_mode); | ||
| 1824 | |||
| 1825 | // Need to restore or update any limits changed while the window was fullscreen. | ||
| 1826 | X11_SetWindowMinMax(data->window, !!(flags & SDL_WINDOW_MAXIMIZED)); | ||
| 1827 | |||
| 1828 | // Toggle the borders if they were forced on while creating a borderless fullscreen window. | ||
| 1829 | if (data->fullscreen_borders_forced_on) { | ||
| 1830 | data->toggle_borders = true; | ||
| 1831 | data->fullscreen_borders_forced_on = false; | ||
| 1832 | } | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | if ((flags & SDL_WINDOW_FULLSCREEN) && | ||
| 1836 | (data->border_top || data->border_left || data->border_bottom || data->border_right)) { | ||
| 1837 | /* If the window is entering fullscreen and the borders are | ||
| 1838 | * non-zero sized, turn off size events until the borders are | ||
| 1839 | * shut off to avoid bogus window sizes and positions, and | ||
| 1840 | * note that the old borders were non-zero for restoration. | ||
| 1841 | */ | ||
| 1842 | data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; | ||
| 1843 | data->previous_borders_nonzero = true; | ||
| 1844 | } else if (!(flags & SDL_WINDOW_FULLSCREEN) && | ||
| 1845 | data->previous_borders_nonzero && | ||
| 1846 | (!data->border_top && !data->border_left && !data->border_bottom && !data->border_right)) { | ||
| 1847 | /* If the window is leaving fullscreen and the current borders | ||
| 1848 | * are zero sized, but weren't when entering fullscreen, turn | ||
| 1849 | * off size events until the borders come back to avoid bogus | ||
| 1850 | * window sizes and positions. | ||
| 1851 | */ | ||
| 1852 | data->size_move_event_flags |= X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; | ||
| 1853 | data->previous_borders_nonzero = false; | ||
| 1854 | } else { | ||
| 1855 | data->size_move_event_flags = 0; | ||
| 1856 | data->previous_borders_nonzero = false; | ||
| 1857 | |||
| 1858 | if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { | ||
| 1859 | data->toggle_borders = false; | ||
| 1860 | X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); | ||
| 1861 | } | ||
| 1862 | } | ||
| 1863 | } | ||
| 1864 | if ((changed & SDL_WINDOW_MAXIMIZED) && ((flags & SDL_WINDOW_MAXIMIZED) && !(flags & SDL_WINDOW_MINIMIZED))) { | ||
| 1865 | data->pending_operation &= ~X11_PENDING_OP_MAXIMIZE; | ||
| 1866 | if ((changed & SDL_WINDOW_MINIMIZED)) { | ||
| 1867 | data->pending_operation &= ~X11_PENDING_OP_RESTORE; | ||
| 1868 | // If coming out of minimized, send a restore event before sending maximized. | ||
| 1869 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); | ||
| 1870 | } | ||
| 1871 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MAXIMIZED, 0, 0); | ||
| 1872 | } | ||
| 1873 | if ((changed & SDL_WINDOW_MINIMIZED) && (flags & SDL_WINDOW_MINIMIZED)) { | ||
| 1874 | data->pending_operation &= ~X11_PENDING_OP_MINIMIZE; | ||
| 1875 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0); | ||
| 1876 | } | ||
| 1877 | if (!(flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_MINIMIZED))) { | ||
| 1878 | data->pending_operation &= ~X11_PENDING_OP_RESTORE; | ||
| 1879 | SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0); | ||
| 1880 | |||
| 1881 | // Apply any pending state if restored. | ||
| 1882 | if (!(flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1883 | if (data->pending_position) { | ||
| 1884 | data->pending_position = false; | ||
| 1885 | data->pending_operation |= X11_PENDING_OP_MOVE; | ||
| 1886 | data->expected.x = data->window->pending.x - data->border_left; | ||
| 1887 | data->expected.y = data->window->pending.y - data->border_top; | ||
| 1888 | X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y); | ||
| 1889 | } | ||
| 1890 | if (data->pending_size) { | ||
| 1891 | data->pending_size = false; | ||
| 1892 | data->pending_operation |= X11_PENDING_OP_RESIZE; | ||
| 1893 | data->expected.w = data->window->pending.w; | ||
| 1894 | data->expected.h = data->window->pending.h; | ||
| 1895 | X11_XResizeWindow(display, data->xwindow, data->window->pending.w, data->window->pending.h); | ||
| 1896 | } | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | if ((flags & SDL_WINDOW_INPUT_FOCUS)) { | ||
| 1900 | if (data->pending_move) { | ||
| 1901 | DispatchWindowMove(_this, data, &data->pending_move_point); | ||
| 1902 | data->pending_move = false; | ||
| 1903 | } | ||
| 1904 | } | ||
| 1905 | } | ||
| 1906 | if (changed & SDL_WINDOW_OCCLUDED) { | ||
| 1907 | SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0); | ||
| 1908 | } | ||
| 1909 | } else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) { | ||
| 1910 | /* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify | ||
| 1911 | events when the keyboard layout changes (for example, | ||
| 1912 | changing from English to French on the menubar's keyboard | ||
| 1913 | icon). Since it changes the XKLAVIER_STATE property, we | ||
| 1914 | notice and reinit our keymap here. This might not be the | ||
| 1915 | right approach, but it seems to work. */ | ||
| 1916 | X11_UpdateKeymap(_this, true); | ||
| 1917 | } else if (xevent->xproperty.atom == videodata->atoms._NET_FRAME_EXTENTS) { | ||
| 1918 | /* Events are disabled when leaving fullscreen until the borders appear to avoid | ||
| 1919 | * incorrect size/position events. | ||
| 1920 | */ | ||
| 1921 | if (data->size_move_event_flags) { | ||
| 1922 | data->size_move_event_flags &= ~X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS; | ||
| 1923 | X11_GetBorderValues(data); | ||
| 1924 | |||
| 1925 | } | ||
| 1926 | if (!(data->window->flags & SDL_WINDOW_FULLSCREEN) && data->toggle_borders) { | ||
| 1927 | data->toggle_borders = false; | ||
| 1928 | X11_SetWindowBordered(_this, data->window, !(data->window->flags & SDL_WINDOW_BORDERLESS)); | ||
| 1929 | } | ||
| 1930 | } | ||
| 1931 | } break; | ||
| 1932 | |||
| 1933 | case SelectionNotify: | ||
| 1934 | { | ||
| 1935 | Atom target = xevent->xselection.target; | ||
| 1936 | #ifdef DEBUG_XEVENTS | ||
| 1937 | SDL_Log("window 0x%lx: SelectionNotify (requestor = 0x%lx, target = 0x%lx)", xevent->xany.window, | ||
| 1938 | xevent->xselection.requestor, xevent->xselection.target); | ||
| 1939 | #endif | ||
| 1940 | if (target == data->xdnd_req) { | ||
| 1941 | // read data | ||
| 1942 | SDL_x11Prop p; | ||
| 1943 | X11_ReadProperty(&p, display, data->xwindow, videodata->atoms.PRIMARY); | ||
| 1944 | |||
| 1945 | if (p.format == 8) { | ||
| 1946 | char *saveptr = NULL; | ||
| 1947 | char *name = X11_XGetAtomName(display, target); | ||
| 1948 | if (name) { | ||
| 1949 | char *token = SDL_strtok_r((char *)p.data, "\r\n", &saveptr); | ||
| 1950 | while (token) { | ||
| 1951 | if ((SDL_strcmp("text/plain;charset=utf-8", name) == 0) || | ||
| 1952 | (SDL_strcmp("UTF8_STRING", name) == 0) || | ||
| 1953 | (SDL_strcmp("text/plain", name) == 0) || | ||
| 1954 | (SDL_strcmp("TEXT", name) == 0)) { | ||
| 1955 | SDL_SendDropText(data->window, token); | ||
| 1956 | } else if (SDL_strcmp("text/uri-list", name) == 0) { | ||
| 1957 | if (SDL_URIToLocal(token, token) >= 0) { | ||
| 1958 | SDL_SendDropFile(data->window, NULL, token); | ||
| 1959 | } | ||
| 1960 | } | ||
| 1961 | token = SDL_strtok_r(NULL, "\r\n", &saveptr); | ||
| 1962 | } | ||
| 1963 | X11_XFree(name); | ||
| 1964 | } | ||
| 1965 | SDL_SendDropComplete(data->window); | ||
| 1966 | } | ||
| 1967 | X11_XFree(p.data); | ||
| 1968 | |||
| 1969 | // send reply | ||
| 1970 | SDL_memset(&m, 0, sizeof(XClientMessageEvent)); | ||
| 1971 | m.type = ClientMessage; | ||
| 1972 | m.display = display; | ||
| 1973 | m.window = data->xdnd_source; | ||
| 1974 | m.message_type = videodata->atoms.XdndFinished; | ||
| 1975 | m.format = 32; | ||
| 1976 | m.data.l[0] = data->xwindow; | ||
| 1977 | m.data.l[1] = 1; | ||
| 1978 | m.data.l[2] = videodata->atoms.XdndActionCopy; | ||
| 1979 | X11_XSendEvent(display, data->xdnd_source, False, NoEventMask, (XEvent *)&m); | ||
| 1980 | |||
| 1981 | X11_XSync(display, False); | ||
| 1982 | } | ||
| 1983 | } break; | ||
| 1984 | |||
| 1985 | default: | ||
| 1986 | { | ||
| 1987 | #ifdef DEBUG_XEVENTS | ||
| 1988 | SDL_Log("window 0x%lx: Unhandled event %d", xevent->xany.window, xevent->type); | ||
| 1989 | #endif | ||
| 1990 | } break; | ||
| 1991 | } | ||
| 1992 | } | ||
| 1993 | |||
| 1994 | static void X11_HandleFocusChanges(SDL_VideoDevice *_this) | ||
| 1995 | { | ||
| 1996 | SDL_VideoData *videodata = _this->internal; | ||
| 1997 | int i; | ||
| 1998 | |||
| 1999 | if (videodata && videodata->windowlist) { | ||
| 2000 | for (i = 0; i < videodata->numwindows; ++i) { | ||
| 2001 | SDL_WindowData *data = videodata->windowlist[i]; | ||
| 2002 | if (data && data->pending_focus != PENDING_FOCUS_NONE) { | ||
| 2003 | Uint64 now = SDL_GetTicks(); | ||
| 2004 | if (now >= data->pending_focus_time) { | ||
| 2005 | if (data->pending_focus == PENDING_FOCUS_IN) { | ||
| 2006 | X11_DispatchFocusIn(_this, data); | ||
| 2007 | } else { | ||
| 2008 | X11_DispatchFocusOut(_this, data); | ||
| 2009 | } | ||
| 2010 | data->pending_focus = PENDING_FOCUS_NONE; | ||
| 2011 | } | ||
| 2012 | } | ||
| 2013 | } | ||
| 2014 | } | ||
| 2015 | } | ||
| 2016 | |||
| 2017 | static Bool isAnyEvent(Display *display, XEvent *ev, XPointer arg) | ||
| 2018 | { | ||
| 2019 | return True; | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | static bool X11_PollEvent(Display *display, XEvent *event) | ||
| 2023 | { | ||
| 2024 | if (!X11_XCheckIfEvent(display, event, isAnyEvent, NULL)) { | ||
| 2025 | return false; | ||
| 2026 | } | ||
| 2027 | |||
| 2028 | return true; | ||
| 2029 | } | ||
| 2030 | |||
| 2031 | void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 2032 | { | ||
| 2033 | SDL_VideoData *data = _this->internal; | ||
| 2034 | Display *req_display = data->request_display; | ||
| 2035 | Window xwindow = window->internal->xwindow; | ||
| 2036 | XClientMessageEvent event; | ||
| 2037 | |||
| 2038 | SDL_memset(&event, 0, sizeof(XClientMessageEvent)); | ||
| 2039 | event.type = ClientMessage; | ||
| 2040 | event.display = req_display; | ||
| 2041 | event.send_event = True; | ||
| 2042 | event.message_type = data->atoms._SDL_WAKEUP; | ||
| 2043 | event.format = 8; | ||
| 2044 | |||
| 2045 | X11_XSendEvent(req_display, xwindow, False, NoEventMask, (XEvent *)&event); | ||
| 2046 | /* XSendEvent returns a status and it could be BadValue or BadWindow. If an | ||
| 2047 | error happens it is an SDL's internal error and there is nothing we can do here. */ | ||
| 2048 | X11_XFlush(req_display); | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) | ||
| 2052 | { | ||
| 2053 | SDL_VideoData *videodata = _this->internal; | ||
| 2054 | Display *display; | ||
| 2055 | XEvent xevent; | ||
| 2056 | display = videodata->display; | ||
| 2057 | |||
| 2058 | SDL_zero(xevent); | ||
| 2059 | |||
| 2060 | // Flush and poll to grab any events already read and queued | ||
| 2061 | X11_XFlush(display); | ||
| 2062 | if (X11_PollEvent(display, &xevent)) { | ||
| 2063 | // Fall through | ||
| 2064 | } else if (timeoutNS == 0) { | ||
| 2065 | return 0; | ||
| 2066 | } else { | ||
| 2067 | // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait | ||
| 2068 | int err = SDL_IOReady(ConnectionNumber(display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS); | ||
| 2069 | if (err > 0) { | ||
| 2070 | if (!X11_PollEvent(display, &xevent)) { | ||
| 2071 | /* Someone may have beat us to reading the fd. Return 1 here to | ||
| 2072 | * trigger the normal spurious wakeup logic in the event core. */ | ||
| 2073 | return 1; | ||
| 2074 | } | ||
| 2075 | } else if (err == 0) { | ||
| 2076 | // Timeout | ||
| 2077 | return 0; | ||
| 2078 | } else { | ||
| 2079 | // Error returned from poll()/select() | ||
| 2080 | |||
| 2081 | if (errno == EINTR) { | ||
| 2082 | /* If the wait was interrupted by a signal, we may have generated a | ||
| 2083 | * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */ | ||
| 2084 | return 1; | ||
| 2085 | } else { | ||
| 2086 | return err; | ||
| 2087 | } | ||
| 2088 | } | ||
| 2089 | } | ||
| 2090 | |||
| 2091 | X11_DispatchEvent(_this, &xevent); | ||
| 2092 | |||
| 2093 | #ifdef SDL_USE_LIBDBUS | ||
| 2094 | SDL_DBus_PumpEvents(); | ||
| 2095 | #endif | ||
| 2096 | return 1; | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | void X11_PumpEvents(SDL_VideoDevice *_this) | ||
| 2100 | { | ||
| 2101 | SDL_VideoData *data = _this->internal; | ||
| 2102 | XEvent xevent; | ||
| 2103 | int i; | ||
| 2104 | |||
| 2105 | /* Check if a display had the mode changed and is waiting for a window to asynchronously become | ||
| 2106 | * fullscreen. If there is no fullscreen window past the elapsed timeout, revert the mode switch. | ||
| 2107 | */ | ||
| 2108 | for (i = 0; i < _this->num_displays; ++i) { | ||
| 2109 | if (_this->displays[i]->internal->mode_switch_deadline_ns) { | ||
| 2110 | if (_this->displays[i]->fullscreen_window) { | ||
| 2111 | _this->displays[i]->internal->mode_switch_deadline_ns = 0; | ||
| 2112 | } else if (SDL_GetTicksNS() >= _this->displays[i]->internal->mode_switch_deadline_ns) { | ||
| 2113 | SDL_LogError(SDL_LOG_CATEGORY_VIDEO, | ||
| 2114 | "Time out elapsed after mode switch on display %" SDL_PRIu32 " with no window becoming fullscreen; reverting", _this->displays[i]->id); | ||
| 2115 | SDL_SetDisplayModeForDisplay(_this->displays[i], NULL); | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | if (data->last_mode_change_deadline) { | ||
| 2121 | if (SDL_GetTicks() >= data->last_mode_change_deadline) { | ||
| 2122 | data->last_mode_change_deadline = 0; // assume we're done. | ||
| 2123 | } | ||
| 2124 | } | ||
| 2125 | |||
| 2126 | // Update activity every 30 seconds to prevent screensaver | ||
| 2127 | if (_this->suspend_screensaver) { | ||
| 2128 | Uint64 now = SDL_GetTicks(); | ||
| 2129 | if (!data->screensaver_activity || now >= (data->screensaver_activity + 30000)) { | ||
| 2130 | X11_XResetScreenSaver(data->display); | ||
| 2131 | |||
| 2132 | #ifdef SDL_USE_LIBDBUS | ||
| 2133 | SDL_DBus_ScreensaverTickle(); | ||
| 2134 | #endif | ||
| 2135 | |||
| 2136 | data->screensaver_activity = now; | ||
| 2137 | } | ||
| 2138 | } | ||
| 2139 | |||
| 2140 | SDL_zero(xevent); | ||
| 2141 | |||
| 2142 | // Keep processing pending events | ||
| 2143 | while (X11_PollEvent(data->display, &xevent)) { | ||
| 2144 | X11_DispatchEvent(_this, &xevent); | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | #ifdef SDL_USE_LIBDBUS | ||
| 2148 | SDL_DBus_PumpEvents(); | ||
| 2149 | #endif | ||
| 2150 | |||
| 2151 | // FIXME: Only need to do this when there are pending focus changes | ||
| 2152 | X11_HandleFocusChanges(_this); | ||
| 2153 | |||
| 2154 | // FIXME: Only need to do this when there are flashing windows | ||
| 2155 | for (i = 0; i < data->numwindows; ++i) { | ||
| 2156 | if (data->windowlist[i] != NULL && | ||
| 2157 | data->windowlist[i]->flash_cancel_time && | ||
| 2158 | SDL_GetTicks() >= data->windowlist[i]->flash_cancel_time) { | ||
| 2159 | X11_FlashWindow(_this, data->windowlist[i]->window, SDL_FLASH_CANCEL); | ||
| 2160 | } | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | if (data->xinput_hierarchy_changed) { | ||
| 2164 | X11_Xinput2UpdateDevices(_this, false); | ||
| 2165 | data->xinput_hierarchy_changed = false; | ||
| 2166 | } | ||
| 2167 | } | ||
| 2168 | |||
| 2169 | bool X11_SuspendScreenSaver(SDL_VideoDevice *_this) | ||
| 2170 | { | ||
| 2171 | #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER | ||
| 2172 | SDL_VideoData *data = _this->internal; | ||
| 2173 | int dummy; | ||
| 2174 | int major_version, minor_version; | ||
| 2175 | #endif // SDL_VIDEO_DRIVER_X11_XSCRNSAVER | ||
| 2176 | |||
| 2177 | #ifdef SDL_USE_LIBDBUS | ||
| 2178 | if (SDL_DBus_ScreensaverInhibit(_this->suspend_screensaver)) { | ||
| 2179 | return true; | ||
| 2180 | } | ||
| 2181 | |||
| 2182 | if (_this->suspend_screensaver) { | ||
| 2183 | SDL_DBus_ScreensaverTickle(); | ||
| 2184 | } | ||
| 2185 | #endif | ||
| 2186 | |||
| 2187 | #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER | ||
| 2188 | if (SDL_X11_HAVE_XSS) { | ||
| 2189 | // X11_XScreenSaverSuspend was introduced in MIT-SCREEN-SAVER 1.1 | ||
| 2190 | if (!X11_XScreenSaverQueryExtension(data->display, &dummy, &dummy) || | ||
| 2191 | !X11_XScreenSaverQueryVersion(data->display, | ||
| 2192 | &major_version, &minor_version) || | ||
| 2193 | major_version < 1 || (major_version == 1 && minor_version < 1)) { | ||
| 2194 | return SDL_Unsupported(); | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | X11_XScreenSaverSuspend(data->display, _this->suspend_screensaver); | ||
| 2198 | X11_XResetScreenSaver(data->display); | ||
| 2199 | return true; | ||
| 2200 | } | ||
| 2201 | #endif | ||
| 2202 | return SDL_Unsupported(); | ||
| 2203 | } | ||
| 2204 | |||
| 2205 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h new file mode 100644 index 0000000..bb76f83 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11events.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11events_h_ | ||
| 24 | #define SDL_x11events_h_ | ||
| 25 | |||
| 26 | extern void X11_PumpEvents(SDL_VideoDevice *_this); | ||
| 27 | extern int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS); | ||
| 28 | extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 29 | extern bool X11_SuspendScreenSaver(SDL_VideoDevice *_this); | ||
| 30 | extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this); | ||
| 31 | extern void X11_GetBorderValues(SDL_WindowData *data); | ||
| 32 | extern Uint64 X11_GetEventTimestamp(unsigned long time); | ||
| 33 | extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent); | ||
| 34 | extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time); | ||
| 35 | extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time); | ||
| 36 | extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window); | ||
| 37 | extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result); | ||
| 38 | extern bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y); | ||
| 39 | |||
| 40 | #endif // SDL_x11events_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c new file mode 100644 index 0000000..12642cc --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.c | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | #include "SDL_x11framebuffer.h" | ||
| 27 | #include "SDL_x11xsync.h" | ||
| 28 | |||
| 29 | #ifndef NO_SHARED_MEMORY | ||
| 30 | |||
| 31 | // Shared memory error handler routine | ||
| 32 | static int shm_error; | ||
| 33 | static int (*X_handler)(Display *, XErrorEvent *) = NULL; | ||
| 34 | static int shm_errhandler(Display *d, XErrorEvent *e) | ||
| 35 | { | ||
| 36 | if (e->error_code == BadAccess) { | ||
| 37 | shm_error = True; | ||
| 38 | return 0; | ||
| 39 | } | ||
| 40 | return X_handler(d, e); | ||
| 41 | } | ||
| 42 | |||
| 43 | static bool have_mitshm(Display *dpy) | ||
| 44 | { | ||
| 45 | // Only use shared memory on local X servers | ||
| 46 | return X11_XShmQueryExtension(dpy) ? SDL_X11_HAVE_SHM : false; | ||
| 47 | } | ||
| 48 | |||
| 49 | #endif // !NO_SHARED_MEMORY | ||
| 50 | |||
| 51 | bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format, | ||
| 52 | void **pixels, int *pitch) | ||
| 53 | { | ||
| 54 | SDL_WindowData *data = window->internal; | ||
| 55 | Display *display = data->videodata->display; | ||
| 56 | XGCValues gcv; | ||
| 57 | XVisualInfo vinfo; | ||
| 58 | int w, h; | ||
| 59 | |||
| 60 | SDL_GetWindowSizeInPixels(window, &w, &h); | ||
| 61 | |||
| 62 | // Free the old framebuffer surface | ||
| 63 | X11_DestroyWindowFramebuffer(_this, window); | ||
| 64 | |||
| 65 | // Create the graphics context for drawing | ||
| 66 | gcv.graphics_exposures = False; | ||
| 67 | data->gc = X11_XCreateGC(display, data->xwindow, GCGraphicsExposures, &gcv); | ||
| 68 | if (!data->gc) { | ||
| 69 | return SDL_SetError("Couldn't create graphics context"); | ||
| 70 | } | ||
| 71 | |||
| 72 | // Find out the pixel format and depth | ||
| 73 | if (!X11_GetVisualInfoFromVisual(display, data->visual, &vinfo)) { | ||
| 74 | return SDL_SetError("Couldn't get window visual information"); | ||
| 75 | } | ||
| 76 | |||
| 77 | *format = X11_GetPixelFormatFromVisualInfo(display, &vinfo); | ||
| 78 | if (*format == SDL_PIXELFORMAT_UNKNOWN) { | ||
| 79 | return SDL_SetError("Unknown window pixel format"); | ||
| 80 | } | ||
| 81 | |||
| 82 | // Calculate pitch | ||
| 83 | *pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3); | ||
| 84 | |||
| 85 | // Create the actual image | ||
| 86 | #ifndef NO_SHARED_MEMORY | ||
| 87 | if (have_mitshm(display)) { | ||
| 88 | XShmSegmentInfo *shminfo = &data->shminfo; | ||
| 89 | |||
| 90 | shminfo->shmid = shmget(IPC_PRIVATE, (size_t)h * (*pitch), IPC_CREAT | 0777); | ||
| 91 | if (shminfo->shmid >= 0) { | ||
| 92 | shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0); | ||
| 93 | shminfo->readOnly = False; | ||
| 94 | if (shminfo->shmaddr != (char *)-1) { | ||
| 95 | shm_error = False; | ||
| 96 | X_handler = X11_XSetErrorHandler(shm_errhandler); | ||
| 97 | X11_XShmAttach(display, shminfo); | ||
| 98 | X11_XSync(display, False); | ||
| 99 | X11_XSetErrorHandler(X_handler); | ||
| 100 | if (shm_error) { | ||
| 101 | shmdt(shminfo->shmaddr); | ||
| 102 | } | ||
| 103 | } else { | ||
| 104 | shm_error = True; | ||
| 105 | } | ||
| 106 | shmctl(shminfo->shmid, IPC_RMID, NULL); | ||
| 107 | } else { | ||
| 108 | shm_error = True; | ||
| 109 | } | ||
| 110 | if (!shm_error) { | ||
| 111 | data->ximage = X11_XShmCreateImage(display, data->visual, | ||
| 112 | vinfo.depth, ZPixmap, | ||
| 113 | shminfo->shmaddr, shminfo, | ||
| 114 | w, h); | ||
| 115 | if (!data->ximage) { | ||
| 116 | X11_XShmDetach(display, shminfo); | ||
| 117 | X11_XSync(display, False); | ||
| 118 | shmdt(shminfo->shmaddr); | ||
| 119 | } else { | ||
| 120 | // Done! | ||
| 121 | data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst; | ||
| 122 | data->use_mitshm = true; | ||
| 123 | *pixels = shminfo->shmaddr; | ||
| 124 | return true; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | #endif // not NO_SHARED_MEMORY | ||
| 129 | |||
| 130 | *pixels = SDL_malloc((size_t)h * (*pitch)); | ||
| 131 | if (!*pixels) { | ||
| 132 | return false; | ||
| 133 | } | ||
| 134 | |||
| 135 | data->ximage = X11_XCreateImage(display, data->visual, | ||
| 136 | vinfo.depth, ZPixmap, 0, (char *)(*pixels), | ||
| 137 | w, h, 32, 0); | ||
| 138 | if (!data->ximage) { | ||
| 139 | SDL_free(*pixels); | ||
| 140 | return SDL_SetError("Couldn't create XImage"); | ||
| 141 | } | ||
| 142 | data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst; | ||
| 143 | return true; | ||
| 144 | } | ||
| 145 | |||
| 146 | bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, | ||
| 147 | int numrects) | ||
| 148 | { | ||
| 149 | SDL_WindowData *data = window->internal; | ||
| 150 | Display *display = data->videodata->display; | ||
| 151 | int i; | ||
| 152 | int x, y, w, h; | ||
| 153 | int window_w, window_h; | ||
| 154 | |||
| 155 | SDL_GetWindowSizeInPixels(window, &window_w, &window_h); | ||
| 156 | |||
| 157 | #ifndef NO_SHARED_MEMORY | ||
| 158 | if (data->use_mitshm) { | ||
| 159 | for (i = 0; i < numrects; ++i) { | ||
| 160 | x = rects[i].x; | ||
| 161 | y = rects[i].y; | ||
| 162 | w = rects[i].w; | ||
| 163 | h = rects[i].h; | ||
| 164 | |||
| 165 | if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) { | ||
| 166 | // Clipped? | ||
| 167 | continue; | ||
| 168 | } | ||
| 169 | if (x < 0) { | ||
| 170 | x += w; | ||
| 171 | w += rects[i].x; | ||
| 172 | } | ||
| 173 | if (y < 0) { | ||
| 174 | y += h; | ||
| 175 | h += rects[i].y; | ||
| 176 | } | ||
| 177 | if (x + w > window_w) { | ||
| 178 | w = window_w - x; | ||
| 179 | } | ||
| 180 | if (y + h > window_h) { | ||
| 181 | h = window_h - y; | ||
| 182 | } | ||
| 183 | |||
| 184 | X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage, | ||
| 185 | x, y, x, y, w, h, False); | ||
| 186 | } | ||
| 187 | } else | ||
| 188 | #endif // !NO_SHARED_MEMORY | ||
| 189 | { | ||
| 190 | for (i = 0; i < numrects; ++i) { | ||
| 191 | x = rects[i].x; | ||
| 192 | y = rects[i].y; | ||
| 193 | w = rects[i].w; | ||
| 194 | h = rects[i].h; | ||
| 195 | |||
| 196 | if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) { | ||
| 197 | // Clipped? | ||
| 198 | continue; | ||
| 199 | } | ||
| 200 | if (x < 0) { | ||
| 201 | x += w; | ||
| 202 | w += rects[i].x; | ||
| 203 | } | ||
| 204 | if (y < 0) { | ||
| 205 | y += h; | ||
| 206 | h += rects[i].y; | ||
| 207 | } | ||
| 208 | if (x + w > window_w) { | ||
| 209 | w = window_w - x; | ||
| 210 | } | ||
| 211 | if (y + h > window_h) { | ||
| 212 | h = window_h - y; | ||
| 213 | } | ||
| 214 | |||
| 215 | X11_XPutImage(display, data->xwindow, data->gc, data->ximage, | ||
| 216 | x, y, x, y, w, h); | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 221 | X11_HandlePresent(data->window); | ||
| 222 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 223 | |||
| 224 | X11_XSync(display, False); | ||
| 225 | |||
| 226 | return true; | ||
| 227 | } | ||
| 228 | |||
| 229 | void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 230 | { | ||
| 231 | SDL_WindowData *data = window->internal; | ||
| 232 | Display *display; | ||
| 233 | |||
| 234 | if (!data) { | ||
| 235 | // The window wasn't fully initialized | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | |||
| 239 | display = data->videodata->display; | ||
| 240 | |||
| 241 | if (data->ximage) { | ||
| 242 | XDestroyImage(data->ximage); | ||
| 243 | |||
| 244 | #ifndef NO_SHARED_MEMORY | ||
| 245 | if (data->use_mitshm) { | ||
| 246 | X11_XShmDetach(display, &data->shminfo); | ||
| 247 | X11_XSync(display, False); | ||
| 248 | shmdt(data->shminfo.shmaddr); | ||
| 249 | data->use_mitshm = false; | ||
| 250 | } | ||
| 251 | #endif // !NO_SHARED_MEMORY | ||
| 252 | |||
| 253 | data->ximage = NULL; | ||
| 254 | } | ||
| 255 | if (data->gc) { | ||
| 256 | X11_XFreeGC(display, data->gc); | ||
| 257 | data->gc = NULL; | ||
| 258 | } | ||
| 259 | } | ||
| 260 | |||
| 261 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h new file mode 100644 index 0000000..08feda4 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11framebuffer.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #ifndef SDL_x11framebuffer_h_ | ||
| 23 | #define SDL_x11framebuffer_h_ | ||
| 24 | |||
| 25 | #include "SDL_internal.h" | ||
| 26 | |||
| 27 | extern bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, | ||
| 28 | SDL_PixelFormat *format, | ||
| 29 | void **pixels, int *pitch); | ||
| 30 | extern bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, | ||
| 31 | const SDL_Rect *rects, int numrects); | ||
| 32 | extern void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 33 | |||
| 34 | #endif // SDL_x11framebuffer_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c new file mode 100644 index 0000000..c48e829 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.c | |||
| @@ -0,0 +1,789 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | |||
| 27 | #include "../../events/SDL_keyboard_c.h" | ||
| 28 | #include "../../events/SDL_scancode_tables_c.h" | ||
| 29 | |||
| 30 | #include <X11/keysym.h> | ||
| 31 | #include <X11/XKBlib.h> | ||
| 32 | |||
| 33 | #include "../../events/imKStoUCS.h" | ||
| 34 | #include "../../events/SDL_keysym_to_scancode_c.h" | ||
| 35 | #include "../../events/SDL_keysym_to_keycode_c.h" | ||
| 36 | |||
| 37 | #ifdef X_HAVE_UTF8_STRING | ||
| 38 | #include <locale.h> | ||
| 39 | #endif | ||
| 40 | |||
| 41 | static SDL_ScancodeTable scancode_set[] = { | ||
| 42 | SDL_SCANCODE_TABLE_DARWIN, | ||
| 43 | SDL_SCANCODE_TABLE_XFREE86_1, | ||
| 44 | SDL_SCANCODE_TABLE_XFREE86_2, | ||
| 45 | SDL_SCANCODE_TABLE_XVNC, | ||
| 46 | }; | ||
| 47 | |||
| 48 | static bool X11_ScancodeIsRemappable(SDL_Scancode scancode) | ||
| 49 | { | ||
| 50 | /* | ||
| 51 | * XKB remappings can assign different keysyms for these scancodes, but | ||
| 52 | * as these keys are in fixed positions, the scancodes themselves shouldn't | ||
| 53 | * be switched. Mark them as not being remappable. | ||
| 54 | */ | ||
| 55 | switch (scancode) { | ||
| 56 | case SDL_SCANCODE_ESCAPE: | ||
| 57 | case SDL_SCANCODE_CAPSLOCK: | ||
| 58 | case SDL_SCANCODE_NUMLOCKCLEAR: | ||
| 59 | case SDL_SCANCODE_LSHIFT: | ||
| 60 | case SDL_SCANCODE_RSHIFT: | ||
| 61 | case SDL_SCANCODE_LCTRL: | ||
| 62 | case SDL_SCANCODE_RCTRL: | ||
| 63 | case SDL_SCANCODE_LALT: | ||
| 64 | case SDL_SCANCODE_RALT: | ||
| 65 | case SDL_SCANCODE_LGUI: | ||
| 66 | case SDL_SCANCODE_RGUI: | ||
| 67 | return false; | ||
| 68 | default: | ||
| 69 | return true; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | // This function only correctly maps letters and numbers for keyboards in US QWERTY layout | ||
| 74 | static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode) | ||
| 75 | { | ||
| 76 | const KeySym keysym = X11_KeyCodeToSym(_this, keycode, 0, 0); | ||
| 77 | |||
| 78 | if (keysym == NoSymbol) { | ||
| 79 | return SDL_SCANCODE_UNKNOWN; | ||
| 80 | } | ||
| 81 | |||
| 82 | return SDL_GetScancodeFromKeySym(keysym, keycode); | ||
| 83 | } | ||
| 84 | |||
| 85 | KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask) | ||
| 86 | { | ||
| 87 | SDL_VideoData *data = _this->internal; | ||
| 88 | KeySym keysym; | ||
| 89 | unsigned int mods_ret[16]; | ||
| 90 | |||
| 91 | SDL_zero(mods_ret); | ||
| 92 | |||
| 93 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 94 | if (data->xkb.desc_ptr) { | ||
| 95 | int num_groups = XkbKeyNumGroups(data->xkb.desc_ptr, keycode); | ||
| 96 | unsigned char info = XkbKeyGroupInfo(data->xkb.desc_ptr, keycode); | ||
| 97 | |||
| 98 | if (num_groups && group >= num_groups) { | ||
| 99 | |||
| 100 | int action = XkbOutOfRangeGroupAction(info); | ||
| 101 | |||
| 102 | if (action == XkbRedirectIntoRange) { | ||
| 103 | group = XkbOutOfRangeGroupNumber(info); | ||
| 104 | if (group >= num_groups) { | ||
| 105 | group = 0; | ||
| 106 | } | ||
| 107 | } else if (action == XkbClampIntoRange) { | ||
| 108 | group = num_groups - 1; | ||
| 109 | } else { | ||
| 110 | group %= num_groups; | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) { | ||
| 115 | keysym = NoSymbol; | ||
| 116 | } | ||
| 117 | } else | ||
| 118 | #endif | ||
| 119 | { | ||
| 120 | // TODO: Handle groups and modifiers on the legacy path. | ||
| 121 | keysym = X11_XKeycodeToKeysym(data->display, keycode, 0); | ||
| 122 | } | ||
| 123 | |||
| 124 | return keysym; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool X11_InitKeyboard(SDL_VideoDevice *_this) | ||
| 128 | { | ||
| 129 | SDL_VideoData *data = _this->internal; | ||
| 130 | int i = 0; | ||
| 131 | int j = 0; | ||
| 132 | int min_keycode, max_keycode; | ||
| 133 | struct | ||
| 134 | { | ||
| 135 | SDL_Scancode scancode; | ||
| 136 | KeySym keysym; | ||
| 137 | int value; | ||
| 138 | } fingerprint[] = { | ||
| 139 | { SDL_SCANCODE_HOME, XK_Home, 0 }, | ||
| 140 | { SDL_SCANCODE_PAGEUP, XK_Prior, 0 }, | ||
| 141 | { SDL_SCANCODE_UP, XK_Up, 0 }, | ||
| 142 | { SDL_SCANCODE_LEFT, XK_Left, 0 }, | ||
| 143 | { SDL_SCANCODE_DELETE, XK_Delete, 0 }, | ||
| 144 | { SDL_SCANCODE_KP_ENTER, XK_KP_Enter, 0 }, | ||
| 145 | }; | ||
| 146 | int best_distance; | ||
| 147 | int best_index; | ||
| 148 | int distance; | ||
| 149 | Bool xkb_repeat = 0; | ||
| 150 | |||
| 151 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 152 | { | ||
| 153 | int xkb_major = XkbMajorVersion; | ||
| 154 | int xkb_minor = XkbMinorVersion; | ||
| 155 | |||
| 156 | if (X11_XkbQueryExtension(data->display, NULL, &data->xkb.event, NULL, &xkb_major, &xkb_minor)) { | ||
| 157 | data->xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd); | ||
| 158 | } | ||
| 159 | |||
| 160 | // This will remove KeyRelease events for held keys | ||
| 161 | X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat); | ||
| 162 | } | ||
| 163 | #endif | ||
| 164 | |||
| 165 | // Open a connection to the X input manager | ||
| 166 | #ifdef X_HAVE_UTF8_STRING | ||
| 167 | if (SDL_X11_HAVE_UTF8) { | ||
| 168 | /* Set the locale, and call XSetLocaleModifiers before XOpenIM so that | ||
| 169 | Compose keys will work correctly. */ | ||
| 170 | char *prev_locale = setlocale(LC_ALL, NULL); | ||
| 171 | char *prev_xmods = X11_XSetLocaleModifiers(NULL); | ||
| 172 | |||
| 173 | if (prev_locale) { | ||
| 174 | prev_locale = SDL_strdup(prev_locale); | ||
| 175 | } | ||
| 176 | |||
| 177 | if (prev_xmods) { | ||
| 178 | prev_xmods = SDL_strdup(prev_xmods); | ||
| 179 | } | ||
| 180 | |||
| 181 | (void)setlocale(LC_ALL, ""); | ||
| 182 | X11_XSetLocaleModifiers(""); | ||
| 183 | |||
| 184 | data->im = X11_XOpenIM(data->display, NULL, NULL, NULL); | ||
| 185 | |||
| 186 | /* Reset the locale + X locale modifiers back to how they were, | ||
| 187 | locale first because the X locale modifiers depend on it. */ | ||
| 188 | (void)setlocale(LC_ALL, prev_locale); | ||
| 189 | X11_XSetLocaleModifiers(prev_xmods); | ||
| 190 | |||
| 191 | if (prev_locale) { | ||
| 192 | SDL_free(prev_locale); | ||
| 193 | } | ||
| 194 | |||
| 195 | if (prev_xmods) { | ||
| 196 | SDL_free(prev_xmods); | ||
| 197 | } | ||
| 198 | } | ||
| 199 | #endif | ||
| 200 | // Try to determine which scancodes are being used based on fingerprint | ||
| 201 | best_distance = SDL_arraysize(fingerprint) + 1; | ||
| 202 | best_index = -1; | ||
| 203 | X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode); | ||
| 204 | for (i = 0; i < SDL_arraysize(fingerprint); ++i) { | ||
| 205 | fingerprint[i].value = X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) - min_keycode; | ||
| 206 | } | ||
| 207 | for (i = 0; i < SDL_arraysize(scancode_set); ++i) { | ||
| 208 | int table_size; | ||
| 209 | const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[i], &table_size); | ||
| 210 | |||
| 211 | distance = 0; | ||
| 212 | for (j = 0; j < SDL_arraysize(fingerprint); ++j) { | ||
| 213 | if (fingerprint[j].value < 0 || fingerprint[j].value >= table_size) { | ||
| 214 | distance += 1; | ||
| 215 | } else if (table[fingerprint[j].value] != fingerprint[j].scancode) { | ||
| 216 | distance += 1; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | if (distance < best_distance) { | ||
| 220 | best_distance = distance; | ||
| 221 | best_index = i; | ||
| 222 | } | ||
| 223 | } | ||
| 224 | if (best_index < 0 || best_distance > 2) { | ||
| 225 | // This is likely to be SDL_SCANCODE_TABLE_XFREE86_2 with remapped keys, double check a rarely remapped value | ||
| 226 | int fingerprint_value = X11_XKeysymToKeycode(data->display, 0x1008FF5B /* XF86Documents */) - min_keycode; | ||
| 227 | if (fingerprint_value == 235) { | ||
| 228 | for (i = 0; i < SDL_arraysize(scancode_set); ++i) { | ||
| 229 | if (scancode_set[i] == SDL_SCANCODE_TABLE_XFREE86_2) { | ||
| 230 | best_index = i; | ||
| 231 | best_distance = 0; | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | if (best_index >= 0 && best_distance <= 2) { | ||
| 238 | int table_size; | ||
| 239 | const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[best_index], &table_size); | ||
| 240 | |||
| 241 | #ifdef DEBUG_KEYBOARD | ||
| 242 | SDL_Log("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d", best_index, min_keycode, max_keycode, table_size); | ||
| 243 | #endif | ||
| 244 | // This should never happen, but just in case... | ||
| 245 | if (table_size > (SDL_arraysize(data->key_layout) - min_keycode)) { | ||
| 246 | table_size = (SDL_arraysize(data->key_layout) - min_keycode); | ||
| 247 | } | ||
| 248 | SDL_memcpy(&data->key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size); | ||
| 249 | |||
| 250 | /* Scancodes represent physical locations on the keyboard, unaffected by keyboard mapping. | ||
| 251 | However, there are a number of extended scancodes that have no standard location, so use | ||
| 252 | the X11 mapping for all non-character keys. | ||
| 253 | */ | ||
| 254 | for (i = min_keycode; i <= max_keycode; ++i) { | ||
| 255 | SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i); | ||
| 256 | #ifdef DEBUG_KEYBOARD | ||
| 257 | { | ||
| 258 | KeySym sym; | ||
| 259 | sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0); | ||
| 260 | SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode, | ||
| 261 | (unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym)); | ||
| 262 | } | ||
| 263 | #endif | ||
| 264 | if (scancode == data->key_layout[i]) { | ||
| 265 | continue; | ||
| 266 | } | ||
| 267 | if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) { | ||
| 268 | // Not a character key and the scancode is safe to remap | ||
| 269 | #ifdef DEBUG_KEYBOARD | ||
| 270 | SDL_Log("Changing scancode, was %d (%s), now %d (%s)", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode)); | ||
| 271 | #endif | ||
| 272 | data->key_layout[i] = scancode; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | } else { | ||
| 276 | #ifdef DEBUG_SCANCODES | ||
| 277 | SDL_Log("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):"); | ||
| 278 | #endif | ||
| 279 | |||
| 280 | // Determine key_layout - only works on US QWERTY layout | ||
| 281 | for (i = min_keycode; i <= max_keycode; ++i) { | ||
| 282 | SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i); | ||
| 283 | #ifdef DEBUG_SCANCODES | ||
| 284 | { | ||
| 285 | KeySym sym; | ||
| 286 | sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0); | ||
| 287 | SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode, | ||
| 288 | (unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym)); | ||
| 289 | } | ||
| 290 | if (scancode == SDL_SCANCODE_UNKNOWN) { | ||
| 291 | SDL_Log("scancode not found"); | ||
| 292 | } else { | ||
| 293 | SDL_Log("scancode = %d (%s)", scancode, SDL_GetScancodeName(scancode)); | ||
| 294 | } | ||
| 295 | #endif | ||
| 296 | data->key_layout[i] = scancode; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | X11_UpdateKeymap(_this, false); | ||
| 301 | |||
| 302 | SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu"); | ||
| 303 | |||
| 304 | X11_ReconcileKeyboardState(_this); | ||
| 305 | |||
| 306 | return true; | ||
| 307 | } | ||
| 308 | |||
| 309 | static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this) | ||
| 310 | { | ||
| 311 | SDL_VideoData *videodata = _this->internal; | ||
| 312 | Display *display = videodata->display; | ||
| 313 | unsigned num_mask = 0; | ||
| 314 | int i, j; | ||
| 315 | XModifierKeymap *xmods; | ||
| 316 | unsigned n; | ||
| 317 | |||
| 318 | xmods = X11_XGetModifierMapping(display); | ||
| 319 | n = xmods->max_keypermod; | ||
| 320 | for (i = 3; i < 8; i++) { | ||
| 321 | for (j = 0; j < n; j++) { | ||
| 322 | KeyCode kc = xmods->modifiermap[i * n + j]; | ||
| 323 | if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) { | ||
| 324 | num_mask = 1 << i; | ||
| 325 | break; | ||
| 326 | } | ||
| 327 | } | ||
| 328 | } | ||
| 329 | X11_XFreeModifiermap(xmods); | ||
| 330 | |||
| 331 | return num_mask; | ||
| 332 | } | ||
| 333 | |||
| 334 | static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this) | ||
| 335 | { | ||
| 336 | SDL_VideoData *videodata = _this->internal; | ||
| 337 | Display *display = videodata->display; | ||
| 338 | unsigned num_mask = 0; | ||
| 339 | int i, j; | ||
| 340 | XModifierKeymap *xmods; | ||
| 341 | unsigned n; | ||
| 342 | |||
| 343 | xmods = X11_XGetModifierMapping(display); | ||
| 344 | n = xmods->max_keypermod; | ||
| 345 | for (i = 3; i < 8; i++) { | ||
| 346 | for (j = 0; j < n; j++) { | ||
| 347 | KeyCode kc = xmods->modifiermap[i * n + j]; | ||
| 348 | if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) { | ||
| 349 | num_mask = 1 << i; | ||
| 350 | break; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | } | ||
| 354 | X11_XFreeModifiermap(xmods); | ||
| 355 | |||
| 356 | return num_mask; | ||
| 357 | } | ||
| 358 | |||
| 359 | void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event) | ||
| 360 | { | ||
| 361 | struct Keymod_masks | ||
| 362 | { | ||
| 363 | SDL_Keymod sdl_mask; | ||
| 364 | unsigned int xkb_mask; | ||
| 365 | } const keymod_masks[] = { | ||
| 366 | { SDL_KMOD_NONE, 0 }, | ||
| 367 | { SDL_KMOD_SHIFT, ShiftMask }, | ||
| 368 | { SDL_KMOD_CAPS, LockMask }, | ||
| 369 | { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask }, | ||
| 370 | { SDL_KMOD_MODE, Mod5Mask }, | ||
| 371 | { SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask }, | ||
| 372 | { SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask }, | ||
| 373 | { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask }, | ||
| 374 | { SDL_KMOD_LEVEL5, Mod3Mask }, | ||
| 375 | { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, Mod3Mask | ShiftMask }, | ||
| 376 | { SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, Mod3Mask | LockMask }, | ||
| 377 | { SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | ShiftMask | LockMask }, | ||
| 378 | { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, Mod5Mask | Mod3Mask }, | ||
| 379 | { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod3Mask | Mod5Mask | ShiftMask }, | ||
| 380 | { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | LockMask }, | ||
| 381 | { SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | ShiftMask | LockMask } | ||
| 382 | }; | ||
| 383 | |||
| 384 | SDL_VideoData *data = _this->internal; | ||
| 385 | SDL_Scancode scancode; | ||
| 386 | SDL_Keymap *keymap = SDL_CreateKeymap(); | ||
| 387 | |||
| 388 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 389 | if (data->xkb.desc_ptr) { | ||
| 390 | XkbStateRec state; | ||
| 391 | X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb.desc_ptr); | ||
| 392 | |||
| 393 | if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) { | ||
| 394 | data->xkb.current_group = state.group; | ||
| 395 | } | ||
| 396 | } | ||
| 397 | #endif | ||
| 398 | |||
| 399 | for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) { | ||
| 400 | for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) { | ||
| 401 | // Make sure this is a valid scancode | ||
| 402 | scancode = data->key_layout[i]; | ||
| 403 | if (scancode == SDL_SCANCODE_UNKNOWN) { | ||
| 404 | continue; | ||
| 405 | } | ||
| 406 | |||
| 407 | const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask); | ||
| 408 | |||
| 409 | if (keysym != NoSymbol) { | ||
| 410 | SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask); | ||
| 411 | |||
| 412 | if (!keycode) { | ||
| 413 | switch (scancode) { | ||
| 414 | case SDL_SCANCODE_RETURN: | ||
| 415 | keycode = SDLK_RETURN; | ||
| 416 | break; | ||
| 417 | case SDL_SCANCODE_ESCAPE: | ||
| 418 | keycode = SDLK_ESCAPE; | ||
| 419 | break; | ||
| 420 | case SDL_SCANCODE_BACKSPACE: | ||
| 421 | keycode = SDLK_BACKSPACE; | ||
| 422 | break; | ||
| 423 | case SDL_SCANCODE_DELETE: | ||
| 424 | keycode = SDLK_DELETE; | ||
| 425 | break; | ||
| 426 | default: | ||
| 427 | keycode = SDL_SCANCODE_TO_KEYCODE(scancode); | ||
| 428 | break; | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | } | ||
| 436 | |||
| 437 | data->xkb.numlock_mask = X11_GetNumLockModifierMask(_this); | ||
| 438 | data->xkb.scrolllock_mask = X11_GetScrollLockModifierMask(_this); | ||
| 439 | SDL_SetKeymap(keymap, send_event); | ||
| 440 | } | ||
| 441 | |||
| 442 | void X11_QuitKeyboard(SDL_VideoDevice *_this) | ||
| 443 | { | ||
| 444 | SDL_VideoData *data = _this->internal; | ||
| 445 | |||
| 446 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 447 | if (data->xkb.desc_ptr) { | ||
| 448 | X11_XkbFreeKeyboard(data->xkb.desc_ptr, 0, True); | ||
| 449 | data->xkb.desc_ptr = NULL; | ||
| 450 | } | ||
| 451 | #endif | ||
| 452 | } | ||
| 453 | |||
| 454 | void X11_ClearComposition(SDL_WindowData *data) | ||
| 455 | { | ||
| 456 | if (data->preedit_length > 0) { | ||
| 457 | data->preedit_text[0] = '\0'; | ||
| 458 | data->preedit_length = 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | if (data->ime_needs_clear_composition) { | ||
| 462 | SDL_SendEditingText("", 0, 0); | ||
| 463 | data->ime_needs_clear_composition = false; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | static void X11_SendEditingEvent(SDL_WindowData *data) | ||
| 468 | { | ||
| 469 | if (data->preedit_length == 0) { | ||
| 470 | X11_ClearComposition(data); | ||
| 471 | return; | ||
| 472 | } | ||
| 473 | |||
| 474 | bool in_highlight = false; | ||
| 475 | int start = -1, length = 0, i; | ||
| 476 | for (i = 0; i < data->preedit_length; ++i) { | ||
| 477 | if (data->preedit_feedback[i] & (XIMReverse | XIMHighlight)) { | ||
| 478 | if (start < 0) { | ||
| 479 | start = i; | ||
| 480 | in_highlight = true; | ||
| 481 | } | ||
| 482 | } else if (in_highlight) { | ||
| 483 | // Found the end of the highlight | ||
| 484 | break; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | if (in_highlight) { | ||
| 488 | length = (i - start); | ||
| 489 | } else { | ||
| 490 | start = SDL_clamp(data->preedit_cursor, 0, data->preedit_length); | ||
| 491 | } | ||
| 492 | SDL_SendEditingText(data->preedit_text, start, length); | ||
| 493 | |||
| 494 | data->ime_needs_clear_composition = true; | ||
| 495 | } | ||
| 496 | |||
| 497 | static int preedit_start_callback(XIC xic, XPointer client_data, XPointer call_data) | ||
| 498 | { | ||
| 499 | // No limit on preedit text length | ||
| 500 | return -1; | ||
| 501 | } | ||
| 502 | |||
| 503 | static void preedit_done_callback(XIC xic, XPointer client_data, XPointer call_data) | ||
| 504 | { | ||
| 505 | } | ||
| 506 | |||
| 507 | static void preedit_draw_callback(XIC xic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data) | ||
| 508 | { | ||
| 509 | SDL_WindowData *data = (SDL_WindowData *)client_data; | ||
| 510 | int chg_first = SDL_clamp(call_data->chg_first, 0, data->preedit_length); | ||
| 511 | int chg_length = SDL_clamp(call_data->chg_length, 0, data->preedit_length - chg_first); | ||
| 512 | |||
| 513 | const char *start = data->preedit_text; | ||
| 514 | if (chg_length > 0) { | ||
| 515 | // Delete text in range | ||
| 516 | for (int i = 0; start && *start && i < chg_first; ++i) { | ||
| 517 | SDL_StepUTF8(&start, NULL); | ||
| 518 | } | ||
| 519 | |||
| 520 | const char *end = start; | ||
| 521 | for (int i = 0; end && *end && i < chg_length; ++i) { | ||
| 522 | SDL_StepUTF8(&end, NULL); | ||
| 523 | } | ||
| 524 | |||
| 525 | if (end > start) { | ||
| 526 | SDL_memmove((char *)start, end, SDL_strlen(end) + 1); | ||
| 527 | if ((chg_first + chg_length) > data->preedit_length) { | ||
| 528 | SDL_memmove(&data->preedit_feedback[chg_first], &data->preedit_feedback[chg_first + chg_length], (data->preedit_length - chg_first - chg_length) * sizeof(*data->preedit_feedback)); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | data->preedit_length -= chg_length; | ||
| 532 | } | ||
| 533 | |||
| 534 | XIMText *text = call_data->text; | ||
| 535 | if (text) { | ||
| 536 | // Insert text in range | ||
| 537 | SDL_assert(!text->encoding_is_wchar); | ||
| 538 | |||
| 539 | // The text length isn't calculated as directed by the spec, recalculate it now | ||
| 540 | if (text->string.multi_byte) { | ||
| 541 | text->length = SDL_utf8strlen(text->string.multi_byte); | ||
| 542 | } | ||
| 543 | |||
| 544 | size_t string_size = SDL_strlen(text->string.multi_byte); | ||
| 545 | size_t size = string_size + 1; | ||
| 546 | if (data->preedit_text) { | ||
| 547 | size += SDL_strlen(data->preedit_text); | ||
| 548 | } | ||
| 549 | char *preedit_text = (char *)SDL_malloc(size * sizeof(*preedit_text)); | ||
| 550 | if (preedit_text) { | ||
| 551 | size_t pre_size = (start - data->preedit_text); | ||
| 552 | size_t post_size = start ? SDL_strlen(start) : 0; | ||
| 553 | if (pre_size > 0) { | ||
| 554 | SDL_memcpy(&preedit_text[0], data->preedit_text, pre_size); | ||
| 555 | } | ||
| 556 | SDL_memcpy(&preedit_text[pre_size], text->string.multi_byte, string_size); | ||
| 557 | if (post_size > 0) { | ||
| 558 | SDL_memcpy(&preedit_text[pre_size + string_size], start, post_size); | ||
| 559 | } | ||
| 560 | preedit_text[size - 1] = '\0'; | ||
| 561 | } | ||
| 562 | |||
| 563 | size_t feedback_size = data->preedit_length + text->length; | ||
| 564 | XIMFeedback *feedback = (XIMFeedback *)SDL_malloc(feedback_size * sizeof(*feedback)); | ||
| 565 | if (feedback) { | ||
| 566 | size_t pre_size = (size_t)chg_first; | ||
| 567 | size_t post_size = (size_t)data->preedit_length - pre_size; | ||
| 568 | if (pre_size > 0) { | ||
| 569 | SDL_memcpy(&feedback[0], data->preedit_feedback, pre_size * sizeof(*feedback)); | ||
| 570 | } | ||
| 571 | SDL_memcpy(&feedback[pre_size], text->feedback, text->length * sizeof(*feedback)); | ||
| 572 | if (post_size > 0) { | ||
| 573 | SDL_memcpy(&feedback[pre_size + text->length], &data->preedit_feedback[pre_size], post_size * sizeof(*feedback)); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | |||
| 577 | if (preedit_text && feedback) { | ||
| 578 | SDL_free(data->preedit_text); | ||
| 579 | data->preedit_text = preedit_text; | ||
| 580 | |||
| 581 | SDL_free(data->preedit_feedback); | ||
| 582 | data->preedit_feedback = feedback; | ||
| 583 | |||
| 584 | data->preedit_length += text->length; | ||
| 585 | } else { | ||
| 586 | SDL_free(preedit_text); | ||
| 587 | SDL_free(feedback); | ||
| 588 | } | ||
| 589 | } | ||
| 590 | |||
| 591 | data->preedit_cursor = call_data->caret; | ||
| 592 | |||
| 593 | #ifdef DEBUG_XIM | ||
| 594 | if (call_data->chg_length > 0) { | ||
| 595 | SDL_Log("Draw callback deleted %d characters at %d", call_data->chg_length, call_data->chg_first); | ||
| 596 | } | ||
| 597 | if (text) { | ||
| 598 | SDL_Log("Draw callback inserted %s at %d, caret: %d", text->string.multi_byte, call_data->chg_first, call_data->caret); | ||
| 599 | } | ||
| 600 | SDL_Log("Pre-edit text: %s", data->preedit_text); | ||
| 601 | #endif | ||
| 602 | |||
| 603 | X11_SendEditingEvent(data); | ||
| 604 | } | ||
| 605 | |||
| 606 | static void preedit_caret_callback(XIC xic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data) | ||
| 607 | { | ||
| 608 | SDL_WindowData *data = (SDL_WindowData *)client_data; | ||
| 609 | |||
| 610 | switch (call_data->direction) { | ||
| 611 | case XIMAbsolutePosition: | ||
| 612 | if (call_data->position != data->preedit_cursor) { | ||
| 613 | data->preedit_cursor = call_data->position; | ||
| 614 | X11_SendEditingEvent(data); | ||
| 615 | } | ||
| 616 | break; | ||
| 617 | case XIMDontChange: | ||
| 618 | break; | ||
| 619 | default: | ||
| 620 | // Not currently supported | ||
| 621 | break; | ||
| 622 | } | ||
| 623 | } | ||
| 624 | |||
| 625 | void X11_CreateInputContext(SDL_WindowData *data) | ||
| 626 | { | ||
| 627 | #ifdef X_HAVE_UTF8_STRING | ||
| 628 | SDL_VideoData *videodata = data->videodata; | ||
| 629 | |||
| 630 | if (SDL_X11_HAVE_UTF8 && videodata->im) { | ||
| 631 | const char *hint = SDL_GetHint(SDL_HINT_IME_IMPLEMENTED_UI); | ||
| 632 | if (hint && SDL_strstr(hint, "composition")) { | ||
| 633 | XIMCallback draw_callback; | ||
| 634 | draw_callback.client_data = (XPointer)data; | ||
| 635 | draw_callback.callback = (XIMProc)preedit_draw_callback; | ||
| 636 | |||
| 637 | XIMCallback start_callback; | ||
| 638 | start_callback.client_data = (XPointer)data; | ||
| 639 | start_callback.callback = (XIMProc)preedit_start_callback; | ||
| 640 | |||
| 641 | XIMCallback done_callback; | ||
| 642 | done_callback.client_data = (XPointer)data; | ||
| 643 | done_callback.callback = (XIMProc)preedit_done_callback; | ||
| 644 | |||
| 645 | XIMCallback caret_callback; | ||
| 646 | caret_callback.client_data = (XPointer)data; | ||
| 647 | caret_callback.callback = (XIMProc)preedit_caret_callback; | ||
| 648 | |||
| 649 | XVaNestedList attr = X11_XVaCreateNestedList(0, | ||
| 650 | XNPreeditStartCallback, &start_callback, | ||
| 651 | XNPreeditDoneCallback, &done_callback, | ||
| 652 | XNPreeditDrawCallback, &draw_callback, | ||
| 653 | XNPreeditCaretCallback, &caret_callback, | ||
| 654 | NULL); | ||
| 655 | if (attr) { | ||
| 656 | data->ic = X11_XCreateIC(videodata->im, | ||
| 657 | XNInputStyle, XIMPreeditCallbacks | XIMStatusCallbacks, | ||
| 658 | XNPreeditAttributes, attr, | ||
| 659 | XNClientWindow, data->xwindow, | ||
| 660 | NULL); | ||
| 661 | X11_XFree(attr); | ||
| 662 | } | ||
| 663 | } | ||
| 664 | if (!data->ic) { | ||
| 665 | data->ic = X11_XCreateIC(videodata->im, | ||
| 666 | XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | ||
| 667 | XNClientWindow, data->xwindow, | ||
| 668 | NULL); | ||
| 669 | } | ||
| 670 | data->xim_spot.x = -1; | ||
| 671 | data->xim_spot.y = -1; | ||
| 672 | } | ||
| 673 | #endif // X_HAVE_UTF8_STRING | ||
| 674 | } | ||
| 675 | |||
| 676 | static void X11_ResetXIM(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 677 | { | ||
| 678 | #ifdef X_HAVE_UTF8_STRING | ||
| 679 | SDL_WindowData *data = window->internal; | ||
| 680 | |||
| 681 | if (data && data->ic) { | ||
| 682 | // Clear any partially entered dead keys | ||
| 683 | char *contents = X11_Xutf8ResetIC(data->ic); | ||
| 684 | if (contents) { | ||
| 685 | X11_XFree(contents); | ||
| 686 | } | ||
| 687 | } | ||
| 688 | #endif // X_HAVE_UTF8_STRING | ||
| 689 | } | ||
| 690 | |||
| 691 | bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) | ||
| 692 | { | ||
| 693 | X11_ResetXIM(_this, window); | ||
| 694 | |||
| 695 | return X11_UpdateTextInputArea(_this, window); | ||
| 696 | } | ||
| 697 | |||
| 698 | bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 699 | { | ||
| 700 | X11_ResetXIM(_this, window); | ||
| 701 | return true; | ||
| 702 | } | ||
| 703 | |||
| 704 | bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 705 | { | ||
| 706 | #ifdef X_HAVE_UTF8_STRING | ||
| 707 | SDL_WindowData *data = window->internal; | ||
| 708 | |||
| 709 | if (data && data->ic) { | ||
| 710 | XPoint spot; | ||
| 711 | spot.x = window->text_input_rect.x + window->text_input_cursor; | ||
| 712 | spot.y = window->text_input_rect.y + window->text_input_rect.h; | ||
| 713 | if (spot.x != data->xim_spot.x || spot.y != data->xim_spot.y) { | ||
| 714 | XVaNestedList attr = X11_XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); | ||
| 715 | if (attr) { | ||
| 716 | X11_XSetICValues(data->ic, XNPreeditAttributes, attr, NULL); | ||
| 717 | X11_XFree(attr); | ||
| 718 | } | ||
| 719 | SDL_copyp(&data->xim_spot, &spot); | ||
| 720 | } | ||
| 721 | } | ||
| 722 | #endif | ||
| 723 | return true; | ||
| 724 | } | ||
| 725 | |||
| 726 | bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this) | ||
| 727 | { | ||
| 728 | SDL_VideoData *videodata = _this->internal; | ||
| 729 | return videodata->is_steam_deck; | ||
| 730 | } | ||
| 731 | |||
| 732 | void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props) | ||
| 733 | { | ||
| 734 | SDL_VideoData *videodata = _this->internal; | ||
| 735 | |||
| 736 | if (videodata->is_steam_deck) { | ||
| 737 | /* For more documentation of the URL parameters, see: | ||
| 738 | * https://partner.steamgames.com/doc/api/ISteamUtils#ShowFloatingGamepadTextInput | ||
| 739 | */ | ||
| 740 | const int k_EFloatingGamepadTextInputModeModeSingleLine = 0; // Enter dismisses the keyboard | ||
| 741 | const int k_EFloatingGamepadTextInputModeModeMultipleLines = 1; // User needs to explicitly dismiss the keyboard | ||
| 742 | const int k_EFloatingGamepadTextInputModeModeEmail = 2; // Keyboard is displayed in a special mode that makes it easier to enter emails | ||
| 743 | const int k_EFloatingGamepadTextInputModeModeNumeric = 3; // Numeric keypad is shown | ||
| 744 | char deeplink[128]; | ||
| 745 | int mode; | ||
| 746 | |||
| 747 | switch (SDL_GetTextInputType(props)) { | ||
| 748 | case SDL_TEXTINPUT_TYPE_TEXT_EMAIL: | ||
| 749 | mode = k_EFloatingGamepadTextInputModeModeEmail; | ||
| 750 | break; | ||
| 751 | case SDL_TEXTINPUT_TYPE_NUMBER: | ||
| 752 | case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN: | ||
| 753 | case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE: | ||
| 754 | mode = k_EFloatingGamepadTextInputModeModeNumeric; | ||
| 755 | break; | ||
| 756 | default: | ||
| 757 | if (SDL_GetTextInputMultiline(props)) { | ||
| 758 | mode = k_EFloatingGamepadTextInputModeModeMultipleLines; | ||
| 759 | } else { | ||
| 760 | mode = k_EFloatingGamepadTextInputModeModeSingleLine; | ||
| 761 | } | ||
| 762 | break; | ||
| 763 | } | ||
| 764 | (void)SDL_snprintf(deeplink, sizeof(deeplink), | ||
| 765 | "steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d", | ||
| 766 | mode); | ||
| 767 | SDL_OpenURL(deeplink); | ||
| 768 | videodata->steam_keyboard_open = true; | ||
| 769 | } | ||
| 770 | } | ||
| 771 | |||
| 772 | void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 773 | { | ||
| 774 | SDL_VideoData *videodata = _this->internal; | ||
| 775 | |||
| 776 | if (videodata->is_steam_deck) { | ||
| 777 | SDL_OpenURL("steam://close/keyboard"); | ||
| 778 | videodata->steam_keyboard_open = false; | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 782 | bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 783 | { | ||
| 784 | SDL_VideoData *videodata = _this->internal; | ||
| 785 | |||
| 786 | return videodata->steam_keyboard_open; | ||
| 787 | } | ||
| 788 | |||
| 789 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h new file mode 100644 index 0000000..a6cd2f7 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11keyboard.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11keyboard_h_ | ||
| 24 | #define SDL_x11keyboard_h_ | ||
| 25 | |||
| 26 | extern bool X11_InitKeyboard(SDL_VideoDevice *_this); | ||
| 27 | extern void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event); | ||
| 28 | extern void X11_QuitKeyboard(SDL_VideoDevice *_this); | ||
| 29 | extern void X11_CreateInputContext(SDL_WindowData *data); | ||
| 30 | extern void X11_ClearComposition(SDL_WindowData *data); | ||
| 31 | extern bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); | ||
| 32 | extern bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 33 | extern bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 34 | extern bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this); | ||
| 35 | extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props); | ||
| 36 | extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 37 | extern bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 38 | extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask); | ||
| 39 | |||
| 40 | #endif // SDL_x11keyboard_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c new file mode 100644 index 0000000..8aa1c6a --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.c | |||
| @@ -0,0 +1,887 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 25 | |||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11dyn.h" | ||
| 28 | #include "SDL_x11messagebox.h" | ||
| 29 | |||
| 30 | #include <X11/keysym.h> | ||
| 31 | #include <locale.h> | ||
| 32 | |||
| 33 | #define SDL_FORK_MESSAGEBOX 1 | ||
| 34 | #define SDL_SET_LOCALE 1 | ||
| 35 | |||
| 36 | #if SDL_FORK_MESSAGEBOX | ||
| 37 | #include <sys/types.h> | ||
| 38 | #include <sys/wait.h> | ||
| 39 | #include <unistd.h> | ||
| 40 | #include <errno.h> | ||
| 41 | #endif | ||
| 42 | |||
| 43 | #define MAX_BUTTONS 8 // Maximum number of buttons supported | ||
| 44 | #define MIN_BUTTON_WIDTH 64 // Minimum button width | ||
| 45 | #define MIN_DIALOG_WIDTH 200 // Minimum dialog width | ||
| 46 | #define MIN_DIALOG_HEIGHT 100 // Minimum dialog height | ||
| 47 | |||
| 48 | static const char g_MessageBoxFontLatin1[] = | ||
| 49 | "-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1"; | ||
| 50 | |||
| 51 | static const char* g_MessageBoxFont[] = { | ||
| 52 | "-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) | ||
| 53 | "-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1) | ||
| 54 | "-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems) | ||
| 55 | "-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode. | ||
| 56 | "-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. | ||
| 57 | "-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out. | ||
| 58 | "-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems) | ||
| 59 | "-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1. | ||
| 60 | NULL | ||
| 61 | }; | ||
| 62 | |||
| 63 | static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = { | ||
| 64 | { 56, 54, 53 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND, | ||
| 65 | { 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT, | ||
| 66 | { 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, | ||
| 67 | { 105, 102, 99 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, | ||
| 68 | { 205, 202, 53 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, | ||
| 69 | }; | ||
| 70 | |||
| 71 | #define SDL_MAKE_RGB(_r, _g, _b) (((Uint32)(_r) << 16) | \ | ||
| 72 | ((Uint32)(_g) << 8) | \ | ||
| 73 | ((Uint32)(_b))) | ||
| 74 | |||
| 75 | typedef struct SDL_MessageBoxButtonDataX11 | ||
| 76 | { | ||
| 77 | int x, y; // Text position | ||
| 78 | int length; // Text length | ||
| 79 | int text_width; // Text width | ||
| 80 | |||
| 81 | SDL_Rect rect; // Rectangle for entire button | ||
| 82 | |||
| 83 | const SDL_MessageBoxButtonData *buttondata; // Button data from caller | ||
| 84 | } SDL_MessageBoxButtonDataX11; | ||
| 85 | |||
| 86 | typedef struct TextLineData | ||
| 87 | { | ||
| 88 | int width; // Width of this text line | ||
| 89 | int length; // String length of this text line | ||
| 90 | const char *text; // Text for this line | ||
| 91 | } TextLineData; | ||
| 92 | |||
| 93 | typedef struct SDL_MessageBoxDataX11 | ||
| 94 | { | ||
| 95 | Display *display; | ||
| 96 | int screen; | ||
| 97 | Window window; | ||
| 98 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 99 | XdbeBackBuffer buf; | ||
| 100 | bool xdbe; // Whether Xdbe is present or not | ||
| 101 | #endif | ||
| 102 | long event_mask; | ||
| 103 | Atom wm_protocols; | ||
| 104 | Atom wm_delete_message; | ||
| 105 | |||
| 106 | int dialog_width; // Dialog box width. | ||
| 107 | int dialog_height; // Dialog box height. | ||
| 108 | |||
| 109 | XFontSet font_set; // for UTF-8 systems | ||
| 110 | XFontStruct *font_struct; // Latin1 (ASCII) fallback. | ||
| 111 | int xtext, ytext; // Text position to start drawing at. | ||
| 112 | int numlines; // Count of Text lines. | ||
| 113 | int text_height; // Height for text lines. | ||
| 114 | TextLineData *linedata; | ||
| 115 | |||
| 116 | int *pbuttonid; // Pointer to user return buttonID value. | ||
| 117 | |||
| 118 | int button_press_index; // Index into buttondata/buttonpos for button which is pressed (or -1). | ||
| 119 | int mouse_over_index; // Index into buttondata/buttonpos for button mouse is over (or -1). | ||
| 120 | |||
| 121 | int numbuttons; // Count of buttons. | ||
| 122 | const SDL_MessageBoxButtonData *buttondata; | ||
| 123 | SDL_MessageBoxButtonDataX11 buttonpos[MAX_BUTTONS]; | ||
| 124 | |||
| 125 | Uint32 color[SDL_MESSAGEBOX_COLOR_COUNT]; | ||
| 126 | |||
| 127 | const SDL_MessageBoxData *messageboxdata; | ||
| 128 | } SDL_MessageBoxDataX11; | ||
| 129 | |||
| 130 | // Maximum helper for ints. | ||
| 131 | static SDL_INLINE int IntMax(int a, int b) | ||
| 132 | { | ||
| 133 | return (a > b) ? a : b; | ||
| 134 | } | ||
| 135 | |||
| 136 | // Return width and height for a string. | ||
| 137 | static void GetTextWidthHeight(SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight) | ||
| 138 | { | ||
| 139 | #ifdef X_HAVE_UTF8_STRING | ||
| 140 | if (SDL_X11_HAVE_UTF8) { | ||
| 141 | XRectangle overall_ink, overall_logical; | ||
| 142 | X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical); | ||
| 143 | *pwidth = overall_logical.width; | ||
| 144 | *pheight = overall_logical.height; | ||
| 145 | } else | ||
| 146 | #endif | ||
| 147 | { | ||
| 148 | XCharStruct text_structure; | ||
| 149 | int font_direction, font_ascent, font_descent; | ||
| 150 | X11_XTextExtents(data->font_struct, str, nbytes, | ||
| 151 | &font_direction, &font_ascent, &font_descent, | ||
| 152 | &text_structure); | ||
| 153 | *pwidth = text_structure.width; | ||
| 154 | *pheight = text_structure.ascent + text_structure.descent; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | // Return index of button if position x,y is contained therein. | ||
| 159 | static int GetHitButtonIndex(SDL_MessageBoxDataX11 *data, int x, int y) | ||
| 160 | { | ||
| 161 | int i; | ||
| 162 | int numbuttons = data->numbuttons; | ||
| 163 | SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos; | ||
| 164 | |||
| 165 | for (i = 0; i < numbuttons; i++) { | ||
| 166 | SDL_Rect *rect = &buttonpos[i].rect; | ||
| 167 | |||
| 168 | if ((x >= rect->x) && | ||
| 169 | (x <= (rect->x + rect->w)) && | ||
| 170 | (y >= rect->y) && | ||
| 171 | (y <= (rect->y + rect->h))) { | ||
| 172 | return i; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | return -1; | ||
| 177 | } | ||
| 178 | |||
| 179 | // Initialize SDL_MessageBoxData structure and Display, etc. | ||
| 180 | static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid) | ||
| 181 | { | ||
| 182 | int i; | ||
| 183 | int numbuttons = messageboxdata->numbuttons; | ||
| 184 | const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons; | ||
| 185 | const SDL_MessageBoxColor *colorhints; | ||
| 186 | |||
| 187 | if (numbuttons > MAX_BUTTONS) { | ||
| 188 | return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS); | ||
| 189 | } | ||
| 190 | |||
| 191 | data->dialog_width = MIN_DIALOG_WIDTH; | ||
| 192 | data->dialog_height = MIN_DIALOG_HEIGHT; | ||
| 193 | data->messageboxdata = messageboxdata; | ||
| 194 | data->buttondata = buttondata; | ||
| 195 | data->numbuttons = numbuttons; | ||
| 196 | data->pbuttonid = pbuttonid; | ||
| 197 | |||
| 198 | data->display = X11_XOpenDisplay(NULL); | ||
| 199 | if (!data->display) { | ||
| 200 | return SDL_SetError("Couldn't open X11 display"); | ||
| 201 | } | ||
| 202 | |||
| 203 | #ifdef X_HAVE_UTF8_STRING | ||
| 204 | if (SDL_X11_HAVE_UTF8) { | ||
| 205 | char **missing = NULL; | ||
| 206 | int num_missing = 0; | ||
| 207 | int i_font; | ||
| 208 | for (i_font = 0; g_MessageBoxFont[i_font]; ++i_font) { | ||
| 209 | data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont[i_font], | ||
| 210 | &missing, &num_missing, NULL); | ||
| 211 | if (missing) { | ||
| 212 | X11_XFreeStringList(missing); | ||
| 213 | } | ||
| 214 | if (data->font_set) { | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | if (!data->font_set) { | ||
| 219 | return SDL_SetError("Couldn't load x11 message box font"); | ||
| 220 | } | ||
| 221 | } else | ||
| 222 | #endif | ||
| 223 | { | ||
| 224 | data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1); | ||
| 225 | if (!data->font_struct) { | ||
| 226 | return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | if (messageboxdata->colorScheme) { | ||
| 231 | colorhints = messageboxdata->colorScheme->colors; | ||
| 232 | } else { | ||
| 233 | colorhints = g_default_colors; | ||
| 234 | } | ||
| 235 | |||
| 236 | // Convert our SDL_MessageBoxColor r,g,b values to packed RGB format. | ||
| 237 | for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) { | ||
| 238 | data->color[i] = SDL_MAKE_RGB(colorhints[i].r, colorhints[i].g, colorhints[i].b); | ||
| 239 | } | ||
| 240 | |||
| 241 | return true; | ||
| 242 | } | ||
| 243 | |||
| 244 | static int CountLinesOfText(const char *text) | ||
| 245 | { | ||
| 246 | int result = 0; | ||
| 247 | while (text && *text) { | ||
| 248 | const char *lf = SDL_strchr(text, '\n'); | ||
| 249 | result++; // even without an endline, this counts as a line. | ||
| 250 | text = lf ? lf + 1 : NULL; | ||
| 251 | } | ||
| 252 | return result; | ||
| 253 | } | ||
| 254 | |||
| 255 | // Calculate and initialize text and button locations. | ||
| 256 | static bool X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data) | ||
| 257 | { | ||
| 258 | int i; | ||
| 259 | int ybuttons; | ||
| 260 | int text_width_max = 0; | ||
| 261 | int button_text_height = 0; | ||
| 262 | int button_width = MIN_BUTTON_WIDTH; | ||
| 263 | const SDL_MessageBoxData *messageboxdata = data->messageboxdata; | ||
| 264 | |||
| 265 | // Go over text and break linefeeds into separate lines. | ||
| 266 | if (messageboxdata && messageboxdata->message[0]) { | ||
| 267 | const char *text = messageboxdata->message; | ||
| 268 | const int linecount = CountLinesOfText(text); | ||
| 269 | TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount); | ||
| 270 | |||
| 271 | if (!plinedata) { | ||
| 272 | return false; | ||
| 273 | } | ||
| 274 | |||
| 275 | data->linedata = plinedata; | ||
| 276 | data->numlines = linecount; | ||
| 277 | |||
| 278 | for (i = 0; i < linecount; i++, plinedata++) { | ||
| 279 | const char *lf = SDL_strchr(text, '\n'); | ||
| 280 | const int length = lf ? (lf - text) : SDL_strlen(text); | ||
| 281 | int height; | ||
| 282 | |||
| 283 | plinedata->text = text; | ||
| 284 | |||
| 285 | GetTextWidthHeight(data, text, length, &plinedata->width, &height); | ||
| 286 | |||
| 287 | // Text and widths are the largest we've ever seen. | ||
| 288 | data->text_height = IntMax(data->text_height, height); | ||
| 289 | text_width_max = IntMax(text_width_max, plinedata->width); | ||
| 290 | |||
| 291 | plinedata->length = length; | ||
| 292 | if (lf && (lf > text) && (lf[-1] == '\r')) { | ||
| 293 | plinedata->length--; | ||
| 294 | } | ||
| 295 | |||
| 296 | text += length + 1; | ||
| 297 | |||
| 298 | // Break if there are no more linefeeds. | ||
| 299 | if (!lf) { | ||
| 300 | break; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | // Bump up the text height slightly. | ||
| 305 | data->text_height += 2; | ||
| 306 | } | ||
| 307 | |||
| 308 | // Loop through all buttons and calculate the button widths and height. | ||
| 309 | for (i = 0; i < data->numbuttons; i++) { | ||
| 310 | int height; | ||
| 311 | |||
| 312 | data->buttonpos[i].buttondata = &data->buttondata[i]; | ||
| 313 | data->buttonpos[i].length = SDL_strlen(data->buttondata[i].text); | ||
| 314 | |||
| 315 | GetTextWidthHeight(data, data->buttondata[i].text, SDL_strlen(data->buttondata[i].text), | ||
| 316 | &data->buttonpos[i].text_width, &height); | ||
| 317 | |||
| 318 | button_width = IntMax(button_width, data->buttonpos[i].text_width); | ||
| 319 | button_text_height = IntMax(button_text_height, height); | ||
| 320 | } | ||
| 321 | |||
| 322 | if (data->numlines) { | ||
| 323 | // x,y for this line of text. | ||
| 324 | data->xtext = data->text_height; | ||
| 325 | data->ytext = data->text_height + data->text_height; | ||
| 326 | |||
| 327 | // Bump button y down to bottom of text. | ||
| 328 | ybuttons = 3 * data->ytext / 2 + (data->numlines - 1) * data->text_height; | ||
| 329 | |||
| 330 | // Bump the dialog box width and height up if needed. | ||
| 331 | data->dialog_width = IntMax(data->dialog_width, 2 * data->xtext + text_width_max); | ||
| 332 | data->dialog_height = IntMax(data->dialog_height, ybuttons); | ||
| 333 | } else { | ||
| 334 | // Button y starts at height of button text. | ||
| 335 | ybuttons = button_text_height; | ||
| 336 | } | ||
| 337 | |||
| 338 | if (data->numbuttons) { | ||
| 339 | int x, y; | ||
| 340 | int width_of_buttons; | ||
| 341 | int button_spacing = button_text_height; | ||
| 342 | int button_height = 2 * button_text_height; | ||
| 343 | |||
| 344 | // Bump button width up a bit. | ||
| 345 | button_width += button_text_height; | ||
| 346 | |||
| 347 | // Get width of all buttons lined up. | ||
| 348 | width_of_buttons = data->numbuttons * button_width + (data->numbuttons - 1) * button_spacing; | ||
| 349 | |||
| 350 | // Bump up dialog width and height if buttons are wider than text. | ||
| 351 | data->dialog_width = IntMax(data->dialog_width, width_of_buttons + 2 * button_spacing); | ||
| 352 | data->dialog_height = IntMax(data->dialog_height, ybuttons + 2 * button_height); | ||
| 353 | |||
| 354 | // Location for first button. | ||
| 355 | if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { | ||
| 356 | x = data->dialog_width - (data->dialog_width - width_of_buttons) / 2 - (button_width + button_spacing); | ||
| 357 | } else { | ||
| 358 | x = (data->dialog_width - width_of_buttons) / 2; | ||
| 359 | } | ||
| 360 | y = ybuttons + (data->dialog_height - ybuttons - button_height) / 2; | ||
| 361 | |||
| 362 | for (i = 0; i < data->numbuttons; i++) { | ||
| 363 | // Button coordinates. | ||
| 364 | data->buttonpos[i].rect.x = x; | ||
| 365 | data->buttonpos[i].rect.y = y; | ||
| 366 | data->buttonpos[i].rect.w = button_width; | ||
| 367 | data->buttonpos[i].rect.h = button_height; | ||
| 368 | |||
| 369 | // Button text coordinates. | ||
| 370 | data->buttonpos[i].x = x + (button_width - data->buttonpos[i].text_width) / 2; | ||
| 371 | data->buttonpos[i].y = y + (button_height - button_text_height - 1) / 2 + button_text_height; | ||
| 372 | |||
| 373 | // Scoot over for next button. | ||
| 374 | if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) { | ||
| 375 | x -= button_width + button_spacing; | ||
| 376 | } else { | ||
| 377 | x += button_width + button_spacing; | ||
| 378 | } | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | return true; | ||
| 383 | } | ||
| 384 | |||
| 385 | // Free SDL_MessageBoxData data. | ||
| 386 | static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data) | ||
| 387 | { | ||
| 388 | if (data->font_set) { | ||
| 389 | X11_XFreeFontSet(data->display, data->font_set); | ||
| 390 | data->font_set = NULL; | ||
| 391 | } | ||
| 392 | |||
| 393 | if (data->font_struct) { | ||
| 394 | X11_XFreeFont(data->display, data->font_struct); | ||
| 395 | data->font_struct = NULL; | ||
| 396 | } | ||
| 397 | |||
| 398 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 399 | if (SDL_X11_HAVE_XDBE && data->xdbe) { | ||
| 400 | X11_XdbeDeallocateBackBufferName(data->display, data->buf); | ||
| 401 | } | ||
| 402 | #endif | ||
| 403 | |||
| 404 | if (data->display) { | ||
| 405 | if (data->window != None) { | ||
| 406 | X11_XWithdrawWindow(data->display, data->window, data->screen); | ||
| 407 | X11_XDestroyWindow(data->display, data->window); | ||
| 408 | data->window = None; | ||
| 409 | } | ||
| 410 | |||
| 411 | X11_XCloseDisplay(data->display); | ||
| 412 | data->display = NULL; | ||
| 413 | } | ||
| 414 | |||
| 415 | SDL_free(data->linedata); | ||
| 416 | } | ||
| 417 | |||
| 418 | // Create and set up our X11 dialog box indow. | ||
| 419 | static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data) | ||
| 420 | { | ||
| 421 | int x, y; | ||
| 422 | XSizeHints *sizehints; | ||
| 423 | XSetWindowAttributes wnd_attr; | ||
| 424 | Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG; | ||
| 425 | Display *display = data->display; | ||
| 426 | SDL_WindowData *windowdata = NULL; | ||
| 427 | const SDL_MessageBoxData *messageboxdata = data->messageboxdata; | ||
| 428 | |||
| 429 | if (messageboxdata->window) { | ||
| 430 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window); | ||
| 431 | windowdata = messageboxdata->window->internal; | ||
| 432 | data->screen = displaydata->screen; | ||
| 433 | } else { | ||
| 434 | data->screen = DefaultScreen(display); | ||
| 435 | } | ||
| 436 | |||
| 437 | data->event_mask = ExposureMask | | ||
| 438 | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | | ||
| 439 | StructureNotifyMask | FocusChangeMask | PointerMotionMask; | ||
| 440 | wnd_attr.event_mask = data->event_mask; | ||
| 441 | |||
| 442 | data->window = X11_XCreateWindow( | ||
| 443 | display, RootWindow(display, data->screen), | ||
| 444 | 0, 0, | ||
| 445 | data->dialog_width, data->dialog_height, | ||
| 446 | 0, CopyFromParent, InputOutput, CopyFromParent, | ||
| 447 | CWEventMask, &wnd_attr); | ||
| 448 | if (data->window == None) { | ||
| 449 | return SDL_SetError("Couldn't create X window"); | ||
| 450 | } | ||
| 451 | |||
| 452 | if (windowdata) { | ||
| 453 | Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False); | ||
| 454 | Atom stateatoms[16]; | ||
| 455 | size_t statecount = 0; | ||
| 456 | // Set some message-boxy window states when attached to a parent window... | ||
| 457 | // we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc | ||
| 458 | stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False); | ||
| 459 | stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False); | ||
| 460 | stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False); | ||
| 461 | stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False); | ||
| 462 | SDL_assert(statecount <= SDL_arraysize(stateatoms)); | ||
| 463 | X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32, | ||
| 464 | PropModeReplace, (unsigned char *)stateatoms, statecount); | ||
| 465 | |||
| 466 | // http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR | ||
| 467 | X11_XSetTransientForHint(display, data->window, windowdata->xwindow); | ||
| 468 | } | ||
| 469 | |||
| 470 | SDL_X11_SetWindowTitle(display, data->window, (char *)messageboxdata->title); | ||
| 471 | |||
| 472 | // Let the window manager know this is a dialog box | ||
| 473 | _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | ||
| 474 | _NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False); | ||
| 475 | X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, | ||
| 476 | PropModeReplace, | ||
| 477 | (unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1); | ||
| 478 | |||
| 479 | // Allow the window to be deleted by the window manager | ||
| 480 | data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False); | ||
| 481 | X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1); | ||
| 482 | |||
| 483 | data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False); | ||
| 484 | |||
| 485 | if (windowdata) { | ||
| 486 | XWindowAttributes attrib; | ||
| 487 | Window dummy; | ||
| 488 | |||
| 489 | X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib); | ||
| 490 | x = attrib.x + (attrib.width - data->dialog_width) / 2; | ||
| 491 | y = attrib.y + (attrib.height - data->dialog_height) / 3; | ||
| 492 | X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy); | ||
| 493 | } else { | ||
| 494 | const SDL_VideoDevice *dev = SDL_GetVideoDevice(); | ||
| 495 | if (dev && dev->displays && dev->num_displays > 0) { | ||
| 496 | const SDL_VideoDisplay *dpy = dev->displays[0]; | ||
| 497 | const SDL_DisplayData *dpydata = dpy->internal; | ||
| 498 | x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2); | ||
| 499 | y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3); | ||
| 500 | } else { // oh well. This will misposition on a multi-head setup. Init first next time. | ||
| 501 | x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2; | ||
| 502 | y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | X11_XMoveWindow(display, data->window, x, y); | ||
| 506 | |||
| 507 | sizehints = X11_XAllocSizeHints(); | ||
| 508 | if (sizehints) { | ||
| 509 | sizehints->flags = USPosition | USSize | PMaxSize | PMinSize; | ||
| 510 | sizehints->x = x; | ||
| 511 | sizehints->y = y; | ||
| 512 | sizehints->width = data->dialog_width; | ||
| 513 | sizehints->height = data->dialog_height; | ||
| 514 | |||
| 515 | sizehints->min_width = sizehints->max_width = data->dialog_width; | ||
| 516 | sizehints->min_height = sizehints->max_height = data->dialog_height; | ||
| 517 | |||
| 518 | X11_XSetWMNormalHints(display, data->window, sizehints); | ||
| 519 | |||
| 520 | X11_XFree(sizehints); | ||
| 521 | } | ||
| 522 | |||
| 523 | X11_XMapRaised(display, data->window); | ||
| 524 | |||
| 525 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 526 | // Initialise a back buffer for double buffering | ||
| 527 | if (SDL_X11_HAVE_XDBE) { | ||
| 528 | int xdbe_major, xdbe_minor; | ||
| 529 | if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) { | ||
| 530 | data->xdbe = true; | ||
| 531 | data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined); | ||
| 532 | } else { | ||
| 533 | data->xdbe = false; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | #endif | ||
| 537 | |||
| 538 | return true; | ||
| 539 | } | ||
| 540 | |||
| 541 | // Draw our message box. | ||
| 542 | static void X11_MessageBoxDraw(SDL_MessageBoxDataX11 *data, GC ctx) | ||
| 543 | { | ||
| 544 | int i; | ||
| 545 | Drawable window = data->window; | ||
| 546 | Display *display = data->display; | ||
| 547 | |||
| 548 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 549 | if (SDL_X11_HAVE_XDBE && data->xdbe) { | ||
| 550 | window = data->buf; | ||
| 551 | X11_XdbeBeginIdiom(data->display); | ||
| 552 | } | ||
| 553 | #endif | ||
| 554 | |||
| 555 | X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND]); | ||
| 556 | X11_XFillRectangle(display, window, ctx, 0, 0, data->dialog_width, data->dialog_height); | ||
| 557 | |||
| 558 | X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_TEXT]); | ||
| 559 | for (i = 0; i < data->numlines; i++) { | ||
| 560 | TextLineData *plinedata = &data->linedata[i]; | ||
| 561 | |||
| 562 | #ifdef X_HAVE_UTF8_STRING | ||
| 563 | if (SDL_X11_HAVE_UTF8) { | ||
| 564 | X11_Xutf8DrawString(display, window, data->font_set, ctx, | ||
| 565 | data->xtext, data->ytext + i * data->text_height, | ||
| 566 | plinedata->text, plinedata->length); | ||
| 567 | } else | ||
| 568 | #endif | ||
| 569 | { | ||
| 570 | X11_XDrawString(display, window, ctx, | ||
| 571 | data->xtext, data->ytext + i * data->text_height, | ||
| 572 | plinedata->text, plinedata->length); | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | for (i = 0; i < data->numbuttons; i++) { | ||
| 577 | SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i]; | ||
| 578 | const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata; | ||
| 579 | int border = (buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) ? 2 : 0; | ||
| 580 | int offset = ((data->mouse_over_index == i) && (data->button_press_index == data->mouse_over_index)) ? 1 : 0; | ||
| 581 | |||
| 582 | X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND]); | ||
| 583 | X11_XFillRectangle(display, window, ctx, | ||
| 584 | buttondatax11->rect.x - border, buttondatax11->rect.y - border, | ||
| 585 | buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border); | ||
| 586 | |||
| 587 | X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER]); | ||
| 588 | X11_XDrawRectangle(display, window, ctx, | ||
| 589 | buttondatax11->rect.x, buttondatax11->rect.y, | ||
| 590 | buttondatax11->rect.w, buttondatax11->rect.h); | ||
| 591 | |||
| 592 | X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->color[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] : data->color[SDL_MESSAGEBOX_COLOR_TEXT]); | ||
| 593 | |||
| 594 | #ifdef X_HAVE_UTF8_STRING | ||
| 595 | if (SDL_X11_HAVE_UTF8) { | ||
| 596 | X11_Xutf8DrawString(display, window, data->font_set, ctx, | ||
| 597 | buttondatax11->x + offset, | ||
| 598 | buttondatax11->y + offset, | ||
| 599 | buttondata->text, buttondatax11->length); | ||
| 600 | } else | ||
| 601 | #endif | ||
| 602 | { | ||
| 603 | X11_XDrawString(display, window, ctx, | ||
| 604 | buttondatax11->x + offset, buttondatax11->y + offset, | ||
| 605 | buttondata->text, buttondatax11->length); | ||
| 606 | } | ||
| 607 | } | ||
| 608 | |||
| 609 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 610 | if (SDL_X11_HAVE_XDBE && data->xdbe) { | ||
| 611 | XdbeSwapInfo swap_info; | ||
| 612 | swap_info.swap_window = data->window; | ||
| 613 | swap_info.swap_action = XdbeUndefined; | ||
| 614 | X11_XdbeSwapBuffers(data->display, &swap_info, 1); | ||
| 615 | X11_XdbeEndIdiom(data->display); | ||
| 616 | } | ||
| 617 | #endif | ||
| 618 | } | ||
| 619 | |||
| 620 | // NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef | ||
| 621 | static Bool X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg) | ||
| 622 | { | ||
| 623 | const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *)arg; | ||
| 624 | return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False; | ||
| 625 | } | ||
| 626 | |||
| 627 | // Loop and handle message box event messages until something kills it. | ||
| 628 | static bool X11_MessageBoxLoop(SDL_MessageBoxDataX11 *data) | ||
| 629 | { | ||
| 630 | GC ctx; | ||
| 631 | XGCValues ctx_vals; | ||
| 632 | bool close_dialog = false; | ||
| 633 | bool has_focus = true; | ||
| 634 | KeySym last_key_pressed = XK_VoidSymbol; | ||
| 635 | unsigned long gcflags = GCForeground | GCBackground; | ||
| 636 | #ifdef X_HAVE_UTF8_STRING | ||
| 637 | const int have_utf8 = SDL_X11_HAVE_UTF8; | ||
| 638 | #else | ||
| 639 | const int have_utf8 = 0; | ||
| 640 | #endif | ||
| 641 | |||
| 642 | SDL_zero(ctx_vals); | ||
| 643 | ctx_vals.foreground = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND]; | ||
| 644 | ctx_vals.background = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND]; | ||
| 645 | |||
| 646 | if (!have_utf8) { | ||
| 647 | gcflags |= GCFont; | ||
| 648 | ctx_vals.font = data->font_struct->fid; | ||
| 649 | } | ||
| 650 | |||
| 651 | ctx = X11_XCreateGC(data->display, data->window, gcflags, &ctx_vals); | ||
| 652 | if (ctx == None) { | ||
| 653 | return SDL_SetError("Couldn't create graphics context"); | ||
| 654 | } | ||
| 655 | |||
| 656 | data->button_press_index = -1; // Reset what button is currently depressed. | ||
| 657 | data->mouse_over_index = -1; // Reset what button the mouse is over. | ||
| 658 | |||
| 659 | while (!close_dialog) { | ||
| 660 | XEvent e; | ||
| 661 | bool draw = true; | ||
| 662 | |||
| 663 | // can't use XWindowEvent() because it can't handle ClientMessage events. | ||
| 664 | // can't use XNextEvent() because we only want events for this window. | ||
| 665 | X11_XIfEvent(data->display, &e, X11_MessageBoxEventTest, (XPointer)data); | ||
| 666 | |||
| 667 | /* If X11_XFilterEvent returns True, then some input method has filtered the | ||
| 668 | event, and the client should discard the event. */ | ||
| 669 | if ((e.type != Expose) && X11_XFilterEvent(&e, None)) { | ||
| 670 | continue; | ||
| 671 | } | ||
| 672 | |||
| 673 | switch (e.type) { | ||
| 674 | case Expose: | ||
| 675 | if (e.xexpose.count > 0) { | ||
| 676 | draw = false; | ||
| 677 | } | ||
| 678 | break; | ||
| 679 | |||
| 680 | case FocusIn: | ||
| 681 | // Got focus. | ||
| 682 | has_focus = true; | ||
| 683 | break; | ||
| 684 | |||
| 685 | case FocusOut: | ||
| 686 | // lost focus. Reset button and mouse info. | ||
| 687 | has_focus = false; | ||
| 688 | data->button_press_index = -1; | ||
| 689 | data->mouse_over_index = -1; | ||
| 690 | break; | ||
| 691 | |||
| 692 | case MotionNotify: | ||
| 693 | if (has_focus) { | ||
| 694 | // Mouse moved... | ||
| 695 | const int previndex = data->mouse_over_index; | ||
| 696 | data->mouse_over_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); | ||
| 697 | if (data->mouse_over_index == previndex) { | ||
| 698 | draw = false; | ||
| 699 | } | ||
| 700 | } | ||
| 701 | break; | ||
| 702 | |||
| 703 | case ClientMessage: | ||
| 704 | if (e.xclient.message_type == data->wm_protocols && | ||
| 705 | e.xclient.format == 32 && | ||
| 706 | e.xclient.data.l[0] == data->wm_delete_message) { | ||
| 707 | close_dialog = true; | ||
| 708 | } | ||
| 709 | break; | ||
| 710 | |||
| 711 | case KeyPress: | ||
| 712 | // Store key press - we make sure in key release that we got both. | ||
| 713 | last_key_pressed = X11_XLookupKeysym(&e.xkey, 0); | ||
| 714 | break; | ||
| 715 | |||
| 716 | case KeyRelease: | ||
| 717 | { | ||
| 718 | Uint32 mask = 0; | ||
| 719 | KeySym key = X11_XLookupKeysym(&e.xkey, 0); | ||
| 720 | |||
| 721 | // If this is a key release for something we didn't get the key down for, then bail. | ||
| 722 | if (key != last_key_pressed) { | ||
| 723 | break; | ||
| 724 | } | ||
| 725 | |||
| 726 | if (key == XK_Escape) { | ||
| 727 | mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT; | ||
| 728 | } else if ((key == XK_Return) || (key == XK_KP_Enter)) { | ||
| 729 | mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT; | ||
| 730 | } | ||
| 731 | |||
| 732 | if (mask) { | ||
| 733 | int i; | ||
| 734 | |||
| 735 | // Look for first button with this mask set, and return it if found. | ||
| 736 | for (i = 0; i < data->numbuttons; i++) { | ||
| 737 | SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i]; | ||
| 738 | |||
| 739 | if (buttondatax11->buttondata->flags & mask) { | ||
| 740 | *data->pbuttonid = buttondatax11->buttondata->buttonID; | ||
| 741 | close_dialog = true; | ||
| 742 | break; | ||
| 743 | } | ||
| 744 | } | ||
| 745 | } | ||
| 746 | break; | ||
| 747 | } | ||
| 748 | |||
| 749 | case ButtonPress: | ||
| 750 | data->button_press_index = -1; | ||
| 751 | if (e.xbutton.button == Button1) { | ||
| 752 | // Find index of button they clicked on. | ||
| 753 | data->button_press_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); | ||
| 754 | } | ||
| 755 | break; | ||
| 756 | |||
| 757 | case ButtonRelease: | ||
| 758 | // If button is released over the same button that was clicked down on, then return it. | ||
| 759 | if ((e.xbutton.button == Button1) && (data->button_press_index >= 0)) { | ||
| 760 | int button = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y); | ||
| 761 | |||
| 762 | if (data->button_press_index == button) { | ||
| 763 | SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[button]; | ||
| 764 | |||
| 765 | *data->pbuttonid = buttondatax11->buttondata->buttonID; | ||
| 766 | close_dialog = true; | ||
| 767 | } | ||
| 768 | } | ||
| 769 | data->button_press_index = -1; | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | |||
| 773 | if (draw) { | ||
| 774 | // Draw our dialog box. | ||
| 775 | X11_MessageBoxDraw(data, ctx); | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | X11_XFreeGC(data->display, ctx); | ||
| 780 | return true; | ||
| 781 | } | ||
| 782 | |||
| 783 | static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID) | ||
| 784 | { | ||
| 785 | bool result = false; | ||
| 786 | SDL_MessageBoxDataX11 data; | ||
| 787 | #if SDL_SET_LOCALE | ||
| 788 | char *origlocale; | ||
| 789 | #endif | ||
| 790 | |||
| 791 | SDL_zero(data); | ||
| 792 | |||
| 793 | if (!SDL_X11_LoadSymbols()) { | ||
| 794 | return false; | ||
| 795 | } | ||
| 796 | |||
| 797 | #if SDL_SET_LOCALE | ||
| 798 | origlocale = setlocale(LC_ALL, NULL); | ||
| 799 | if (origlocale) { | ||
| 800 | origlocale = SDL_strdup(origlocale); | ||
| 801 | if (!origlocale) { | ||
| 802 | return false; | ||
| 803 | } | ||
| 804 | (void)setlocale(LC_ALL, ""); | ||
| 805 | } | ||
| 806 | #endif | ||
| 807 | |||
| 808 | // This code could get called from multiple threads maybe? | ||
| 809 | X11_XInitThreads(); | ||
| 810 | |||
| 811 | // Initialize the return buttonID value to -1 (for error or dialogbox closed). | ||
| 812 | *buttonID = -1; | ||
| 813 | |||
| 814 | // Init and display the message box. | ||
| 815 | if (X11_MessageBoxInit(&data, messageboxdata, buttonID) && | ||
| 816 | X11_MessageBoxInitPositions(&data) && | ||
| 817 | X11_MessageBoxCreateWindow(&data)) { | ||
| 818 | result = X11_MessageBoxLoop(&data); | ||
| 819 | } | ||
| 820 | |||
| 821 | X11_MessageBoxShutdown(&data); | ||
| 822 | |||
| 823 | #if SDL_SET_LOCALE | ||
| 824 | if (origlocale) { | ||
| 825 | (void)setlocale(LC_ALL, origlocale); | ||
| 826 | SDL_free(origlocale); | ||
| 827 | } | ||
| 828 | #endif | ||
| 829 | |||
| 830 | return result; | ||
| 831 | } | ||
| 832 | |||
| 833 | // Display an x11 message box. | ||
| 834 | bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID) | ||
| 835 | { | ||
| 836 | #if SDL_FORK_MESSAGEBOX | ||
| 837 | // Use a child process to protect against setlocale(). Annoying. | ||
| 838 | pid_t pid; | ||
| 839 | int fds[2]; | ||
| 840 | int status = 0; | ||
| 841 | bool result = true; | ||
| 842 | |||
| 843 | if (pipe(fds) == -1) { | ||
| 844 | return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well. | ||
| 845 | } | ||
| 846 | |||
| 847 | pid = fork(); | ||
| 848 | if (pid == -1) { // failed | ||
| 849 | close(fds[0]); | ||
| 850 | close(fds[1]); | ||
| 851 | return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well. | ||
| 852 | } else if (pid == 0) { // we're the child | ||
| 853 | int exitcode = 0; | ||
| 854 | close(fds[0]); | ||
| 855 | result = X11_ShowMessageBoxImpl(messageboxdata, buttonID); | ||
| 856 | if (write(fds[1], &result, sizeof(result)) != sizeof(result)) { | ||
| 857 | exitcode = 1; | ||
| 858 | } else if (write(fds[1], buttonID, sizeof(*buttonID)) != sizeof(*buttonID)) { | ||
| 859 | exitcode = 1; | ||
| 860 | } | ||
| 861 | close(fds[1]); | ||
| 862 | _exit(exitcode); // don't run atexit() stuff, static destructors, etc. | ||
| 863 | } else { // we're the parent | ||
| 864 | pid_t rc; | ||
| 865 | close(fds[1]); | ||
| 866 | do { | ||
| 867 | rc = waitpid(pid, &status, 0); | ||
| 868 | } while ((rc == -1) && (errno == EINTR)); | ||
| 869 | |||
| 870 | SDL_assert(rc == pid); // not sure what to do if this fails. | ||
| 871 | |||
| 872 | if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) { | ||
| 873 | result = SDL_SetError("msgbox child process failed"); | ||
| 874 | } else if ((read(fds[0], &result, sizeof(result)) != sizeof(result)) || | ||
| 875 | (read(fds[0], buttonID, sizeof(*buttonID)) != sizeof(*buttonID))) { | ||
| 876 | result = SDL_SetError("read from msgbox child process failed"); | ||
| 877 | *buttonID = 0; | ||
| 878 | } | ||
| 879 | close(fds[0]); | ||
| 880 | |||
| 881 | return result; | ||
| 882 | } | ||
| 883 | #else | ||
| 884 | return X11_ShowMessageBoxImpl(messageboxdata, buttonID); | ||
| 885 | #endif | ||
| 886 | } | ||
| 887 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h new file mode 100644 index 0000000..9baa875 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11messagebox.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #ifndef SDL_x11messagebox_h_ | ||
| 23 | #define SDL_x11messagebox_h_ | ||
| 24 | |||
| 25 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 26 | |||
| 27 | extern bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID); | ||
| 28 | |||
| 29 | #endif // SDL_VIDEO_DRIVER_X11 | ||
| 30 | |||
| 31 | #endif // SDL_x11messagebox_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c new file mode 100644 index 0000000..e17a2d1 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.c | |||
| @@ -0,0 +1,1051 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | #include "SDL_x11settings.h" | ||
| 27 | #include "edid.h" | ||
| 28 | #include "../../events/SDL_displayevents_c.h" | ||
| 29 | |||
| 30 | // #define X11MODES_DEBUG | ||
| 31 | |||
| 32 | /* Timeout and revert mode switches if the timespan has elapsed without the window becoming fullscreen. | ||
| 33 | * 5 seconds seems good from testing. | ||
| 34 | */ | ||
| 35 | #define MODE_SWITCH_TIMEOUT_NS SDL_NS_PER_SECOND * 5 | ||
| 36 | |||
| 37 | /* I'm becoming more and more convinced that the application should never | ||
| 38 | * use XRandR, and it's the window manager's responsibility to track and | ||
| 39 | * manage display modes for fullscreen windows. Right now XRandR is completely | ||
| 40 | * broken with respect to window manager behavior on every window manager that | ||
| 41 | * I can find. For example, on Unity 3D if you show a fullscreen window while | ||
| 42 | * the resolution is changing (within ~250 ms) your window will retain the | ||
| 43 | * fullscreen state hint but be decorated and windowed. | ||
| 44 | * | ||
| 45 | * However, many people swear by it, so let them swear at it. :) | ||
| 46 | */ | ||
| 47 | // #define XRANDR_DISABLED_BY_DEFAULT | ||
| 48 | |||
| 49 | static float GetGlobalContentScale(SDL_VideoDevice *_this) | ||
| 50 | { | ||
| 51 | static double scale_factor = 0.0; | ||
| 52 | |||
| 53 | if (scale_factor <= 0.0) { | ||
| 54 | |||
| 55 | // First use the forced scaling factor specified by the app/user | ||
| 56 | const char *hint = SDL_GetHint(SDL_HINT_VIDEO_X11_SCALING_FACTOR); | ||
| 57 | if (hint && *hint) { | ||
| 58 | double value = SDL_atof(hint); | ||
| 59 | if (value >= 1.0f && value <= 10.0f) { | ||
| 60 | scale_factor = value; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | // If that failed, try "Xft.dpi" from the XResourcesDatabase... | ||
| 65 | if (scale_factor <= 0.0) | ||
| 66 | { | ||
| 67 | SDL_VideoData *data = _this->internal; | ||
| 68 | Display *display = data->display; | ||
| 69 | char *resource_manager; | ||
| 70 | XrmDatabase db; | ||
| 71 | XrmValue value; | ||
| 72 | char *type; | ||
| 73 | |||
| 74 | X11_XrmInitialize(); | ||
| 75 | |||
| 76 | resource_manager = X11_XResourceManagerString(display); | ||
| 77 | if (resource_manager) { | ||
| 78 | db = X11_XrmGetStringDatabase(resource_manager); | ||
| 79 | |||
| 80 | // Get the value of Xft.dpi from the Database | ||
| 81 | if (X11_XrmGetResource(db, "Xft.dpi", "String", &type, &value)) { | ||
| 82 | if (value.addr && type && SDL_strcmp(type, "String") == 0) { | ||
| 83 | int dpi = SDL_atoi(value.addr); | ||
| 84 | scale_factor = dpi / 96.0; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | X11_XrmDestroyDatabase(db); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | // If that failed, try the XSETTINGS keys... | ||
| 92 | if (scale_factor <= 0.0) { | ||
| 93 | scale_factor = X11_GetXsettingsIntKey(_this, "Gdk/WindowScalingFactor", -1); | ||
| 94 | |||
| 95 | // The Xft/DPI key is stored in increments of 1024th | ||
| 96 | if (scale_factor <= 0.0) { | ||
| 97 | int dpi = X11_GetXsettingsIntKey(_this, "Xft/DPI", -1); | ||
| 98 | if (dpi > 0) { | ||
| 99 | scale_factor = (double) dpi / 1024.0; | ||
| 100 | scale_factor /= 96.0; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | // If that failed, try the GDK_SCALE envvar... | ||
| 106 | if (scale_factor <= 0.0) { | ||
| 107 | const char *scale_str = SDL_getenv("GDK_SCALE"); | ||
| 108 | if (scale_str) { | ||
| 109 | scale_factor = SDL_atoi(scale_str); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | // Nothing or a bad value, just fall back to 1.0 | ||
| 114 | if (scale_factor <= 0.0) { | ||
| 115 | scale_factor = 1.0; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | return (float)scale_factor; | ||
| 120 | } | ||
| 121 | |||
| 122 | static bool get_visualinfo(Display *display, int screen, XVisualInfo *vinfo) | ||
| 123 | { | ||
| 124 | const char *visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID); | ||
| 125 | int depth; | ||
| 126 | |||
| 127 | // Look for an exact visual, if requested | ||
| 128 | if (visual_id && *visual_id) { | ||
| 129 | XVisualInfo *vi, template; | ||
| 130 | int nvis; | ||
| 131 | |||
| 132 | SDL_zero(template); | ||
| 133 | template.visualid = SDL_strtol(visual_id, NULL, 0); | ||
| 134 | vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis); | ||
| 135 | if (vi) { | ||
| 136 | *vinfo = *vi; | ||
| 137 | X11_XFree(vi); | ||
| 138 | return true; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | depth = DefaultDepth(display, screen); | ||
| 143 | if ((X11_UseDirectColorVisuals() && | ||
| 144 | X11_XMatchVisualInfo(display, screen, depth, DirectColor, vinfo)) || | ||
| 145 | X11_XMatchVisualInfo(display, screen, depth, TrueColor, vinfo) || | ||
| 146 | X11_XMatchVisualInfo(display, screen, depth, PseudoColor, vinfo) || | ||
| 147 | X11_XMatchVisualInfo(display, screen, depth, StaticColor, vinfo)) { | ||
| 148 | return true; | ||
| 149 | } | ||
| 150 | return false; | ||
| 151 | } | ||
| 152 | |||
| 153 | bool X11_GetVisualInfoFromVisual(Display *display, Visual *visual, XVisualInfo *vinfo) | ||
| 154 | { | ||
| 155 | XVisualInfo *vi; | ||
| 156 | int nvis; | ||
| 157 | |||
| 158 | vinfo->visualid = X11_XVisualIDFromVisual(visual); | ||
| 159 | vi = X11_XGetVisualInfo(display, VisualIDMask, vinfo, &nvis); | ||
| 160 | if (vi) { | ||
| 161 | *vinfo = *vi; | ||
| 162 | X11_XFree(vi); | ||
| 163 | return true; | ||
| 164 | } | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | |||
| 168 | SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *vinfo) | ||
| 169 | { | ||
| 170 | if (vinfo->class == DirectColor || vinfo->class == TrueColor) { | ||
| 171 | int bpp; | ||
| 172 | Uint32 Rmask, Gmask, Bmask, Amask; | ||
| 173 | |||
| 174 | Rmask = vinfo->visual->red_mask; | ||
| 175 | Gmask = vinfo->visual->green_mask; | ||
| 176 | Bmask = vinfo->visual->blue_mask; | ||
| 177 | if (vinfo->depth == 32) { | ||
| 178 | Amask = (0xFFFFFFFF & ~(Rmask | Gmask | Bmask)); | ||
| 179 | } else { | ||
| 180 | Amask = 0; | ||
| 181 | } | ||
| 182 | |||
| 183 | bpp = vinfo->depth; | ||
| 184 | if (bpp == 24) { | ||
| 185 | int i, n; | ||
| 186 | XPixmapFormatValues *p = X11_XListPixmapFormats(display, &n); | ||
| 187 | if (p) { | ||
| 188 | for (i = 0; i < n; ++i) { | ||
| 189 | if (p[i].depth == 24) { | ||
| 190 | bpp = p[i].bits_per_pixel; | ||
| 191 | break; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | X11_XFree(p); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | return SDL_GetPixelFormatForMasks(bpp, Rmask, Gmask, Bmask, Amask); | ||
| 199 | } | ||
| 200 | |||
| 201 | if (vinfo->class == PseudoColor || vinfo->class == StaticColor) { | ||
| 202 | switch (vinfo->depth) { | ||
| 203 | case 8: | ||
| 204 | return SDL_PIXELFORMAT_INDEX8; | ||
| 205 | case 4: | ||
| 206 | if (BitmapBitOrder(display) == LSBFirst) { | ||
| 207 | return SDL_PIXELFORMAT_INDEX4LSB; | ||
| 208 | } else { | ||
| 209 | return SDL_PIXELFORMAT_INDEX4MSB; | ||
| 210 | } | ||
| 211 | // break; -Wunreachable-code-break | ||
| 212 | case 1: | ||
| 213 | if (BitmapBitOrder(display) == LSBFirst) { | ||
| 214 | return SDL_PIXELFORMAT_INDEX1LSB; | ||
| 215 | } else { | ||
| 216 | return SDL_PIXELFORMAT_INDEX1MSB; | ||
| 217 | } | ||
| 218 | // break; -Wunreachable-code-break | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | return SDL_PIXELFORMAT_UNKNOWN; | ||
| 223 | } | ||
| 224 | |||
| 225 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 226 | static bool CheckXRandR(Display *display, int *major, int *minor) | ||
| 227 | { | ||
| 228 | // Default the extension not available | ||
| 229 | *major = *minor = 0; | ||
| 230 | |||
| 231 | // Allow environment override | ||
| 232 | #ifdef XRANDR_DISABLED_BY_DEFAULT | ||
| 233 | if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, false)) { | ||
| 234 | #ifdef X11MODES_DEBUG | ||
| 235 | printf("XRandR disabled by default due to window manager issues\n"); | ||
| 236 | #endif | ||
| 237 | return false; | ||
| 238 | } | ||
| 239 | #else | ||
| 240 | if (!SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_XRANDR, true)) { | ||
| 241 | #ifdef X11MODES_DEBUG | ||
| 242 | printf("XRandR disabled due to hint\n"); | ||
| 243 | #endif | ||
| 244 | return false; | ||
| 245 | } | ||
| 246 | #endif // XRANDR_DISABLED_BY_DEFAULT | ||
| 247 | |||
| 248 | if (!SDL_X11_HAVE_XRANDR) { | ||
| 249 | #ifdef X11MODES_DEBUG | ||
| 250 | printf("XRandR support not available\n"); | ||
| 251 | #endif | ||
| 252 | return false; | ||
| 253 | } | ||
| 254 | |||
| 255 | // Query the extension version | ||
| 256 | *major = 1; | ||
| 257 | *minor = 3; // we want 1.3 | ||
| 258 | if (!X11_XRRQueryVersion(display, major, minor)) { | ||
| 259 | #ifdef X11MODES_DEBUG | ||
| 260 | printf("XRandR not active on the display\n"); | ||
| 261 | #endif | ||
| 262 | *major = *minor = 0; | ||
| 263 | return false; | ||
| 264 | } | ||
| 265 | #ifdef X11MODES_DEBUG | ||
| 266 | printf("XRandR available at version %d.%d!\n", *major, *minor); | ||
| 267 | #endif | ||
| 268 | return true; | ||
| 269 | } | ||
| 270 | |||
| 271 | #define XRANDR_ROTATION_LEFT (1 << 1) | ||
| 272 | #define XRANDR_ROTATION_RIGHT (1 << 3) | ||
| 273 | |||
| 274 | static void CalculateXRandRRefreshRate(const XRRModeInfo *info, int *numerator, int *denominator) | ||
| 275 | { | ||
| 276 | unsigned int vTotal = info->vTotal; | ||
| 277 | |||
| 278 | if (info->modeFlags & RR_DoubleScan) { | ||
| 279 | // doublescan doubles the number of lines | ||
| 280 | vTotal *= 2; | ||
| 281 | } | ||
| 282 | |||
| 283 | if (info->modeFlags & RR_Interlace) { | ||
| 284 | // interlace splits the frame into two fields | ||
| 285 | // the field rate is what is typically reported by monitors | ||
| 286 | vTotal /= 2; | ||
| 287 | } | ||
| 288 | |||
| 289 | if (info->hTotal && vTotal) { | ||
| 290 | *numerator = info->dotClock; | ||
| 291 | *denominator = (info->hTotal * vTotal); | ||
| 292 | } else { | ||
| 293 | *numerator = 0; | ||
| 294 | *denominator = 0; | ||
| 295 | } | ||
| 296 | } | ||
| 297 | |||
| 298 | static bool SetXRandRModeInfo(Display *display, XRRScreenResources *res, RRCrtc crtc, | ||
| 299 | RRMode modeID, SDL_DisplayMode *mode) | ||
| 300 | { | ||
| 301 | int i; | ||
| 302 | for (i = 0; i < res->nmode; ++i) { | ||
| 303 | const XRRModeInfo *info = &res->modes[i]; | ||
| 304 | if (info->id == modeID) { | ||
| 305 | XRRCrtcInfo *crtcinfo; | ||
| 306 | Rotation rotation = 0; | ||
| 307 | XFixed scale_w = 0x10000, scale_h = 0x10000; | ||
| 308 | XRRCrtcTransformAttributes *attr; | ||
| 309 | |||
| 310 | crtcinfo = X11_XRRGetCrtcInfo(display, res, crtc); | ||
| 311 | if (crtcinfo) { | ||
| 312 | rotation = crtcinfo->rotation; | ||
| 313 | X11_XRRFreeCrtcInfo(crtcinfo); | ||
| 314 | } | ||
| 315 | if (X11_XRRGetCrtcTransform(display, crtc, &attr) && attr) { | ||
| 316 | scale_w = attr->currentTransform.matrix[0][0]; | ||
| 317 | scale_h = attr->currentTransform.matrix[1][1]; | ||
| 318 | X11_XFree(attr); | ||
| 319 | } | ||
| 320 | |||
| 321 | if (rotation & (XRANDR_ROTATION_LEFT | XRANDR_ROTATION_RIGHT)) { | ||
| 322 | mode->w = (info->height * scale_w + 0xffff) >> 16; | ||
| 323 | mode->h = (info->width * scale_h + 0xffff) >> 16; | ||
| 324 | } else { | ||
| 325 | mode->w = (info->width * scale_w + 0xffff) >> 16; | ||
| 326 | mode->h = (info->height * scale_h + 0xffff) >> 16; | ||
| 327 | } | ||
| 328 | CalculateXRandRRefreshRate(info, &mode->refresh_rate_numerator, &mode->refresh_rate_denominator); | ||
| 329 | mode->internal->xrandr_mode = modeID; | ||
| 330 | #ifdef X11MODES_DEBUG | ||
| 331 | printf("XRandR mode %d: %dx%d@%d/%dHz\n", (int)modeID, | ||
| 332 | mode->screen_w, mode->screen_h, mode->refresh_rate_numerator, mode->refresh_rate_denominator); | ||
| 333 | #endif | ||
| 334 | return true; | ||
| 335 | } | ||
| 336 | } | ||
| 337 | return false; | ||
| 338 | } | ||
| 339 | |||
| 340 | static void SetXRandRDisplayName(Display *dpy, Atom EDID, char *name, const size_t namelen, RROutput output, const unsigned long widthmm, const unsigned long heightmm) | ||
| 341 | { | ||
| 342 | // See if we can get the EDID data for the real monitor name | ||
| 343 | int inches; | ||
| 344 | int nprop; | ||
| 345 | Atom *props = X11_XRRListOutputProperties(dpy, output, &nprop); | ||
| 346 | int i; | ||
| 347 | |||
| 348 | for (i = 0; i < nprop; ++i) { | ||
| 349 | unsigned char *prop; | ||
| 350 | int actual_format; | ||
| 351 | unsigned long nitems, bytes_after; | ||
| 352 | Atom actual_type; | ||
| 353 | |||
| 354 | if (props[i] == EDID) { | ||
| 355 | if (X11_XRRGetOutputProperty(dpy, output, props[i], 0, 100, False, | ||
| 356 | False, AnyPropertyType, &actual_type, | ||
| 357 | &actual_format, &nitems, &bytes_after, | ||
| 358 | &prop) == Success) { | ||
| 359 | MonitorInfo *info = decode_edid(prop); | ||
| 360 | if (info) { | ||
| 361 | #ifdef X11MODES_DEBUG | ||
| 362 | printf("Found EDID data for %s\n", name); | ||
| 363 | dump_monitor_info(info); | ||
| 364 | #endif | ||
| 365 | SDL_strlcpy(name, info->dsc_product_name, namelen); | ||
| 366 | SDL_free(info); | ||
| 367 | } | ||
| 368 | X11_XFree(prop); | ||
| 369 | } | ||
| 370 | break; | ||
| 371 | } | ||
| 372 | } | ||
| 373 | |||
| 374 | if (props) { | ||
| 375 | X11_XFree(props); | ||
| 376 | } | ||
| 377 | |||
| 378 | inches = (int)((SDL_sqrtf(widthmm * widthmm + heightmm * heightmm) / 25.4f) + 0.5f); | ||
| 379 | if (*name && inches) { | ||
| 380 | const size_t len = SDL_strlen(name); | ||
| 381 | (void)SDL_snprintf(&name[len], namelen - len, " %d\"", inches); | ||
| 382 | } | ||
| 383 | |||
| 384 | #ifdef X11MODES_DEBUG | ||
| 385 | printf("Display name: %s\n", name); | ||
| 386 | #endif | ||
| 387 | } | ||
| 388 | |||
| 389 | static bool X11_FillXRandRDisplayInfo(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, SDL_VideoDisplay *display, char *display_name, size_t display_name_size) | ||
| 390 | { | ||
| 391 | Atom EDID = X11_XInternAtom(dpy, "EDID", False); | ||
| 392 | XRROutputInfo *output_info; | ||
| 393 | int display_x, display_y; | ||
| 394 | unsigned long display_mm_width, display_mm_height; | ||
| 395 | SDL_DisplayData *displaydata; | ||
| 396 | SDL_DisplayMode mode; | ||
| 397 | SDL_DisplayModeData *modedata; | ||
| 398 | RRMode modeID; | ||
| 399 | RRCrtc output_crtc; | ||
| 400 | XRRCrtcInfo *crtc; | ||
| 401 | XVisualInfo vinfo; | ||
| 402 | Uint32 pixelformat; | ||
| 403 | XPixmapFormatValues *pixmapformats; | ||
| 404 | int scanline_pad; | ||
| 405 | int i, n; | ||
| 406 | |||
| 407 | if (!display || !display_name) { | ||
| 408 | return false; // invalid parameters | ||
| 409 | } | ||
| 410 | |||
| 411 | if (!get_visualinfo(dpy, screen, &vinfo)) { | ||
| 412 | return false; // uh, skip this screen? | ||
| 413 | } | ||
| 414 | |||
| 415 | pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo); | ||
| 416 | if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) { | ||
| 417 | return false; // Palettized video modes are no longer supported, ignore this one. | ||
| 418 | } | ||
| 419 | |||
| 420 | scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8; | ||
| 421 | pixmapformats = X11_XListPixmapFormats(dpy, &n); | ||
| 422 | if (pixmapformats) { | ||
| 423 | for (i = 0; i < n; i++) { | ||
| 424 | if (pixmapformats[i].depth == vinfo.depth) { | ||
| 425 | scanline_pad = pixmapformats[i].scanline_pad; | ||
| 426 | break; | ||
| 427 | } | ||
| 428 | } | ||
| 429 | X11_XFree(pixmapformats); | ||
| 430 | } | ||
| 431 | |||
| 432 | output_info = X11_XRRGetOutputInfo(dpy, res, outputid); | ||
| 433 | if (!output_info || !output_info->crtc || output_info->connection == RR_Disconnected) { | ||
| 434 | X11_XRRFreeOutputInfo(output_info); | ||
| 435 | return false; // ignore this one. | ||
| 436 | } | ||
| 437 | |||
| 438 | SDL_strlcpy(display_name, output_info->name, display_name_size); | ||
| 439 | display_mm_width = output_info->mm_width; | ||
| 440 | display_mm_height = output_info->mm_height; | ||
| 441 | output_crtc = output_info->crtc; | ||
| 442 | X11_XRRFreeOutputInfo(output_info); | ||
| 443 | |||
| 444 | crtc = X11_XRRGetCrtcInfo(dpy, res, output_crtc); | ||
| 445 | if (!crtc) { | ||
| 446 | return false; // oh well, ignore it. | ||
| 447 | } | ||
| 448 | |||
| 449 | SDL_zero(mode); | ||
| 450 | modeID = crtc->mode; | ||
| 451 | mode.w = crtc->width; | ||
| 452 | mode.h = crtc->height; | ||
| 453 | mode.format = pixelformat; | ||
| 454 | |||
| 455 | display_x = crtc->x; | ||
| 456 | display_y = crtc->y; | ||
| 457 | |||
| 458 | X11_XRRFreeCrtcInfo(crtc); | ||
| 459 | |||
| 460 | displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); | ||
| 461 | if (!displaydata) { | ||
| 462 | return false; | ||
| 463 | } | ||
| 464 | |||
| 465 | modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData)); | ||
| 466 | if (!modedata) { | ||
| 467 | SDL_free(displaydata); | ||
| 468 | return false; | ||
| 469 | } | ||
| 470 | |||
| 471 | modedata->xrandr_mode = modeID; | ||
| 472 | mode.internal = modedata; | ||
| 473 | |||
| 474 | displaydata->screen = screen; | ||
| 475 | displaydata->visual = vinfo.visual; | ||
| 476 | displaydata->depth = vinfo.depth; | ||
| 477 | displaydata->scanline_pad = scanline_pad; | ||
| 478 | displaydata->x = display_x; | ||
| 479 | displaydata->y = display_y; | ||
| 480 | displaydata->use_xrandr = true; | ||
| 481 | displaydata->xrandr_output = outputid; | ||
| 482 | SDL_strlcpy(displaydata->connector_name, display_name, sizeof(displaydata->connector_name)); | ||
| 483 | |||
| 484 | SetXRandRModeInfo(dpy, res, output_crtc, modeID, &mode); | ||
| 485 | SetXRandRDisplayName(dpy, EDID, display_name, display_name_size, outputid, display_mm_width, display_mm_height); | ||
| 486 | |||
| 487 | SDL_zero(*display); | ||
| 488 | if (*display_name) { | ||
| 489 | display->name = display_name; | ||
| 490 | } | ||
| 491 | display->desktop_mode = mode; | ||
| 492 | display->content_scale = GetGlobalContentScale(_this); | ||
| 493 | display->internal = displaydata; | ||
| 494 | |||
| 495 | return true; | ||
| 496 | } | ||
| 497 | |||
| 498 | static bool X11_AddXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, bool send_event) | ||
| 499 | { | ||
| 500 | SDL_VideoDisplay display; | ||
| 501 | char display_name[128]; | ||
| 502 | |||
| 503 | if (!X11_FillXRandRDisplayInfo(_this, dpy, screen, outputid, res, &display, display_name, sizeof(display_name))) { | ||
| 504 | return true; // failed to query data, skip this display | ||
| 505 | } | ||
| 506 | |||
| 507 | if (SDL_AddVideoDisplay(&display, send_event) == 0) { | ||
| 508 | return false; | ||
| 509 | } | ||
| 510 | |||
| 511 | return true; | ||
| 512 | } | ||
| 513 | |||
| 514 | |||
| 515 | static bool X11_UpdateXRandRDisplay(SDL_VideoDevice *_this, Display *dpy, int screen, RROutput outputid, XRRScreenResources *res, SDL_VideoDisplay *existing_display) | ||
| 516 | { | ||
| 517 | SDL_VideoDisplay display; | ||
| 518 | char display_name[128]; | ||
| 519 | |||
| 520 | if (!X11_FillXRandRDisplayInfo(_this, dpy, screen, outputid, res, &display, display_name, sizeof(display_name))) { | ||
| 521 | return false; // failed to query current display state | ||
| 522 | } | ||
| 523 | |||
| 524 | // update mode - this call takes ownership of display.desktop_mode.internal | ||
| 525 | SDL_SetDesktopDisplayMode(existing_display, &display.desktop_mode); | ||
| 526 | |||
| 527 | // update bounds | ||
| 528 | if (existing_display->internal->x != display.internal->x || | ||
| 529 | existing_display->internal->y != display.internal->y) { | ||
| 530 | existing_display->internal->x = display.internal->x; | ||
| 531 | existing_display->internal->y = display.internal->y; | ||
| 532 | SDL_SendDisplayEvent(existing_display, SDL_EVENT_DISPLAY_MOVED, 0, 0); | ||
| 533 | } | ||
| 534 | |||
| 535 | // update scale | ||
| 536 | SDL_SetDisplayContentScale(existing_display, display.content_scale); | ||
| 537 | |||
| 538 | // SDL_DisplayData is updated piece-meal above, free our local copy of this data | ||
| 539 | SDL_free( display.internal ); | ||
| 540 | |||
| 541 | return true; | ||
| 542 | } | ||
| 543 | |||
| 544 | static XRRScreenResources *X11_GetScreenResources(Display *dpy, int screen) | ||
| 545 | { | ||
| 546 | XRRScreenResources *res = X11_XRRGetScreenResourcesCurrent(dpy, RootWindow(dpy, screen)); | ||
| 547 | if (!res || res->noutput == 0) { | ||
| 548 | if (res) { | ||
| 549 | X11_XRRFreeScreenResources(res); | ||
| 550 | } | ||
| 551 | res = X11_XRRGetScreenResources(dpy, RootWindow(dpy, screen)); | ||
| 552 | } | ||
| 553 | return res; | ||
| 554 | } | ||
| 555 | |||
| 556 | static void X11_CheckDisplaysMoved(SDL_VideoDevice *_this, Display *dpy) | ||
| 557 | { | ||
| 558 | const int screen = DefaultScreen(dpy); | ||
| 559 | XRRScreenResources *res = X11_GetScreenResources(dpy, screen); | ||
| 560 | if (!res) { | ||
| 561 | return; | ||
| 562 | } | ||
| 563 | |||
| 564 | SDL_DisplayID *displays = SDL_GetDisplays(NULL); | ||
| 565 | if (displays) { | ||
| 566 | for (int i = 0; displays[i]; ++i) { | ||
| 567 | SDL_VideoDisplay *display = SDL_GetVideoDisplay(displays[i]); | ||
| 568 | const SDL_DisplayData *displaydata = display->internal; | ||
| 569 | X11_UpdateXRandRDisplay(_this, dpy, screen, displaydata->xrandr_output, res, display); | ||
| 570 | } | ||
| 571 | SDL_free(displays); | ||
| 572 | } | ||
| 573 | X11_XRRFreeScreenResources(res); | ||
| 574 | } | ||
| 575 | |||
| 576 | static void X11_HandleXRandROutputChange(SDL_VideoDevice *_this, const XRROutputChangeNotifyEvent *ev) | ||
| 577 | { | ||
| 578 | SDL_DisplayID *displays; | ||
| 579 | SDL_VideoDisplay *display = NULL; | ||
| 580 | int i; | ||
| 581 | |||
| 582 | #if 0 | ||
| 583 | printf("XRROutputChangeNotifyEvent! [output=%u, crtc=%u, mode=%u, rotation=%u, connection=%u]", (unsigned int) ev->output, (unsigned int) ev->crtc, (unsigned int) ev->mode, (unsigned int) ev->rotation, (unsigned int) ev->connection); | ||
| 584 | #endif | ||
| 585 | |||
| 586 | displays = SDL_GetDisplays(NULL); | ||
| 587 | if (displays) { | ||
| 588 | for (i = 0; displays[i]; ++i) { | ||
| 589 | SDL_VideoDisplay *thisdisplay = SDL_GetVideoDisplay(displays[i]); | ||
| 590 | const SDL_DisplayData *displaydata = thisdisplay->internal; | ||
| 591 | if (displaydata->xrandr_output == ev->output) { | ||
| 592 | display = thisdisplay; | ||
| 593 | break; | ||
| 594 | } | ||
| 595 | } | ||
| 596 | SDL_free(displays); | ||
| 597 | } | ||
| 598 | |||
| 599 | if (ev->connection == RR_Disconnected) { // output is going away | ||
| 600 | if (display) { | ||
| 601 | SDL_DelVideoDisplay(display->id, true); | ||
| 602 | } | ||
| 603 | X11_CheckDisplaysMoved(_this, ev->display); | ||
| 604 | |||
| 605 | } else if (ev->connection == RR_Connected) { // output is coming online | ||
| 606 | if (!display) { | ||
| 607 | Display *dpy = ev->display; | ||
| 608 | const int screen = DefaultScreen(dpy); | ||
| 609 | XRRScreenResources *res = X11_GetScreenResources(dpy, screen); | ||
| 610 | if (res) { | ||
| 611 | X11_AddXRandRDisplay(_this, dpy, screen, ev->output, res, true); | ||
| 612 | X11_XRRFreeScreenResources(res); | ||
| 613 | } | ||
| 614 | } | ||
| 615 | X11_CheckDisplaysMoved(_this, ev->display); | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 619 | void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent) | ||
| 620 | { | ||
| 621 | SDL_VideoData *videodata = _this->internal; | ||
| 622 | SDL_assert(xevent->type == (videodata->xrandr_event_base + RRNotify)); | ||
| 623 | |||
| 624 | switch (((const XRRNotifyEvent *)xevent)->subtype) { | ||
| 625 | case RRNotify_OutputChange: | ||
| 626 | X11_HandleXRandROutputChange(_this, (const XRROutputChangeNotifyEvent *)xevent); | ||
| 627 | break; | ||
| 628 | default: | ||
| 629 | break; | ||
| 630 | } | ||
| 631 | } | ||
| 632 | |||
| 633 | static void X11_SortOutputsByPriorityHint(SDL_VideoDevice *_this) | ||
| 634 | { | ||
| 635 | const char *name_hint = SDL_GetHint(SDL_HINT_VIDEO_DISPLAY_PRIORITY); | ||
| 636 | |||
| 637 | if (name_hint) { | ||
| 638 | char *saveptr; | ||
| 639 | char *str = SDL_strdup(name_hint); | ||
| 640 | SDL_VideoDisplay **sorted_list = SDL_malloc(sizeof(SDL_VideoDisplay *) * _this->num_displays); | ||
| 641 | |||
| 642 | if (str && sorted_list) { | ||
| 643 | int sorted_index = 0; | ||
| 644 | |||
| 645 | // Sort the requested displays to the front of the list. | ||
| 646 | const char *token = SDL_strtok_r(str, ",", &saveptr); | ||
| 647 | while (token) { | ||
| 648 | for (int i = 0; i < _this->num_displays; ++i) { | ||
| 649 | SDL_VideoDisplay *d = _this->displays[i]; | ||
| 650 | if (d) { | ||
| 651 | SDL_DisplayData *data = d->internal; | ||
| 652 | if (SDL_strcmp(token, data->connector_name) == 0) { | ||
| 653 | sorted_list[sorted_index++] = d; | ||
| 654 | _this->displays[i] = NULL; | ||
| 655 | break; | ||
| 656 | } | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | token = SDL_strtok_r(NULL, ",", &saveptr); | ||
| 661 | } | ||
| 662 | |||
| 663 | // Append the remaining displays to the end of the list. | ||
| 664 | for (int i = 0; i < _this->num_displays; ++i) { | ||
| 665 | if (_this->displays[i]) { | ||
| 666 | sorted_list[sorted_index++] = _this->displays[i]; | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | // Copy the sorted list back to the display list. | ||
| 671 | SDL_memcpy(_this->displays, sorted_list, sizeof(SDL_VideoDisplay *) * _this->num_displays); | ||
| 672 | } | ||
| 673 | |||
| 674 | SDL_free(str); | ||
| 675 | SDL_free(sorted_list); | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | static bool X11_InitModes_XRandR(SDL_VideoDevice *_this) | ||
| 680 | { | ||
| 681 | SDL_VideoData *data = _this->internal; | ||
| 682 | Display *dpy = data->display; | ||
| 683 | const int screencount = ScreenCount(dpy); | ||
| 684 | const int default_screen = DefaultScreen(dpy); | ||
| 685 | RROutput primary = X11_XRRGetOutputPrimary(dpy, RootWindow(dpy, default_screen)); | ||
| 686 | int xrandr_error_base = 0; | ||
| 687 | int looking_for_primary; | ||
| 688 | int output; | ||
| 689 | int screen; | ||
| 690 | |||
| 691 | if (!X11_XRRQueryExtension(dpy, &data->xrandr_event_base, &xrandr_error_base)) { | ||
| 692 | return SDL_SetError("XRRQueryExtension failed"); | ||
| 693 | } | ||
| 694 | |||
| 695 | for (looking_for_primary = 1; looking_for_primary >= 0; looking_for_primary--) { | ||
| 696 | for (screen = 0; screen < screencount; screen++) { | ||
| 697 | |||
| 698 | // we want the primary output first, and then skipped later. | ||
| 699 | if (looking_for_primary && (screen != default_screen)) { | ||
| 700 | continue; | ||
| 701 | } | ||
| 702 | |||
| 703 | XRRScreenResources *res = X11_GetScreenResources(dpy, screen); | ||
| 704 | if (!res) { | ||
| 705 | continue; | ||
| 706 | } | ||
| 707 | |||
| 708 | for (output = 0; output < res->noutput; output++) { | ||
| 709 | // The primary output _should_ always be sorted first, but just in case... | ||
| 710 | if ((looking_for_primary && (res->outputs[output] != primary)) || | ||
| 711 | (!looking_for_primary && (screen == default_screen) && (res->outputs[output] == primary))) { | ||
| 712 | continue; | ||
| 713 | } | ||
| 714 | if (!X11_AddXRandRDisplay(_this, dpy, screen, res->outputs[output], res, false)) { | ||
| 715 | break; | ||
| 716 | } | ||
| 717 | } | ||
| 718 | |||
| 719 | X11_XRRFreeScreenResources(res); | ||
| 720 | |||
| 721 | // This will generate events for displays that come and go at runtime. | ||
| 722 | X11_XRRSelectInput(dpy, RootWindow(dpy, screen), RROutputChangeNotifyMask); | ||
| 723 | } | ||
| 724 | } | ||
| 725 | |||
| 726 | if (_this->num_displays == 0) { | ||
| 727 | return SDL_SetError("No available displays"); | ||
| 728 | } | ||
| 729 | |||
| 730 | X11_SortOutputsByPriorityHint(_this); | ||
| 731 | |||
| 732 | return true; | ||
| 733 | } | ||
| 734 | #endif // SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 735 | |||
| 736 | /* This is used if there's no better functionality--like XRandR--to use. | ||
| 737 | It won't attempt to supply different display modes at all, but it can | ||
| 738 | enumerate the current displays and their current sizes. */ | ||
| 739 | static bool X11_InitModes_StdXlib(SDL_VideoDevice *_this) | ||
| 740 | { | ||
| 741 | // !!! FIXME: a lot of copy/paste from X11_InitModes_XRandR in this function. | ||
| 742 | SDL_VideoData *data = _this->internal; | ||
| 743 | Display *dpy = data->display; | ||
| 744 | const int default_screen = DefaultScreen(dpy); | ||
| 745 | Screen *screen = ScreenOfDisplay(dpy, default_screen); | ||
| 746 | int scanline_pad, n, i; | ||
| 747 | SDL_DisplayModeData *modedata; | ||
| 748 | SDL_DisplayData *displaydata; | ||
| 749 | SDL_DisplayMode mode; | ||
| 750 | XPixmapFormatValues *pixmapformats; | ||
| 751 | Uint32 pixelformat; | ||
| 752 | XVisualInfo vinfo; | ||
| 753 | SDL_VideoDisplay display; | ||
| 754 | |||
| 755 | // note that generally even if you have a multiple physical monitors, ScreenCount(dpy) still only reports ONE screen. | ||
| 756 | |||
| 757 | if (!get_visualinfo(dpy, default_screen, &vinfo)) { | ||
| 758 | return SDL_SetError("Failed to find an X11 visual for the primary display"); | ||
| 759 | } | ||
| 760 | |||
| 761 | pixelformat = X11_GetPixelFormatFromVisualInfo(dpy, &vinfo); | ||
| 762 | if (SDL_ISPIXELFORMAT_INDEXED(pixelformat)) { | ||
| 763 | return SDL_SetError("Palettized video modes are no longer supported"); | ||
| 764 | } | ||
| 765 | |||
| 766 | SDL_zero(mode); | ||
| 767 | mode.w = WidthOfScreen(screen); | ||
| 768 | mode.h = HeightOfScreen(screen); | ||
| 769 | mode.format = pixelformat; | ||
| 770 | |||
| 771 | displaydata = (SDL_DisplayData *)SDL_calloc(1, sizeof(*displaydata)); | ||
| 772 | if (!displaydata) { | ||
| 773 | return false; | ||
| 774 | } | ||
| 775 | |||
| 776 | modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData)); | ||
| 777 | if (!modedata) { | ||
| 778 | SDL_free(displaydata); | ||
| 779 | return false; | ||
| 780 | } | ||
| 781 | mode.internal = modedata; | ||
| 782 | |||
| 783 | displaydata->screen = default_screen; | ||
| 784 | displaydata->visual = vinfo.visual; | ||
| 785 | displaydata->depth = vinfo.depth; | ||
| 786 | |||
| 787 | scanline_pad = SDL_BYTESPERPIXEL(pixelformat) * 8; | ||
| 788 | pixmapformats = X11_XListPixmapFormats(dpy, &n); | ||
| 789 | if (pixmapformats) { | ||
| 790 | for (i = 0; i < n; ++i) { | ||
| 791 | if (pixmapformats[i].depth == vinfo.depth) { | ||
| 792 | scanline_pad = pixmapformats[i].scanline_pad; | ||
| 793 | break; | ||
| 794 | } | ||
| 795 | } | ||
| 796 | X11_XFree(pixmapformats); | ||
| 797 | } | ||
| 798 | |||
| 799 | displaydata->scanline_pad = scanline_pad; | ||
| 800 | displaydata->x = 0; | ||
| 801 | displaydata->y = 0; | ||
| 802 | displaydata->use_xrandr = false; | ||
| 803 | |||
| 804 | SDL_zero(display); | ||
| 805 | display.name = (char *)"Generic X11 Display"; /* this is just copied and thrown away, it's safe to cast to char* here. */ | ||
| 806 | display.desktop_mode = mode; | ||
| 807 | display.internal = displaydata; | ||
| 808 | display.content_scale = GetGlobalContentScale(_this); | ||
| 809 | if (SDL_AddVideoDisplay(&display, true) == 0) { | ||
| 810 | return false; | ||
| 811 | } | ||
| 812 | return true; | ||
| 813 | } | ||
| 814 | |||
| 815 | bool X11_InitModes(SDL_VideoDevice *_this) | ||
| 816 | { | ||
| 817 | /* XRandR is the One True Modern Way to do this on X11. If this | ||
| 818 | fails, we just won't report any display modes except the current | ||
| 819 | desktop size. */ | ||
| 820 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 821 | { | ||
| 822 | SDL_VideoData *data = _this->internal; | ||
| 823 | int xrandr_major, xrandr_minor; | ||
| 824 | // require at least XRandR v1.3 | ||
| 825 | if (CheckXRandR(data->display, &xrandr_major, &xrandr_minor) && | ||
| 826 | (xrandr_major >= 2 || (xrandr_major == 1 && xrandr_minor >= 3)) && | ||
| 827 | X11_InitModes_XRandR(_this)) { | ||
| 828 | return true; | ||
| 829 | } | ||
| 830 | } | ||
| 831 | #endif // SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 832 | |||
| 833 | // still here? Just set up an extremely basic display. | ||
| 834 | return X11_InitModes_StdXlib(_this); | ||
| 835 | } | ||
| 836 | |||
| 837 | bool X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display) | ||
| 838 | { | ||
| 839 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 840 | SDL_DisplayData *data = sdl_display->internal; | ||
| 841 | SDL_DisplayMode mode; | ||
| 842 | |||
| 843 | /* Unfortunately X11 requires the window to be created with the correct | ||
| 844 | * visual and depth ahead of time, but the SDL API allows you to create | ||
| 845 | * a window before setting the fullscreen display mode. This means that | ||
| 846 | * we have to use the same format for all windows and all display modes. | ||
| 847 | * (or support recreating the window with a new visual behind the scenes) | ||
| 848 | */ | ||
| 849 | SDL_zero(mode); | ||
| 850 | mode.format = sdl_display->desktop_mode.format; | ||
| 851 | |||
| 852 | if (data->use_xrandr) { | ||
| 853 | Display *display = _this->internal->display; | ||
| 854 | XRRScreenResources *res; | ||
| 855 | |||
| 856 | res = X11_XRRGetScreenResources(display, RootWindow(display, data->screen)); | ||
| 857 | if (res) { | ||
| 858 | SDL_DisplayModeData *modedata; | ||
| 859 | XRROutputInfo *output_info; | ||
| 860 | int i; | ||
| 861 | |||
| 862 | output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output); | ||
| 863 | if (output_info && output_info->connection != RR_Disconnected) { | ||
| 864 | for (i = 0; i < output_info->nmode; ++i) { | ||
| 865 | modedata = (SDL_DisplayModeData *)SDL_calloc(1, sizeof(SDL_DisplayModeData)); | ||
| 866 | if (!modedata) { | ||
| 867 | continue; | ||
| 868 | } | ||
| 869 | mode.internal = modedata; | ||
| 870 | |||
| 871 | if (!SetXRandRModeInfo(display, res, output_info->crtc, output_info->modes[i], &mode) || | ||
| 872 | !SDL_AddFullscreenDisplayMode(sdl_display, &mode)) { | ||
| 873 | SDL_free(modedata); | ||
| 874 | } | ||
| 875 | } | ||
| 876 | } | ||
| 877 | X11_XRRFreeOutputInfo(output_info); | ||
| 878 | X11_XRRFreeScreenResources(res); | ||
| 879 | } | ||
| 880 | } | ||
| 881 | #endif // SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 882 | return true; | ||
| 883 | } | ||
| 884 | |||
| 885 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 886 | // This catches an error from XRRSetScreenSize, as a workaround for now. | ||
| 887 | // !!! FIXME: remove this later when we have a better solution. | ||
| 888 | static int (*PreXRRSetScreenSizeErrorHandler)(Display *, XErrorEvent *) = NULL; | ||
| 889 | static int SDL_XRRSetScreenSizeErrHandler(Display *d, XErrorEvent *e) | ||
| 890 | { | ||
| 891 | // BadMatch: https://github.com/libsdl-org/SDL/issues/4561 | ||
| 892 | // BadValue: https://github.com/libsdl-org/SDL/issues/4840 | ||
| 893 | if ((e->error_code == BadMatch) || (e->error_code == BadValue)) { | ||
| 894 | return 0; | ||
| 895 | } | ||
| 896 | |||
| 897 | return PreXRRSetScreenSizeErrorHandler(d, e); | ||
| 898 | } | ||
| 899 | #endif | ||
| 900 | |||
| 901 | bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_DisplayMode *mode) | ||
| 902 | { | ||
| 903 | SDL_VideoData *viddata = _this->internal; | ||
| 904 | SDL_DisplayData *data = sdl_display->internal; | ||
| 905 | |||
| 906 | viddata->last_mode_change_deadline = SDL_GetTicks() + (PENDING_FOCUS_TIME * 2); | ||
| 907 | |||
| 908 | // XWayland mode switches are emulated with viewports and thus instantaneous. | ||
| 909 | if (!viddata->is_xwayland) { | ||
| 910 | if (sdl_display->current_mode != mode) { | ||
| 911 | data->mode_switch_deadline_ns = SDL_GetTicksNS() + MODE_SWITCH_TIMEOUT_NS; | ||
| 912 | } else { | ||
| 913 | data->mode_switch_deadline_ns = 0; | ||
| 914 | } | ||
| 915 | } | ||
| 916 | |||
| 917 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 918 | if (data->use_xrandr) { | ||
| 919 | Display *display = viddata->display; | ||
| 920 | SDL_DisplayModeData *modedata = mode->internal; | ||
| 921 | int mm_width, mm_height; | ||
| 922 | XRRScreenResources *res; | ||
| 923 | XRROutputInfo *output_info; | ||
| 924 | XRRCrtcInfo *crtc; | ||
| 925 | Status status; | ||
| 926 | |||
| 927 | res = X11_XRRGetScreenResources(display, RootWindow(display, data->screen)); | ||
| 928 | if (!res) { | ||
| 929 | return SDL_SetError("Couldn't get XRandR screen resources"); | ||
| 930 | } | ||
| 931 | |||
| 932 | output_info = X11_XRRGetOutputInfo(display, res, data->xrandr_output); | ||
| 933 | if (!output_info || output_info->connection == RR_Disconnected) { | ||
| 934 | X11_XRRFreeScreenResources(res); | ||
| 935 | return SDL_SetError("Couldn't get XRandR output info"); | ||
| 936 | } | ||
| 937 | |||
| 938 | crtc = X11_XRRGetCrtcInfo(display, res, output_info->crtc); | ||
| 939 | if (!crtc) { | ||
| 940 | X11_XRRFreeOutputInfo(output_info); | ||
| 941 | X11_XRRFreeScreenResources(res); | ||
| 942 | return SDL_SetError("Couldn't get XRandR crtc info"); | ||
| 943 | } | ||
| 944 | |||
| 945 | if (crtc->mode == modedata->xrandr_mode) { | ||
| 946 | #ifdef X11MODES_DEBUG | ||
| 947 | printf("already in desired mode 0x%lx (%ux%u), nothing to do\n", | ||
| 948 | crtc->mode, crtc->width, crtc->height); | ||
| 949 | #endif | ||
| 950 | status = Success; | ||
| 951 | goto freeInfo; | ||
| 952 | } | ||
| 953 | |||
| 954 | X11_XGrabServer(display); | ||
| 955 | status = X11_XRRSetCrtcConfig(display, res, output_info->crtc, CurrentTime, | ||
| 956 | 0, 0, None, crtc->rotation, NULL, 0); | ||
| 957 | if (status != Success) { | ||
| 958 | goto ungrabServer; | ||
| 959 | } | ||
| 960 | |||
| 961 | mm_width = mode->w * DisplayWidthMM(display, data->screen) / DisplayWidth(display, data->screen); | ||
| 962 | mm_height = mode->h * DisplayHeightMM(display, data->screen) / DisplayHeight(display, data->screen); | ||
| 963 | |||
| 964 | /* !!! FIXME: this can get into a problem scenario when a window is | ||
| 965 | bigger than a physical monitor in a configuration where one screen | ||
| 966 | spans multiple physical monitors. A detailed reproduction case is | ||
| 967 | discussed at https://github.com/libsdl-org/SDL/issues/4561 ... | ||
| 968 | for now we cheat and just catch the X11 error and carry on, which | ||
| 969 | is likely to cause subtle issues but is better than outright | ||
| 970 | crashing */ | ||
| 971 | X11_XSync(display, False); | ||
| 972 | PreXRRSetScreenSizeErrorHandler = X11_XSetErrorHandler(SDL_XRRSetScreenSizeErrHandler); | ||
| 973 | X11_XRRSetScreenSize(display, RootWindow(display, data->screen), | ||
| 974 | mode->w, mode->h, mm_width, mm_height); | ||
| 975 | X11_XSync(display, False); | ||
| 976 | X11_XSetErrorHandler(PreXRRSetScreenSizeErrorHandler); | ||
| 977 | |||
| 978 | status = X11_XRRSetCrtcConfig(display, res, output_info->crtc, CurrentTime, | ||
| 979 | crtc->x, crtc->y, modedata->xrandr_mode, crtc->rotation, | ||
| 980 | &data->xrandr_output, 1); | ||
| 981 | |||
| 982 | ungrabServer: | ||
| 983 | X11_XUngrabServer(display); | ||
| 984 | freeInfo: | ||
| 985 | X11_XRRFreeCrtcInfo(crtc); | ||
| 986 | X11_XRRFreeOutputInfo(output_info); | ||
| 987 | X11_XRRFreeScreenResources(res); | ||
| 988 | |||
| 989 | if (status != Success) { | ||
| 990 | return SDL_SetError("X11_XRRSetCrtcConfig failed"); | ||
| 991 | } | ||
| 992 | } | ||
| 993 | #else | ||
| 994 | (void)data; | ||
| 995 | #endif // SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 996 | |||
| 997 | return true; | ||
| 998 | } | ||
| 999 | |||
| 1000 | void X11_QuitModes(SDL_VideoDevice *_this) | ||
| 1001 | { | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect) | ||
| 1005 | { | ||
| 1006 | SDL_DisplayData *data = sdl_display->internal; | ||
| 1007 | |||
| 1008 | rect->x = data->x; | ||
| 1009 | rect->y = data->y; | ||
| 1010 | rect->w = sdl_display->current_mode->w; | ||
| 1011 | rect->h = sdl_display->current_mode->h; | ||
| 1012 | return true; | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect) | ||
| 1016 | { | ||
| 1017 | SDL_VideoData *data = _this->internal; | ||
| 1018 | Display *display = data->display; | ||
| 1019 | Atom _NET_WORKAREA; | ||
| 1020 | int real_format; | ||
| 1021 | Atom real_type; | ||
| 1022 | unsigned long items_read = 0, items_left = 0; | ||
| 1023 | unsigned char *propdata = NULL; | ||
| 1024 | bool result = false; | ||
| 1025 | |||
| 1026 | if (!X11_GetDisplayBounds(_this, sdl_display, rect)) { | ||
| 1027 | return false; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | _NET_WORKAREA = X11_XInternAtom(display, "_NET_WORKAREA", False); | ||
| 1031 | int status = X11_XGetWindowProperty(display, DefaultRootWindow(display), | ||
| 1032 | _NET_WORKAREA, 0L, 4L, False, XA_CARDINAL, | ||
| 1033 | &real_type, &real_format, &items_read, | ||
| 1034 | &items_left, &propdata); | ||
| 1035 | if ((status == Success) && (items_read >= 4)) { | ||
| 1036 | const long *p = (long *)propdata; | ||
| 1037 | const SDL_Rect usable = { (int)p[0], (int)p[1], (int)p[2], (int)p[3] }; | ||
| 1038 | result = true; | ||
| 1039 | if (!SDL_GetRectIntersection(rect, &usable, rect)) { | ||
| 1040 | SDL_zerop(rect); | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | if (propdata) { | ||
| 1045 | X11_XFree(propdata); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | return result; | ||
| 1049 | } | ||
| 1050 | |||
| 1051 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h new file mode 100644 index 0000000..35fd866 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11modes.h | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11modes_h_ | ||
| 24 | #define SDL_x11modes_h_ | ||
| 25 | |||
| 26 | struct SDL_DisplayData | ||
| 27 | { | ||
| 28 | int screen; | ||
| 29 | Visual *visual; | ||
| 30 | int depth; | ||
| 31 | int scanline_pad; | ||
| 32 | int x; | ||
| 33 | int y; | ||
| 34 | |||
| 35 | Uint64 mode_switch_deadline_ns; | ||
| 36 | |||
| 37 | bool use_xrandr; | ||
| 38 | |||
| 39 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 40 | RROutput xrandr_output; | ||
| 41 | char connector_name[16]; | ||
| 42 | #endif | ||
| 43 | }; | ||
| 44 | |||
| 45 | struct SDL_DisplayModeData | ||
| 46 | { | ||
| 47 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 48 | RRMode xrandr_mode; | ||
| 49 | #else | ||
| 50 | int unused; // just so struct isn't empty. | ||
| 51 | #endif | ||
| 52 | }; | ||
| 53 | |||
| 54 | extern bool X11_InitModes(SDL_VideoDevice *_this); | ||
| 55 | extern bool X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display); | ||
| 56 | extern bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode); | ||
| 57 | extern void X11_QuitModes(SDL_VideoDevice *_this); | ||
| 58 | |||
| 59 | // Some utility functions for working with visuals | ||
| 60 | extern bool X11_GetVisualInfoFromVisual(Display *display, Visual *visual, XVisualInfo *vinfo); | ||
| 61 | extern SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *vinfo); | ||
| 62 | extern bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect); | ||
| 63 | extern bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect); | ||
| 64 | |||
| 65 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 66 | extern void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent); | ||
| 67 | #endif | ||
| 68 | |||
| 69 | #endif // SDL_x11modes_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c new file mode 100644 index 0000000..5c72dbf --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.c | |||
| @@ -0,0 +1,552 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include <X11/cursorfont.h> | ||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11mouse.h" | ||
| 28 | #include "SDL_x11xinput2.h" | ||
| 29 | #include "../SDL_video_c.h" | ||
| 30 | #include "../../events/SDL_mouse_c.h" | ||
| 31 | |||
| 32 | struct SDL_CursorData | ||
| 33 | { | ||
| 34 | Cursor cursor; | ||
| 35 | }; | ||
| 36 | |||
| 37 | // FIXME: Find a better place to put this... | ||
| 38 | static Cursor x11_empty_cursor = None; | ||
| 39 | static bool x11_cursor_visible = true; | ||
| 40 | |||
| 41 | static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1]; | ||
| 42 | |||
| 43 | static Display *GetDisplay(void) | ||
| 44 | { | ||
| 45 | return SDL_GetVideoDevice()->internal->display; | ||
| 46 | } | ||
| 47 | |||
| 48 | static Cursor X11_CreateEmptyCursor(void) | ||
| 49 | { | ||
| 50 | if (x11_empty_cursor == None) { | ||
| 51 | Display *display = GetDisplay(); | ||
| 52 | char data[1]; | ||
| 53 | XColor color; | ||
| 54 | Pixmap pixmap; | ||
| 55 | |||
| 56 | SDL_zeroa(data); | ||
| 57 | color.red = color.green = color.blue = 0; | ||
| 58 | pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), | ||
| 59 | data, 1, 1); | ||
| 60 | if (pixmap) { | ||
| 61 | x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap, | ||
| 62 | &color, &color, 0, 0); | ||
| 63 | X11_XFreePixmap(display, pixmap); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | return x11_empty_cursor; | ||
| 67 | } | ||
| 68 | |||
| 69 | static void X11_DestroyEmptyCursor(void) | ||
| 70 | { | ||
| 71 | if (x11_empty_cursor != None) { | ||
| 72 | X11_XFreeCursor(GetDisplay(), x11_empty_cursor); | ||
| 73 | x11_empty_cursor = None; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | static SDL_Cursor *X11_CreateCursorAndData(Cursor x11_cursor) | ||
| 78 | { | ||
| 79 | SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor)); | ||
| 80 | if (cursor) { | ||
| 81 | SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data)); | ||
| 82 | if (!data) { | ||
| 83 | SDL_free(cursor); | ||
| 84 | return NULL; | ||
| 85 | } | ||
| 86 | data->cursor = x11_cursor; | ||
| 87 | cursor->internal = data; | ||
| 88 | } | ||
| 89 | return cursor; | ||
| 90 | } | ||
| 91 | |||
| 92 | #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 93 | static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y) | ||
| 94 | { | ||
| 95 | Display *display = GetDisplay(); | ||
| 96 | Cursor cursor = None; | ||
| 97 | XcursorImage *image; | ||
| 98 | |||
| 99 | image = X11_XcursorImageCreate(surface->w, surface->h); | ||
| 100 | if (!image) { | ||
| 101 | SDL_OutOfMemory(); | ||
| 102 | return None; | ||
| 103 | } | ||
| 104 | image->xhot = hot_x; | ||
| 105 | image->yhot = hot_y; | ||
| 106 | image->delay = 0; | ||
| 107 | |||
| 108 | SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888); | ||
| 109 | SDL_assert(surface->pitch == surface->w * 4); | ||
| 110 | SDL_memcpy(image->pixels, surface->pixels, (size_t)surface->h * surface->pitch); | ||
| 111 | |||
| 112 | cursor = X11_XcursorImageLoadCursor(display, image); | ||
| 113 | |||
| 114 | X11_XcursorImageDestroy(image); | ||
| 115 | |||
| 116 | return cursor; | ||
| 117 | } | ||
| 118 | #endif // SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 119 | |||
| 120 | static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y) | ||
| 121 | { | ||
| 122 | Display *display = GetDisplay(); | ||
| 123 | XColor fg, bg; | ||
| 124 | Cursor cursor = None; | ||
| 125 | Uint32 *ptr; | ||
| 126 | Uint8 *data_bits, *mask_bits; | ||
| 127 | Pixmap data_pixmap, mask_pixmap; | ||
| 128 | int x, y; | ||
| 129 | unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits; | ||
| 130 | size_t width_bytes = ((surface->w + 7) & ~((size_t)7)) / 8; | ||
| 131 | |||
| 132 | data_bits = SDL_calloc(1, surface->h * width_bytes); | ||
| 133 | if (!data_bits) { | ||
| 134 | return None; | ||
| 135 | } | ||
| 136 | |||
| 137 | mask_bits = SDL_calloc(1, surface->h * width_bytes); | ||
| 138 | if (!mask_bits) { | ||
| 139 | SDL_free(data_bits); | ||
| 140 | return None; | ||
| 141 | } | ||
| 142 | |||
| 143 | // Code below assumes ARGB pixel format | ||
| 144 | SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888); | ||
| 145 | |||
| 146 | rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0; | ||
| 147 | for (y = 0; y < surface->h; ++y) { | ||
| 148 | ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch); | ||
| 149 | for (x = 0; x < surface->w; ++x) { | ||
| 150 | int alpha = (*ptr >> 24) & 0xff; | ||
| 151 | int red = (*ptr >> 16) & 0xff; | ||
| 152 | int green = (*ptr >> 8) & 0xff; | ||
| 153 | int blue = (*ptr >> 0) & 0xff; | ||
| 154 | if (alpha > 25) { | ||
| 155 | mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8)); | ||
| 156 | |||
| 157 | if ((red + green + blue) > 0x40) { | ||
| 158 | fgBits++; | ||
| 159 | rfg += red; | ||
| 160 | gfg += green; | ||
| 161 | bfg += blue; | ||
| 162 | data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8)); | ||
| 163 | } else { | ||
| 164 | bgBits++; | ||
| 165 | rbg += red; | ||
| 166 | gbg += green; | ||
| 167 | bbg += blue; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | ++ptr; | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | if (fgBits) { | ||
| 175 | fg.red = rfg * 257 / fgBits; | ||
| 176 | fg.green = gfg * 257 / fgBits; | ||
| 177 | fg.blue = bfg * 257 / fgBits; | ||
| 178 | } else { | ||
| 179 | fg.red = fg.green = fg.blue = 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (bgBits) { | ||
| 183 | bg.red = rbg * 257 / bgBits; | ||
| 184 | bg.green = gbg * 257 / bgBits; | ||
| 185 | bg.blue = bbg * 257 / bgBits; | ||
| 186 | } else { | ||
| 187 | bg.red = bg.green = bg.blue = 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), | ||
| 191 | (char *)data_bits, | ||
| 192 | surface->w, surface->h); | ||
| 193 | mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display), | ||
| 194 | (char *)mask_bits, | ||
| 195 | surface->w, surface->h); | ||
| 196 | cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap, | ||
| 197 | &fg, &bg, hot_x, hot_y); | ||
| 198 | X11_XFreePixmap(display, data_pixmap); | ||
| 199 | X11_XFreePixmap(display, mask_pixmap); | ||
| 200 | SDL_free(data_bits); | ||
| 201 | SDL_free(mask_bits); | ||
| 202 | |||
| 203 | return cursor; | ||
| 204 | } | ||
| 205 | |||
| 206 | static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y) | ||
| 207 | { | ||
| 208 | Cursor x11_cursor = None; | ||
| 209 | |||
| 210 | #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 211 | if (SDL_X11_HAVE_XCURSOR) { | ||
| 212 | x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y); | ||
| 213 | } | ||
| 214 | #endif | ||
| 215 | if (x11_cursor == None) { | ||
| 216 | x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y); | ||
| 217 | } | ||
| 218 | return X11_CreateCursorAndData(x11_cursor); | ||
| 219 | } | ||
| 220 | |||
| 221 | static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id) | ||
| 222 | { | ||
| 223 | switch (id) { | ||
| 224 | // X Font Cursors reference: | ||
| 225 | // http://tronche.com/gui/x/xlib/appendix/b/ | ||
| 226 | case SDL_SYSTEM_CURSOR_DEFAULT: return XC_left_ptr; | ||
| 227 | case SDL_SYSTEM_CURSOR_TEXT: return XC_xterm; | ||
| 228 | case SDL_SYSTEM_CURSOR_WAIT: return XC_watch; | ||
| 229 | case SDL_SYSTEM_CURSOR_CROSSHAIR: return XC_tcross; | ||
| 230 | case SDL_SYSTEM_CURSOR_PROGRESS: return XC_watch; | ||
| 231 | case SDL_SYSTEM_CURSOR_NWSE_RESIZE: return XC_top_left_corner; | ||
| 232 | case SDL_SYSTEM_CURSOR_NESW_RESIZE: return XC_top_right_corner; | ||
| 233 | case SDL_SYSTEM_CURSOR_EW_RESIZE: return XC_sb_h_double_arrow; | ||
| 234 | case SDL_SYSTEM_CURSOR_NS_RESIZE: return XC_sb_v_double_arrow; | ||
| 235 | case SDL_SYSTEM_CURSOR_MOVE: return XC_fleur; | ||
| 236 | case SDL_SYSTEM_CURSOR_NOT_ALLOWED: return XC_pirate; | ||
| 237 | case SDL_SYSTEM_CURSOR_POINTER: return XC_hand2; | ||
| 238 | case SDL_SYSTEM_CURSOR_NW_RESIZE: return XC_top_left_corner; | ||
| 239 | case SDL_SYSTEM_CURSOR_N_RESIZE: return XC_top_side; | ||
| 240 | case SDL_SYSTEM_CURSOR_NE_RESIZE: return XC_top_right_corner; | ||
| 241 | case SDL_SYSTEM_CURSOR_E_RESIZE: return XC_right_side; | ||
| 242 | case SDL_SYSTEM_CURSOR_SE_RESIZE: return XC_bottom_right_corner; | ||
| 243 | case SDL_SYSTEM_CURSOR_S_RESIZE: return XC_bottom_side; | ||
| 244 | case SDL_SYSTEM_CURSOR_SW_RESIZE: return XC_bottom_left_corner; | ||
| 245 | case SDL_SYSTEM_CURSOR_W_RESIZE: return XC_left_side; | ||
| 246 | case SDL_SYSTEM_CURSOR_COUNT: break; // so the compiler might notice if an enum value is missing here. | ||
| 247 | } | ||
| 248 | |||
| 249 | SDL_assert(0); | ||
| 250 | return 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id) | ||
| 254 | { | ||
| 255 | SDL_Cursor *cursor = NULL; | ||
| 256 | Display *dpy = GetDisplay(); | ||
| 257 | Cursor x11_cursor = None; | ||
| 258 | |||
| 259 | #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 260 | if (SDL_X11_HAVE_XCURSOR) { | ||
| 261 | x11_cursor = X11_XcursorLibraryLoadCursor(dpy, SDL_GetCSSCursorName(id, NULL)); | ||
| 262 | } | ||
| 263 | #endif | ||
| 264 | |||
| 265 | if (x11_cursor == None) { | ||
| 266 | x11_cursor = X11_XCreateFontCursor(dpy, GetLegacySystemCursorShape(id)); | ||
| 267 | } | ||
| 268 | |||
| 269 | if (x11_cursor != None) { | ||
| 270 | cursor = X11_CreateCursorAndData(x11_cursor); | ||
| 271 | } | ||
| 272 | |||
| 273 | return cursor; | ||
| 274 | } | ||
| 275 | |||
| 276 | static SDL_Cursor *X11_CreateDefaultCursor(void) | ||
| 277 | { | ||
| 278 | SDL_SystemCursor id = SDL_GetDefaultSystemCursor(); | ||
| 279 | return X11_CreateSystemCursor(id); | ||
| 280 | } | ||
| 281 | |||
| 282 | static void X11_FreeCursor(SDL_Cursor *cursor) | ||
| 283 | { | ||
| 284 | Cursor x11_cursor = cursor->internal->cursor; | ||
| 285 | |||
| 286 | if (x11_cursor != None) { | ||
| 287 | X11_XFreeCursor(GetDisplay(), x11_cursor); | ||
| 288 | } | ||
| 289 | SDL_free(cursor->internal); | ||
| 290 | SDL_free(cursor); | ||
| 291 | } | ||
| 292 | |||
| 293 | static bool X11_ShowCursor(SDL_Cursor *cursor) | ||
| 294 | { | ||
| 295 | Cursor x11_cursor = 0; | ||
| 296 | |||
| 297 | if (cursor) { | ||
| 298 | x11_cursor = cursor->internal->cursor; | ||
| 299 | } else { | ||
| 300 | x11_cursor = X11_CreateEmptyCursor(); | ||
| 301 | } | ||
| 302 | |||
| 303 | // FIXME: Is there a better way than this? | ||
| 304 | { | ||
| 305 | SDL_VideoDevice *video = SDL_GetVideoDevice(); | ||
| 306 | Display *display = GetDisplay(); | ||
| 307 | SDL_Window *window; | ||
| 308 | |||
| 309 | x11_cursor_visible = !!cursor; | ||
| 310 | |||
| 311 | for (window = video->windows; window; window = window->next) { | ||
| 312 | SDL_WindowData *data = window->internal; | ||
| 313 | if (data) { | ||
| 314 | if (x11_cursor != None) { | ||
| 315 | X11_XDefineCursor(display, data->xwindow, x11_cursor); | ||
| 316 | } else { | ||
| 317 | X11_XUndefineCursor(display, data->xwindow); | ||
| 318 | } | ||
| 319 | } | ||
| 320 | } | ||
| 321 | X11_XFlush(display); | ||
| 322 | } | ||
| 323 | return true; | ||
| 324 | } | ||
| 325 | |||
| 326 | static void X11_WarpMouseInternal(Window xwindow, float x, float y) | ||
| 327 | { | ||
| 328 | SDL_VideoData *videodata = SDL_GetVideoDevice()->internal; | ||
| 329 | Display *display = videodata->display; | ||
| 330 | bool warp_hack = false; | ||
| 331 | |||
| 332 | // XWayland will only warp the cursor if it is hidden, so this workaround is required. | ||
| 333 | if (videodata->is_xwayland && x11_cursor_visible) { | ||
| 334 | warp_hack = true; | ||
| 335 | } | ||
| 336 | |||
| 337 | if (warp_hack) { | ||
| 338 | X11_ShowCursor(NULL); | ||
| 339 | } | ||
| 340 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 341 | int deviceid = 0; | ||
| 342 | if (X11_Xinput2IsInitialized()) { | ||
| 343 | /* It seems XIWarpPointer() doesn't work correctly on multi-head setups: | ||
| 344 | * https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea | ||
| 345 | */ | ||
| 346 | if (ScreenCount(display) == 1) { | ||
| 347 | X11_XIGetClientPointer(display, None, &deviceid); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | if (deviceid != 0) { | ||
| 351 | SDL_assert(SDL_X11_HAVE_XINPUT2); | ||
| 352 | X11_XIWarpPointer(display, deviceid, None, xwindow, 0.0, 0.0, 0, 0, x, y); | ||
| 353 | } else | ||
| 354 | #endif | ||
| 355 | { | ||
| 356 | X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, (int)x, (int)y); | ||
| 357 | } | ||
| 358 | |||
| 359 | if (warp_hack) { | ||
| 360 | X11_ShowCursor(SDL_GetCursor()); | ||
| 361 | } | ||
| 362 | X11_XSync(display, False); | ||
| 363 | videodata->global_mouse_changed = true; | ||
| 364 | } | ||
| 365 | |||
| 366 | static bool X11_WarpMouse(SDL_Window *window, float x, float y) | ||
| 367 | { | ||
| 368 | SDL_WindowData *data = window->internal; | ||
| 369 | |||
| 370 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 371 | // If we have no barrier, we need to warp | ||
| 372 | if (data->pointer_barrier_active == false) { | ||
| 373 | X11_WarpMouseInternal(data->xwindow, x, y); | ||
| 374 | } | ||
| 375 | #else | ||
| 376 | X11_WarpMouseInternal(data->xwindow, x, y); | ||
| 377 | #endif | ||
| 378 | return true; | ||
| 379 | } | ||
| 380 | |||
| 381 | static bool X11_WarpMouseGlobal(float x, float y) | ||
| 382 | { | ||
| 383 | X11_WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y); | ||
| 384 | return true; | ||
| 385 | } | ||
| 386 | |||
| 387 | static bool X11_SetRelativeMouseMode(bool enabled) | ||
| 388 | { | ||
| 389 | if (!X11_Xinput2IsInitialized()) { | ||
| 390 | return SDL_Unsupported(); | ||
| 391 | } | ||
| 392 | return true; | ||
| 393 | } | ||
| 394 | |||
| 395 | static bool X11_CaptureMouse(SDL_Window *window) | ||
| 396 | { | ||
| 397 | Display *display = GetDisplay(); | ||
| 398 | SDL_Window *mouse_focus = SDL_GetMouseFocus(); | ||
| 399 | |||
| 400 | if (window) { | ||
| 401 | SDL_WindowData *data = window->internal; | ||
| 402 | |||
| 403 | /* If XInput2 is handling the pointer input, non-confinement grabs will always fail with 'AlreadyGrabbed', | ||
| 404 | * since the pointer is being grabbed by XInput2. | ||
| 405 | */ | ||
| 406 | if (!data->xinput2_mouse_enabled || data->mouse_grabbed) { | ||
| 407 | const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; | ||
| 408 | Window confined = (data->mouse_grabbed ? data->xwindow : None); | ||
| 409 | const int rc = X11_XGrabPointer(display, data->xwindow, False, | ||
| 410 | mask, GrabModeAsync, GrabModeAsync, | ||
| 411 | confined, None, CurrentTime); | ||
| 412 | if (rc != GrabSuccess) { | ||
| 413 | return SDL_SetError("X server refused mouse capture"); | ||
| 414 | } | ||
| 415 | |||
| 416 | if (data->mouse_grabbed) { | ||
| 417 | // XGrabPointer can warp the cursor when confining, so update the coordinates. | ||
| 418 | data->videodata->global_mouse_changed = true; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | } else if (mouse_focus) { | ||
| 422 | SDL_UpdateWindowGrab(mouse_focus); | ||
| 423 | } else { | ||
| 424 | X11_XUngrabPointer(display, CurrentTime); | ||
| 425 | } | ||
| 426 | |||
| 427 | X11_XSync(display, False); | ||
| 428 | |||
| 429 | return true; | ||
| 430 | } | ||
| 431 | |||
| 432 | static SDL_MouseButtonFlags X11_GetGlobalMouseState(float *x, float *y) | ||
| 433 | { | ||
| 434 | SDL_VideoData *videodata = SDL_GetVideoDevice()->internal; | ||
| 435 | SDL_DisplayID *displays; | ||
| 436 | Display *display = GetDisplay(); | ||
| 437 | int i; | ||
| 438 | |||
| 439 | // !!! FIXME: should we XSync() here first? | ||
| 440 | |||
| 441 | if (!X11_Xinput2IsInitialized()) { | ||
| 442 | videodata->global_mouse_changed = true; | ||
| 443 | } | ||
| 444 | |||
| 445 | // check if we have this cached since XInput last saw the mouse move. | ||
| 446 | // !!! FIXME: can we just calculate this from XInput's events? | ||
| 447 | if (videodata->global_mouse_changed) { | ||
| 448 | displays = SDL_GetDisplays(NULL); | ||
| 449 | if (displays) { | ||
| 450 | for (i = 0; displays[i]; ++i) { | ||
| 451 | SDL_DisplayData *data = SDL_GetDisplayDriverData(displays[i]); | ||
| 452 | if (data) { | ||
| 453 | Window root, child; | ||
| 454 | int rootx, rooty, winx, winy; | ||
| 455 | unsigned int mask; | ||
| 456 | if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) { | ||
| 457 | XWindowAttributes root_attrs; | ||
| 458 | SDL_MouseButtonFlags buttons = 0; | ||
| 459 | buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0; | ||
| 460 | buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0; | ||
| 461 | buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0; | ||
| 462 | // Use the SDL state for the extended buttons - it's better than nothing | ||
| 463 | buttons |= (SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK)); | ||
| 464 | /* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing | ||
| 465 | * (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right). | ||
| 466 | * | ||
| 467 | * Adding root position to root-relative coordinates seems to be a better way to get absolute position. */ | ||
| 468 | X11_XGetWindowAttributes(display, root, &root_attrs); | ||
| 469 | videodata->global_mouse_position.x = root_attrs.x + rootx; | ||
| 470 | videodata->global_mouse_position.y = root_attrs.y + rooty; | ||
| 471 | videodata->global_mouse_buttons = buttons; | ||
| 472 | videodata->global_mouse_changed = false; | ||
| 473 | break; | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 477 | SDL_free(displays); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | SDL_assert(!videodata->global_mouse_changed); // The pointer wasn't on any X11 screen?! | ||
| 482 | |||
| 483 | *x = (float)videodata->global_mouse_position.x; | ||
| 484 | *y = (float)videodata->global_mouse_position.y; | ||
| 485 | return videodata->global_mouse_buttons; | ||
| 486 | } | ||
| 487 | |||
| 488 | void X11_InitMouse(SDL_VideoDevice *_this) | ||
| 489 | { | ||
| 490 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 491 | |||
| 492 | mouse->CreateCursor = X11_CreateCursor; | ||
| 493 | mouse->CreateSystemCursor = X11_CreateSystemCursor; | ||
| 494 | mouse->ShowCursor = X11_ShowCursor; | ||
| 495 | mouse->FreeCursor = X11_FreeCursor; | ||
| 496 | mouse->WarpMouse = X11_WarpMouse; | ||
| 497 | mouse->WarpMouseGlobal = X11_WarpMouseGlobal; | ||
| 498 | mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode; | ||
| 499 | mouse->CaptureMouse = X11_CaptureMouse; | ||
| 500 | mouse->GetGlobalMouseState = X11_GetGlobalMouseState; | ||
| 501 | |||
| 502 | SDL_HitTestResult r = SDL_HITTEST_NORMAL; | ||
| 503 | while (r <= SDL_HITTEST_RESIZE_LEFT) { | ||
| 504 | switch (r) { | ||
| 505 | case SDL_HITTEST_NORMAL: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break; | ||
| 506 | case SDL_HITTEST_DRAGGABLE: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break; | ||
| 507 | case SDL_HITTEST_RESIZE_TOPLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NW_RESIZE); break; | ||
| 508 | case SDL_HITTEST_RESIZE_TOP: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_N_RESIZE); break; | ||
| 509 | case SDL_HITTEST_RESIZE_TOPRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NE_RESIZE); break; | ||
| 510 | case SDL_HITTEST_RESIZE_RIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_E_RESIZE); break; | ||
| 511 | case SDL_HITTEST_RESIZE_BOTTOMRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SE_RESIZE); break; | ||
| 512 | case SDL_HITTEST_RESIZE_BOTTOM: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_S_RESIZE); break; | ||
| 513 | case SDL_HITTEST_RESIZE_BOTTOMLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SW_RESIZE); break; | ||
| 514 | case SDL_HITTEST_RESIZE_LEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_W_RESIZE); break; | ||
| 515 | } | ||
| 516 | r++; | ||
| 517 | } | ||
| 518 | |||
| 519 | SDL_SetDefaultCursor(X11_CreateDefaultCursor()); | ||
| 520 | } | ||
| 521 | |||
| 522 | void X11_QuitMouse(SDL_VideoDevice *_this) | ||
| 523 | { | ||
| 524 | SDL_VideoData *data = _this->internal; | ||
| 525 | SDL_XInput2DeviceInfo *i; | ||
| 526 | SDL_XInput2DeviceInfo *next; | ||
| 527 | int j; | ||
| 528 | |||
| 529 | for (j = 0; j < SDL_arraysize(sys_cursors); j++) { | ||
| 530 | X11_FreeCursor(sys_cursors[j]); | ||
| 531 | sys_cursors[j] = NULL; | ||
| 532 | } | ||
| 533 | |||
| 534 | for (i = data->mouse_device_info; i; i = next) { | ||
| 535 | next = i->next; | ||
| 536 | SDL_free(i); | ||
| 537 | } | ||
| 538 | data->mouse_device_info = NULL; | ||
| 539 | |||
| 540 | X11_DestroyEmptyCursor(); | ||
| 541 | } | ||
| 542 | |||
| 543 | void X11_SetHitTestCursor(SDL_HitTestResult rc) | ||
| 544 | { | ||
| 545 | if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) { | ||
| 546 | SDL_SetCursor(NULL); | ||
| 547 | } else { | ||
| 548 | X11_ShowCursor(sys_cursors[rc]); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h new file mode 100644 index 0000000..2a2973c --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11mouse.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11mouse_h_ | ||
| 24 | #define SDL_x11mouse_h_ | ||
| 25 | |||
| 26 | typedef struct SDL_XInput2DeviceInfo | ||
| 27 | { | ||
| 28 | int device_id; | ||
| 29 | bool relative[2]; | ||
| 30 | double minval[2]; | ||
| 31 | double maxval[2]; | ||
| 32 | double prev_coords[2]; | ||
| 33 | struct SDL_XInput2DeviceInfo *next; | ||
| 34 | } SDL_XInput2DeviceInfo; | ||
| 35 | |||
| 36 | extern void X11_InitMouse(SDL_VideoDevice *_this); | ||
| 37 | extern void X11_QuitMouse(SDL_VideoDevice *_this); | ||
| 38 | extern void X11_SetHitTestCursor(SDL_HitTestResult rc); | ||
| 39 | |||
| 40 | #endif // SDL_x11mouse_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c new file mode 100644 index 0000000..d46409f --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.c | |||
| @@ -0,0 +1,1116 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org> | ||
| 4 | Copyright (C) 2021 NVIDIA Corporation | ||
| 5 | |||
| 6 | This software is provided 'as-is', without any express or implied | ||
| 7 | warranty. In no event will the authors be held liable for any damages | ||
| 8 | arising from the use of this software. | ||
| 9 | |||
| 10 | Permission is granted to anyone to use this software for any purpose, | ||
| 11 | including commercial applications, and to alter it and redistribute it | ||
| 12 | freely, subject to the following restrictions: | ||
| 13 | |||
| 14 | 1. The origin of this software must not be misrepresented; you must not | ||
| 15 | claim that you wrote the original software. If you use this software | ||
| 16 | in a product, an acknowledgment in the product documentation would be | ||
| 17 | appreciated but is not required. | ||
| 18 | 2. Altered source versions must be plainly marked as such, and must not be | ||
| 19 | misrepresented as being the original software. | ||
| 20 | 3. This notice may not be removed or altered from any source distribution. | ||
| 21 | */ | ||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 25 | |||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11xsync.h" | ||
| 28 | |||
| 29 | // GLX implementation of SDL OpenGL support | ||
| 30 | |||
| 31 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 32 | #include "SDL_x11opengles.h" | ||
| 33 | |||
| 34 | #if defined(SDL_PLATFORM_IRIX) || defined(SDL_PLATFORM_NETBSD) || defined(SDL_PLATFORM_OPENBSD) | ||
| 35 | /* | ||
| 36 | * IRIX doesn't have a GL library versioning system. | ||
| 37 | * NetBSD and OpenBSD have different GL library versions depending on how | ||
| 38 | * the library was installed. | ||
| 39 | */ | ||
| 40 | #define DEFAULT_OPENGL "libGL.so" | ||
| 41 | #elif defined(SDL_PLATFORM_MACOS) | ||
| 42 | #define DEFAULT_OPENGL "/opt/X11/lib/libGL.1.dylib" | ||
| 43 | #else | ||
| 44 | #define DEFAULT_OPENGL "libGL.so.1" | ||
| 45 | #endif | ||
| 46 | |||
| 47 | #ifndef GLX_NONE_EXT | ||
| 48 | #define GLX_NONE_EXT 0x8000 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | #ifndef GLX_ARB_multisample | ||
| 52 | #define GLX_ARB_multisample | ||
| 53 | #define GLX_SAMPLE_BUFFERS_ARB 100000 | ||
| 54 | #define GLX_SAMPLES_ARB 100001 | ||
| 55 | #endif | ||
| 56 | |||
| 57 | #ifndef GLX_EXT_visual_rating | ||
| 58 | #define GLX_EXT_visual_rating | ||
| 59 | #define GLX_VISUAL_CAVEAT_EXT 0x20 | ||
| 60 | #define GLX_NONE_EXT 0x8000 | ||
| 61 | #define GLX_SLOW_VISUAL_EXT 0x8001 | ||
| 62 | #define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D | ||
| 63 | #endif | ||
| 64 | |||
| 65 | #ifndef GLX_EXT_visual_info | ||
| 66 | #define GLX_EXT_visual_info | ||
| 67 | #define GLX_X_VISUAL_TYPE_EXT 0x22 | ||
| 68 | #define GLX_DIRECT_COLOR_EXT 0x8003 | ||
| 69 | #endif | ||
| 70 | |||
| 71 | #ifndef GLX_ARB_create_context | ||
| 72 | #define GLX_ARB_create_context | ||
| 73 | #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 | ||
| 74 | #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 | ||
| 75 | #define GLX_CONTEXT_FLAGS_ARB 0x2094 | ||
| 76 | #define GLX_CONTEXT_DEBUG_BIT_ARB 0x0001 | ||
| 77 | #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 | ||
| 78 | |||
| 79 | // Typedef for the GL 3.0 context creation function | ||
| 80 | typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display *dpy, | ||
| 81 | GLXFBConfig config, | ||
| 82 | GLXContext | ||
| 83 | share_context, | ||
| 84 | Bool direct, | ||
| 85 | const int | ||
| 86 | *attrib_list); | ||
| 87 | #endif | ||
| 88 | |||
| 89 | #ifndef GLX_ARB_create_context_profile | ||
| 90 | #define GLX_ARB_create_context_profile | ||
| 91 | #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 | ||
| 92 | #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 | ||
| 93 | #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 | ||
| 94 | #endif | ||
| 95 | |||
| 96 | #ifndef GLX_ARB_create_context_robustness | ||
| 97 | #define GLX_ARB_create_context_robustness | ||
| 98 | #define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 | ||
| 99 | #define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 | ||
| 100 | #define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 | ||
| 101 | #define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 | ||
| 102 | #endif | ||
| 103 | |||
| 104 | #ifndef GLX_EXT_create_context_es2_profile | ||
| 105 | #define GLX_EXT_create_context_es2_profile | ||
| 106 | #ifndef GLX_CONTEXT_ES2_PROFILE_BIT_EXT | ||
| 107 | #define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000002 | ||
| 108 | #endif | ||
| 109 | #endif | ||
| 110 | |||
| 111 | #ifndef GLX_ARB_framebuffer_sRGB | ||
| 112 | #define GLX_ARB_framebuffer_sRGB | ||
| 113 | #ifndef GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB | ||
| 114 | #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20B2 | ||
| 115 | #endif | ||
| 116 | #endif | ||
| 117 | |||
| 118 | #ifndef GLX_ARB_fbconfig_float | ||
| 119 | #define GLX_ARB_fbconfig_float | ||
| 120 | #ifndef GLX_RGBA_FLOAT_TYPE_ARB | ||
| 121 | #define GLX_RGBA_FLOAT_TYPE_ARB 0x20B9 | ||
| 122 | #endif | ||
| 123 | #ifndef GLX_RGBA_FLOAT_BIT_ARB | ||
| 124 | #define GLX_RGBA_FLOAT_BIT_ARB 0x00000004 | ||
| 125 | #endif | ||
| 126 | #endif | ||
| 127 | |||
| 128 | #ifndef GLX_ARB_create_context_no_error | ||
| 129 | #define GLX_ARB_create_context_no_error | ||
| 130 | #ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB | ||
| 131 | #define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3 | ||
| 132 | #endif | ||
| 133 | #endif | ||
| 134 | |||
| 135 | #ifndef GLX_EXT_swap_control | ||
| 136 | #define GLX_SWAP_INTERVAL_EXT 0x20F1 | ||
| 137 | #define GLX_MAX_SWAP_INTERVAL_EXT 0x20F2 | ||
| 138 | #endif | ||
| 139 | |||
| 140 | #ifndef GLX_EXT_swap_control_tear | ||
| 141 | #define GLX_LATE_SWAPS_TEAR_EXT 0x20F3 | ||
| 142 | #endif | ||
| 143 | |||
| 144 | #ifndef GLX_ARB_context_flush_control | ||
| 145 | #define GLX_ARB_context_flush_control | ||
| 146 | #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 | ||
| 147 | #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0x0000 | ||
| 148 | #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 | ||
| 149 | #endif | ||
| 150 | |||
| 151 | #define OPENGL_REQUIRES_DLOPEN | ||
| 152 | #if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN) | ||
| 153 | #include <dlfcn.h> | ||
| 154 | #define GL_LoadObject(X) dlopen(X, (RTLD_NOW | RTLD_GLOBAL)) | ||
| 155 | #define GL_LoadFunction dlsym | ||
| 156 | #define GL_UnloadObject dlclose | ||
| 157 | #else | ||
| 158 | #define GL_LoadObject SDL_LoadObject | ||
| 159 | #define GL_LoadFunction SDL_LoadFunction | ||
| 160 | #define GL_UnloadObject SDL_UnloadObject | ||
| 161 | #endif | ||
| 162 | |||
| 163 | static void X11_GL_InitExtensions(SDL_VideoDevice *_this); | ||
| 164 | |||
| 165 | bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path) | ||
| 166 | { | ||
| 167 | Display *display; | ||
| 168 | SDL_SharedObject *handle; | ||
| 169 | |||
| 170 | if (_this->gl_data) { | ||
| 171 | return SDL_SetError("OpenGL context already created"); | ||
| 172 | } | ||
| 173 | |||
| 174 | // Load the OpenGL library | ||
| 175 | if (path == NULL) { | ||
| 176 | path = SDL_GetHint(SDL_HINT_OPENGL_LIBRARY); | ||
| 177 | } | ||
| 178 | if (path == NULL) { | ||
| 179 | path = DEFAULT_OPENGL; | ||
| 180 | } | ||
| 181 | _this->gl_config.dll_handle = GL_LoadObject(path); | ||
| 182 | if (!_this->gl_config.dll_handle) { | ||
| 183 | #if defined(OPENGL_REQUIRES_DLOPEN) && defined(HAVE_DLOPEN) | ||
| 184 | SDL_SetError("Failed loading %s: %s", path, dlerror()); | ||
| 185 | #endif | ||
| 186 | return false; | ||
| 187 | } | ||
| 188 | SDL_strlcpy(_this->gl_config.driver_path, path, | ||
| 189 | SDL_arraysize(_this->gl_config.driver_path)); | ||
| 190 | |||
| 191 | // Allocate OpenGL memory | ||
| 192 | _this->gl_data = | ||
| 193 | (struct SDL_GLDriverData *)SDL_calloc(1, | ||
| 194 | sizeof(struct | ||
| 195 | SDL_GLDriverData)); | ||
| 196 | if (!_this->gl_data) { | ||
| 197 | return false; | ||
| 198 | } | ||
| 199 | |||
| 200 | // Load function pointers | ||
| 201 | handle = _this->gl_config.dll_handle; | ||
| 202 | _this->gl_data->glXQueryExtension = | ||
| 203 | (Bool(*)(Display *, int *, int *)) | ||
| 204 | GL_LoadFunction(handle, "glXQueryExtension"); | ||
| 205 | _this->gl_data->glXGetProcAddress = | ||
| 206 | (__GLXextFuncPtr (*)(const GLubyte *)) | ||
| 207 | GL_LoadFunction(handle, "glXGetProcAddressARB"); | ||
| 208 | _this->gl_data->glXChooseVisual = | ||
| 209 | (XVisualInfo * (*)(Display *, int, int *)) | ||
| 210 | X11_GL_GetProcAddress(_this, "glXChooseVisual"); | ||
| 211 | _this->gl_data->glXCreateContext = | ||
| 212 | (GLXContext(*)(Display *, XVisualInfo *, GLXContext, int)) | ||
| 213 | X11_GL_GetProcAddress(_this, "glXCreateContext"); | ||
| 214 | _this->gl_data->glXDestroyContext = | ||
| 215 | (void (*)(Display *, GLXContext)) | ||
| 216 | X11_GL_GetProcAddress(_this, "glXDestroyContext"); | ||
| 217 | _this->gl_data->glXMakeCurrent = | ||
| 218 | (int (*)(Display *, GLXDrawable, GLXContext)) | ||
| 219 | X11_GL_GetProcAddress(_this, "glXMakeCurrent"); | ||
| 220 | _this->gl_data->glXSwapBuffers = | ||
| 221 | (void (*)(Display *, GLXDrawable)) | ||
| 222 | X11_GL_GetProcAddress(_this, "glXSwapBuffers"); | ||
| 223 | _this->gl_data->glXQueryDrawable = | ||
| 224 | (void (*)(Display *, GLXDrawable, int, unsigned int *)) | ||
| 225 | X11_GL_GetProcAddress(_this, "glXQueryDrawable"); | ||
| 226 | |||
| 227 | if (!_this->gl_data->glXQueryExtension || | ||
| 228 | !_this->gl_data->glXChooseVisual || | ||
| 229 | !_this->gl_data->glXCreateContext || | ||
| 230 | !_this->gl_data->glXDestroyContext || | ||
| 231 | !_this->gl_data->glXMakeCurrent || | ||
| 232 | !_this->gl_data->glXSwapBuffers) { | ||
| 233 | return SDL_SetError("Could not retrieve OpenGL functions"); | ||
| 234 | } | ||
| 235 | |||
| 236 | display = _this->internal->display; | ||
| 237 | if (!_this->gl_data->glXQueryExtension(display, &_this->gl_data->errorBase, &_this->gl_data->eventBase)) { | ||
| 238 | return SDL_SetError("GLX is not supported"); | ||
| 239 | } | ||
| 240 | |||
| 241 | _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNTESTED; | ||
| 242 | |||
| 243 | // Initialize extensions | ||
| 244 | /* See lengthy comment about the inc/dec in | ||
| 245 | ../windows/SDL_windowsopengl.c. */ | ||
| 246 | ++_this->gl_config.driver_loaded; | ||
| 247 | X11_GL_InitExtensions(_this); | ||
| 248 | --_this->gl_config.driver_loaded; | ||
| 249 | |||
| 250 | /* If we need a GL ES context and there's no | ||
| 251 | * GLX_EXT_create_context_es2_profile extension, switch over to X11_GLES functions | ||
| 252 | */ | ||
| 253 | if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || | ||
| 254 | SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) && | ||
| 255 | X11_GL_UseEGL(_this)) { | ||
| 256 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 257 | X11_GL_UnloadLibrary(_this); | ||
| 258 | _this->GL_LoadLibrary = X11_GLES_LoadLibrary; | ||
| 259 | _this->GL_GetProcAddress = X11_GLES_GetProcAddress; | ||
| 260 | _this->GL_UnloadLibrary = X11_GLES_UnloadLibrary; | ||
| 261 | _this->GL_CreateContext = X11_GLES_CreateContext; | ||
| 262 | _this->GL_MakeCurrent = X11_GLES_MakeCurrent; | ||
| 263 | _this->GL_SetSwapInterval = X11_GLES_SetSwapInterval; | ||
| 264 | _this->GL_GetSwapInterval = X11_GLES_GetSwapInterval; | ||
| 265 | _this->GL_SwapWindow = X11_GLES_SwapWindow; | ||
| 266 | _this->GL_DestroyContext = X11_GLES_DestroyContext; | ||
| 267 | return X11_GLES_LoadLibrary(_this, NULL); | ||
| 268 | #else | ||
| 269 | return SDL_SetError("SDL not configured with EGL support"); | ||
| 270 | #endif | ||
| 271 | } | ||
| 272 | |||
| 273 | return true; | ||
| 274 | } | ||
| 275 | |||
| 276 | SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc) | ||
| 277 | { | ||
| 278 | if (_this->gl_data->glXGetProcAddress) { | ||
| 279 | return _this->gl_data->glXGetProcAddress((const GLubyte *)proc); | ||
| 280 | } | ||
| 281 | return GL_LoadFunction(_this->gl_config.dll_handle, proc); | ||
| 282 | } | ||
| 283 | |||
| 284 | void X11_GL_UnloadLibrary(SDL_VideoDevice *_this) | ||
| 285 | { | ||
| 286 | /* Don't actually unload the library, since it may have registered | ||
| 287 | * X11 shutdown hooks, per the notes at: | ||
| 288 | * http://dri.sourceforge.net/doc/DRIuserguide.html | ||
| 289 | */ | ||
| 290 | #if 0 | ||
| 291 | GL_UnloadObject(_this->gl_config.dll_handle); | ||
| 292 | _this->gl_config.dll_handle = NULL; | ||
| 293 | #endif | ||
| 294 | |||
| 295 | // Free OpenGL memory | ||
| 296 | SDL_free(_this->gl_data); | ||
| 297 | _this->gl_data = NULL; | ||
| 298 | } | ||
| 299 | |||
| 300 | static bool HasExtension(const char *extension, const char *extensions) | ||
| 301 | { | ||
| 302 | const char *start; | ||
| 303 | const char *where, *terminator; | ||
| 304 | |||
| 305 | if (!extensions) { | ||
| 306 | return false; | ||
| 307 | } | ||
| 308 | |||
| 309 | // Extension names should not have spaces. | ||
| 310 | where = SDL_strchr(extension, ' '); | ||
| 311 | if (where || *extension == '\0') { | ||
| 312 | return false; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* It takes a bit of care to be fool-proof about parsing the | ||
| 316 | * OpenGL extensions string. Don't be fooled by sub-strings, | ||
| 317 | * etc. */ | ||
| 318 | |||
| 319 | start = extensions; | ||
| 320 | |||
| 321 | for (;;) { | ||
| 322 | where = SDL_strstr(start, extension); | ||
| 323 | if (!where) { | ||
| 324 | break; | ||
| 325 | } | ||
| 326 | |||
| 327 | terminator = where + SDL_strlen(extension); | ||
| 328 | if (where == start || *(where - 1) == ' ') { | ||
| 329 | if (*terminator == ' ' || *terminator == '\0') { | ||
| 330 | return true; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | start = terminator; | ||
| 335 | } | ||
| 336 | return false; | ||
| 337 | } | ||
| 338 | |||
| 339 | static void X11_GL_InitExtensions(SDL_VideoDevice *_this) | ||
| 340 | { | ||
| 341 | Display *display = _this->internal->display; | ||
| 342 | const int screen = DefaultScreen(display); | ||
| 343 | XVisualInfo *vinfo = NULL; | ||
| 344 | Window w = 0; | ||
| 345 | GLXContext prev_ctx = 0; | ||
| 346 | GLXDrawable prev_drawable = 0; | ||
| 347 | GLXContext context = 0; | ||
| 348 | const char *(*glXQueryExtensionsStringFunc)(Display *, int); | ||
| 349 | const char *extensions; | ||
| 350 | |||
| 351 | vinfo = X11_GL_GetVisual(_this, display, screen, false); | ||
| 352 | if (vinfo) { | ||
| 353 | GLXContext (*glXGetCurrentContextFunc)(void) = | ||
| 354 | (GLXContext(*)(void)) | ||
| 355 | X11_GL_GetProcAddress(_this, "glXGetCurrentContext"); | ||
| 356 | |||
| 357 | GLXDrawable (*glXGetCurrentDrawableFunc)(void) = | ||
| 358 | (GLXDrawable(*)(void)) | ||
| 359 | X11_GL_GetProcAddress(_this, "glXGetCurrentDrawable"); | ||
| 360 | |||
| 361 | if (glXGetCurrentContextFunc && glXGetCurrentDrawableFunc) { | ||
| 362 | XSetWindowAttributes xattr; | ||
| 363 | prev_ctx = glXGetCurrentContextFunc(); | ||
| 364 | prev_drawable = glXGetCurrentDrawableFunc(); | ||
| 365 | |||
| 366 | xattr.background_pixel = 0; | ||
| 367 | xattr.border_pixel = 0; | ||
| 368 | xattr.colormap = | ||
| 369 | X11_XCreateColormap(display, RootWindow(display, screen), | ||
| 370 | vinfo->visual, AllocNone); | ||
| 371 | w = X11_XCreateWindow(display, RootWindow(display, screen), 0, 0, | ||
| 372 | 32, 32, 0, vinfo->depth, InputOutput, vinfo->visual, | ||
| 373 | (CWBackPixel | CWBorderPixel | CWColormap), &xattr); | ||
| 374 | |||
| 375 | context = _this->gl_data->glXCreateContext(display, vinfo, | ||
| 376 | NULL, True); | ||
| 377 | if (context) { | ||
| 378 | _this->gl_data->glXMakeCurrent(display, w, context); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | X11_XFree(vinfo); | ||
| 383 | } | ||
| 384 | |||
| 385 | glXQueryExtensionsStringFunc = | ||
| 386 | (const char *(*)(Display *, int))X11_GL_GetProcAddress(_this, | ||
| 387 | "glXQueryExtensionsString"); | ||
| 388 | if (glXQueryExtensionsStringFunc) { | ||
| 389 | extensions = glXQueryExtensionsStringFunc(display, screen); | ||
| 390 | } else { | ||
| 391 | extensions = NULL; | ||
| 392 | } | ||
| 393 | |||
| 394 | // Check for GLX_EXT_swap_control(_tear) | ||
| 395 | _this->gl_data->HAS_GLX_EXT_swap_control_tear = false; | ||
| 396 | if (HasExtension("GLX_EXT_swap_control", extensions)) { | ||
| 397 | _this->gl_data->glXSwapIntervalEXT = | ||
| 398 | (void (*)(Display *, GLXDrawable, int)) | ||
| 399 | X11_GL_GetProcAddress(_this, "glXSwapIntervalEXT"); | ||
| 400 | if (HasExtension("GLX_EXT_swap_control_tear", extensions)) { | ||
| 401 | _this->gl_data->HAS_GLX_EXT_swap_control_tear = true; | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | // Check for GLX_MESA_swap_control | ||
| 406 | if (HasExtension("GLX_MESA_swap_control", extensions)) { | ||
| 407 | _this->gl_data->glXSwapIntervalMESA = | ||
| 408 | (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalMESA"); | ||
| 409 | _this->gl_data->glXGetSwapIntervalMESA = | ||
| 410 | (int (*)(void))X11_GL_GetProcAddress(_this, | ||
| 411 | "glXGetSwapIntervalMESA"); | ||
| 412 | } | ||
| 413 | |||
| 414 | // Check for GLX_SGI_swap_control | ||
| 415 | if (HasExtension("GLX_SGI_swap_control", extensions)) { | ||
| 416 | _this->gl_data->glXSwapIntervalSGI = | ||
| 417 | (int (*)(int))X11_GL_GetProcAddress(_this, "glXSwapIntervalSGI"); | ||
| 418 | } | ||
| 419 | |||
| 420 | // Check for GLX_ARB_create_context | ||
| 421 | if (HasExtension("GLX_ARB_create_context", extensions)) { | ||
| 422 | _this->gl_data->glXCreateContextAttribsARB = | ||
| 423 | (GLXContext(*)(Display *, GLXFBConfig, GLXContext, Bool, const int *)) | ||
| 424 | X11_GL_GetProcAddress(_this, "glXCreateContextAttribsARB"); | ||
| 425 | _this->gl_data->glXChooseFBConfig = | ||
| 426 | (GLXFBConfig * (*)(Display *, int, const int *, int *)) | ||
| 427 | X11_GL_GetProcAddress(_this, "glXChooseFBConfig"); | ||
| 428 | _this->gl_data->glXGetVisualFromFBConfig = | ||
| 429 | (XVisualInfo * (*)(Display *, GLXFBConfig)) | ||
| 430 | X11_GL_GetProcAddress(_this, "glXGetVisualFromFBConfig"); | ||
| 431 | } | ||
| 432 | |||
| 433 | // Check for GLX_EXT_visual_rating | ||
| 434 | if (HasExtension("GLX_EXT_visual_rating", extensions)) { | ||
| 435 | _this->gl_data->HAS_GLX_EXT_visual_rating = true; | ||
| 436 | } | ||
| 437 | |||
| 438 | // Check for GLX_EXT_visual_info | ||
| 439 | if (HasExtension("GLX_EXT_visual_info", extensions)) { | ||
| 440 | _this->gl_data->HAS_GLX_EXT_visual_info = true; | ||
| 441 | } | ||
| 442 | |||
| 443 | // Check for GLX_EXT_create_context_es2_profile | ||
| 444 | if (HasExtension("GLX_EXT_create_context_es2_profile", extensions)) { | ||
| 445 | // this wants to call glGetString(), so it needs a context. | ||
| 446 | // !!! FIXME: it would be nice not to make a context here though! | ||
| 447 | if (context) { | ||
| 448 | SDL_GL_DeduceMaxSupportedESProfile( | ||
| 449 | &_this->gl_data->es_profile_max_supported_version.major, | ||
| 450 | &_this->gl_data->es_profile_max_supported_version.minor); | ||
| 451 | } | ||
| 452 | } | ||
| 453 | |||
| 454 | // Check for GLX_ARB_context_flush_control | ||
| 455 | if (HasExtension("GLX_ARB_context_flush_control", extensions)) { | ||
| 456 | _this->gl_data->HAS_GLX_ARB_context_flush_control = true; | ||
| 457 | } | ||
| 458 | |||
| 459 | // Check for GLX_ARB_create_context_robustness | ||
| 460 | if (HasExtension("GLX_ARB_create_context_robustness", extensions)) { | ||
| 461 | _this->gl_data->HAS_GLX_ARB_create_context_robustness = true; | ||
| 462 | } | ||
| 463 | |||
| 464 | // Check for GLX_ARB_create_context_no_error | ||
| 465 | if (HasExtension("GLX_ARB_create_context_no_error", extensions)) { | ||
| 466 | _this->gl_data->HAS_GLX_ARB_create_context_no_error = true; | ||
| 467 | } | ||
| 468 | |||
| 469 | if (context) { | ||
| 470 | _this->gl_data->glXMakeCurrent(display, None, NULL); | ||
| 471 | _this->gl_data->glXDestroyContext(display, context); | ||
| 472 | if (prev_ctx && prev_drawable) { | ||
| 473 | _this->gl_data->glXMakeCurrent(display, prev_drawable, prev_ctx); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | if (w) { | ||
| 478 | X11_XDestroyWindow(display, w); | ||
| 479 | } | ||
| 480 | X11_PumpEvents(_this); | ||
| 481 | } | ||
| 482 | |||
| 483 | /* glXChooseVisual and glXChooseFBConfig have some small differences in | ||
| 484 | * the attribute encoding, it can be chosen with the for_FBConfig parameter. | ||
| 485 | * Some targets fail if you use GLX_X_VISUAL_TYPE_EXT/GLX_DIRECT_COLOR_EXT, | ||
| 486 | * so it gets specified last if used and is pointed to by *_pvistypeattr. | ||
| 487 | * In case of failure, if that pointer is not NULL, set that pointer to None | ||
| 488 | * and try again. | ||
| 489 | */ | ||
| 490 | static int X11_GL_GetAttributes(SDL_VideoDevice *_this, Display *display, int screen, int *attribs, int size, Bool for_FBConfig, int **_pvistypeattr, bool transparent) | ||
| 491 | { | ||
| 492 | int i = 0; | ||
| 493 | const int MAX_ATTRIBUTES = 64; | ||
| 494 | int *pvistypeattr = NULL; | ||
| 495 | |||
| 496 | // assert buffer is large enough to hold all SDL attributes. | ||
| 497 | SDL_assert(size >= MAX_ATTRIBUTES); | ||
| 498 | |||
| 499 | // Setup our GLX attributes according to the gl_config. | ||
| 500 | if (for_FBConfig) { | ||
| 501 | attribs[i++] = GLX_RENDER_TYPE; | ||
| 502 | if (_this->gl_config.floatbuffers) { | ||
| 503 | attribs[i++] = GLX_RGBA_FLOAT_BIT_ARB; | ||
| 504 | } else { | ||
| 505 | attribs[i++] = GLX_RGBA_BIT; | ||
| 506 | } | ||
| 507 | } else { | ||
| 508 | attribs[i++] = GLX_RGBA; | ||
| 509 | } | ||
| 510 | attribs[i++] = GLX_RED_SIZE; | ||
| 511 | attribs[i++] = _this->gl_config.red_size; | ||
| 512 | attribs[i++] = GLX_GREEN_SIZE; | ||
| 513 | attribs[i++] = _this->gl_config.green_size; | ||
| 514 | attribs[i++] = GLX_BLUE_SIZE; | ||
| 515 | attribs[i++] = _this->gl_config.blue_size; | ||
| 516 | |||
| 517 | if (_this->gl_config.alpha_size) { | ||
| 518 | attribs[i++] = GLX_ALPHA_SIZE; | ||
| 519 | attribs[i++] = _this->gl_config.alpha_size; | ||
| 520 | } | ||
| 521 | |||
| 522 | if (_this->gl_config.double_buffer) { | ||
| 523 | attribs[i++] = GLX_DOUBLEBUFFER; | ||
| 524 | if (for_FBConfig) { | ||
| 525 | attribs[i++] = True; | ||
| 526 | } | ||
| 527 | } | ||
| 528 | |||
| 529 | attribs[i++] = GLX_DEPTH_SIZE; | ||
| 530 | attribs[i++] = _this->gl_config.depth_size; | ||
| 531 | |||
| 532 | if (_this->gl_config.stencil_size) { | ||
| 533 | attribs[i++] = GLX_STENCIL_SIZE; | ||
| 534 | attribs[i++] = _this->gl_config.stencil_size; | ||
| 535 | } | ||
| 536 | |||
| 537 | if (_this->gl_config.accum_red_size) { | ||
| 538 | attribs[i++] = GLX_ACCUM_RED_SIZE; | ||
| 539 | attribs[i++] = _this->gl_config.accum_red_size; | ||
| 540 | } | ||
| 541 | |||
| 542 | if (_this->gl_config.accum_green_size) { | ||
| 543 | attribs[i++] = GLX_ACCUM_GREEN_SIZE; | ||
| 544 | attribs[i++] = _this->gl_config.accum_green_size; | ||
| 545 | } | ||
| 546 | |||
| 547 | if (_this->gl_config.accum_blue_size) { | ||
| 548 | attribs[i++] = GLX_ACCUM_BLUE_SIZE; | ||
| 549 | attribs[i++] = _this->gl_config.accum_blue_size; | ||
| 550 | } | ||
| 551 | |||
| 552 | if (_this->gl_config.accum_alpha_size) { | ||
| 553 | attribs[i++] = GLX_ACCUM_ALPHA_SIZE; | ||
| 554 | attribs[i++] = _this->gl_config.accum_alpha_size; | ||
| 555 | } | ||
| 556 | |||
| 557 | if (_this->gl_config.stereo) { | ||
| 558 | attribs[i++] = GLX_STEREO; | ||
| 559 | if (for_FBConfig) { | ||
| 560 | attribs[i++] = True; | ||
| 561 | } | ||
| 562 | } | ||
| 563 | |||
| 564 | if (_this->gl_config.multisamplebuffers) { | ||
| 565 | attribs[i++] = GLX_SAMPLE_BUFFERS_ARB; | ||
| 566 | attribs[i++] = _this->gl_config.multisamplebuffers; | ||
| 567 | } | ||
| 568 | |||
| 569 | if (_this->gl_config.multisamplesamples) { | ||
| 570 | attribs[i++] = GLX_SAMPLES_ARB; | ||
| 571 | attribs[i++] = _this->gl_config.multisamplesamples; | ||
| 572 | } | ||
| 573 | |||
| 574 | if (_this->gl_config.floatbuffers) { | ||
| 575 | attribs[i++] = GLX_RENDER_TYPE; | ||
| 576 | attribs[i++] = GLX_RGBA_FLOAT_TYPE_ARB; | ||
| 577 | } | ||
| 578 | |||
| 579 | if (_this->gl_config.framebuffer_srgb_capable) { | ||
| 580 | attribs[i++] = GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB; | ||
| 581 | attribs[i++] = True; // always needed, for_FBConfig or not! | ||
| 582 | } | ||
| 583 | |||
| 584 | if (_this->gl_config.accelerated >= 0 && | ||
| 585 | _this->gl_data->HAS_GLX_EXT_visual_rating) { | ||
| 586 | attribs[i++] = GLX_VISUAL_CAVEAT_EXT; | ||
| 587 | attribs[i++] = _this->gl_config.accelerated ? GLX_NONE_EXT : GLX_SLOW_VISUAL_EXT; | ||
| 588 | } | ||
| 589 | |||
| 590 | // Un-wanted when we request a transparent buffer | ||
| 591 | if (!transparent) { | ||
| 592 | /* If we're supposed to use DirectColor visuals, and we've got the | ||
| 593 | EXT_visual_info extension, then add GLX_X_VISUAL_TYPE_EXT. */ | ||
| 594 | if (X11_UseDirectColorVisuals() && _this->gl_data->HAS_GLX_EXT_visual_info) { | ||
| 595 | pvistypeattr = &attribs[i]; | ||
| 596 | attribs[i++] = GLX_X_VISUAL_TYPE_EXT; | ||
| 597 | attribs[i++] = GLX_DIRECT_COLOR_EXT; | ||
| 598 | } | ||
| 599 | } | ||
| 600 | |||
| 601 | attribs[i++] = None; | ||
| 602 | |||
| 603 | SDL_assert(i <= MAX_ATTRIBUTES); | ||
| 604 | |||
| 605 | if (_pvistypeattr) { | ||
| 606 | *_pvistypeattr = pvistypeattr; | ||
| 607 | } | ||
| 608 | |||
| 609 | return i; | ||
| 610 | } | ||
| 611 | |||
| 612 | //get the first transparent Visual | ||
| 613 | static XVisualInfo* X11_GL_GetTransparentVisualInfo(Display *display, int screen) | ||
| 614 | { | ||
| 615 | XVisualInfo* visualinfo = NULL; | ||
| 616 | XVisualInfo vi_in; | ||
| 617 | int out_count = 0; | ||
| 618 | |||
| 619 | vi_in.screen = screen; | ||
| 620 | visualinfo = X11_XGetVisualInfo(display, VisualScreenMask, &vi_in, &out_count); | ||
| 621 | if (visualinfo != NULL) { | ||
| 622 | int i = 0; | ||
| 623 | for (i = 0; i < out_count; i++) { | ||
| 624 | XVisualInfo* v = &visualinfo[i]; | ||
| 625 | Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v); | ||
| 626 | if (SDL_ISPIXELFORMAT_ALPHA(format)) { | ||
| 627 | vi_in.screen = screen; | ||
| 628 | vi_in.visualid = v->visualid; | ||
| 629 | X11_XFree(visualinfo); | ||
| 630 | visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count); | ||
| 631 | break; | ||
| 632 | } | ||
| 633 | } | ||
| 634 | } | ||
| 635 | return visualinfo; | ||
| 636 | } | ||
| 637 | |||
| 638 | XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent) | ||
| 639 | { | ||
| 640 | // 64 seems nice. | ||
| 641 | int attribs[64]; | ||
| 642 | XVisualInfo *vinfo = NULL; | ||
| 643 | int *pvistypeattr = NULL; | ||
| 644 | |||
| 645 | if (!_this->gl_data) { | ||
| 646 | // The OpenGL library wasn't loaded, SDL_GetError() should have info | ||
| 647 | return NULL; | ||
| 648 | } | ||
| 649 | |||
| 650 | if (_this->gl_data->glXChooseFBConfig && | ||
| 651 | _this->gl_data->glXGetVisualFromFBConfig) { | ||
| 652 | GLXFBConfig *framebuffer_config = NULL; | ||
| 653 | int fbcount = 0; | ||
| 654 | |||
| 655 | X11_GL_GetAttributes(_this, display, screen, attribs, 64, true, &pvistypeattr, transparent); | ||
| 656 | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount); | ||
| 657 | if (!framebuffer_config && (pvistypeattr != NULL)) { | ||
| 658 | *pvistypeattr = None; | ||
| 659 | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, screen, attribs, &fbcount); | ||
| 660 | } | ||
| 661 | |||
| 662 | if (transparent) { | ||
| 663 | // Return the first transparent Visual | ||
| 664 | int i; | ||
| 665 | for (i = 0; i < fbcount; i++) { | ||
| 666 | Uint32 format; | ||
| 667 | vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]); | ||
| 668 | format = X11_GetPixelFormatFromVisualInfo(display, vinfo); | ||
| 669 | if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found! | ||
| 670 | X11_XFree(framebuffer_config); | ||
| 671 | framebuffer_config = NULL; | ||
| 672 | break; | ||
| 673 | } | ||
| 674 | X11_XFree(vinfo); | ||
| 675 | vinfo = NULL; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | if (framebuffer_config) { | ||
| 680 | vinfo = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[0]); | ||
| 681 | } | ||
| 682 | |||
| 683 | X11_XFree(framebuffer_config); | ||
| 684 | } | ||
| 685 | |||
| 686 | if (!vinfo) { | ||
| 687 | X11_GL_GetAttributes(_this, display, screen, attribs, 64, false, &pvistypeattr, transparent); | ||
| 688 | vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs); | ||
| 689 | |||
| 690 | if (!vinfo && (pvistypeattr != NULL)) { | ||
| 691 | *pvistypeattr = None; | ||
| 692 | vinfo = _this->gl_data->glXChooseVisual(display, screen, attribs); | ||
| 693 | } | ||
| 694 | } | ||
| 695 | |||
| 696 | if (transparent && vinfo) { | ||
| 697 | Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo); | ||
| 698 | if (!SDL_ISPIXELFORMAT_ALPHA(format)) { | ||
| 699 | // not transparent! | ||
| 700 | XVisualInfo* visualinfo = X11_GL_GetTransparentVisualInfo(display, screen); | ||
| 701 | if (visualinfo != NULL) { | ||
| 702 | X11_XFree(vinfo); | ||
| 703 | vinfo = visualinfo; | ||
| 704 | } | ||
| 705 | } | ||
| 706 | } | ||
| 707 | |||
| 708 | if (!vinfo) { | ||
| 709 | SDL_SetError("Couldn't find matching GLX visual"); | ||
| 710 | } | ||
| 711 | return vinfo; | ||
| 712 | } | ||
| 713 | |||
| 714 | static int (*handler)(Display *, XErrorEvent *) = NULL; | ||
| 715 | static const char *errorHandlerOperation = NULL; | ||
| 716 | static int errorBase = 0; | ||
| 717 | static int errorCode = 0; | ||
| 718 | static int X11_GL_ErrorHandler(Display *d, XErrorEvent *e) | ||
| 719 | { | ||
| 720 | char *x11_error = NULL; | ||
| 721 | char x11_error_locale[256]; | ||
| 722 | |||
| 723 | errorCode = e->error_code; | ||
| 724 | if (X11_XGetErrorText(d, errorCode, x11_error_locale, sizeof(x11_error_locale)) == Success) { | ||
| 725 | x11_error = SDL_iconv_string("UTF-8", "", x11_error_locale, SDL_strlen(x11_error_locale) + 1); | ||
| 726 | } | ||
| 727 | |||
| 728 | if (x11_error) { | ||
| 729 | SDL_SetError("Could not %s: %s", errorHandlerOperation, x11_error); | ||
| 730 | SDL_free(x11_error); | ||
| 731 | } else { | ||
| 732 | SDL_SetError("Could not %s: %i (Base %i)", errorHandlerOperation, errorCode, errorBase); | ||
| 733 | } | ||
| 734 | |||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | |||
| 738 | bool X11_GL_UseEGL(SDL_VideoDevice *_this) | ||
| 739 | { | ||
| 740 | SDL_assert(_this->gl_data != NULL); | ||
| 741 | if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) { | ||
| 742 | // use of EGL has been requested, even for desktop GL | ||
| 743 | return true; | ||
| 744 | } | ||
| 745 | |||
| 746 | SDL_assert(_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES); | ||
| 747 | return (SDL_GetHintBoolean(SDL_HINT_OPENGL_ES_DRIVER, false) || _this->gl_config.major_version == 1 // No GLX extension for OpenGL ES 1.x profiles. | ||
| 748 | || _this->gl_config.major_version > _this->gl_data->es_profile_max_supported_version.major || (_this->gl_config.major_version == _this->gl_data->es_profile_max_supported_version.major && _this->gl_config.minor_version > _this->gl_data->es_profile_max_supported_version.minor)); | ||
| 749 | } | ||
| 750 | |||
| 751 | SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 752 | { | ||
| 753 | SDL_WindowData *data = window->internal; | ||
| 754 | Display *display = data->videodata->display; | ||
| 755 | int screen = SDL_GetDisplayDriverDataForWindow(window)->screen; | ||
| 756 | XWindowAttributes xattr; | ||
| 757 | XVisualInfo v, *vinfo; | ||
| 758 | int n; | ||
| 759 | SDL_GLContext context = NULL; | ||
| 760 | GLXContext share_context; | ||
| 761 | const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false; | ||
| 762 | |||
| 763 | if (_this->gl_config.share_with_current_context) { | ||
| 764 | share_context = (GLXContext)SDL_GL_GetCurrentContext(); | ||
| 765 | } else { | ||
| 766 | share_context = NULL; | ||
| 767 | } | ||
| 768 | |||
| 769 | // We do this to create a clean separation between X and GLX errors. | ||
| 770 | X11_XSync(display, False); | ||
| 771 | errorHandlerOperation = "create GL context"; | ||
| 772 | errorBase = _this->gl_data->errorBase; | ||
| 773 | errorCode = Success; | ||
| 774 | handler = X11_XSetErrorHandler(X11_GL_ErrorHandler); | ||
| 775 | X11_XGetWindowAttributes(display, data->xwindow, &xattr); | ||
| 776 | v.screen = screen; | ||
| 777 | v.visualid = X11_XVisualIDFromVisual(xattr.visual); | ||
| 778 | vinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &v, &n); | ||
| 779 | if (vinfo) { | ||
| 780 | if (_this->gl_config.major_version < 3 && | ||
| 781 | _this->gl_config.profile_mask == 0 && | ||
| 782 | _this->gl_config.flags == 0 && !transparent) { | ||
| 783 | // Create legacy context | ||
| 784 | context = | ||
| 785 | (SDL_GLContext)_this->gl_data->glXCreateContext(display, vinfo, share_context, True); | ||
| 786 | } else { | ||
| 787 | // max 14 attributes plus terminator | ||
| 788 | int attribs[15] = { | ||
| 789 | GLX_CONTEXT_MAJOR_VERSION_ARB, | ||
| 790 | _this->gl_config.major_version, | ||
| 791 | GLX_CONTEXT_MINOR_VERSION_ARB, | ||
| 792 | _this->gl_config.minor_version, | ||
| 793 | 0 | ||
| 794 | }; | ||
| 795 | int iattr = 4; | ||
| 796 | |||
| 797 | // SDL profile bits match GLX profile bits | ||
| 798 | if (_this->gl_config.profile_mask != 0) { | ||
| 799 | attribs[iattr++] = GLX_CONTEXT_PROFILE_MASK_ARB; | ||
| 800 | attribs[iattr++] = _this->gl_config.profile_mask; | ||
| 801 | } | ||
| 802 | |||
| 803 | // SDL flags match GLX flags | ||
| 804 | if (_this->gl_config.flags != 0) { | ||
| 805 | attribs[iattr++] = GLX_CONTEXT_FLAGS_ARB; | ||
| 806 | attribs[iattr++] = _this->gl_config.flags; | ||
| 807 | } | ||
| 808 | |||
| 809 | // only set if glx extension is available and not the default setting | ||
| 810 | if ((_this->gl_data->HAS_GLX_ARB_context_flush_control) && (_this->gl_config.release_behavior == 0)) { | ||
| 811 | attribs[iattr++] = GLX_CONTEXT_RELEASE_BEHAVIOR_ARB; | ||
| 812 | attribs[iattr++] = | ||
| 813 | _this->gl_config.release_behavior ? GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB : GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB; | ||
| 814 | } | ||
| 815 | |||
| 816 | // only set if glx extension is available and not the default setting | ||
| 817 | if ((_this->gl_data->HAS_GLX_ARB_create_context_robustness) && (_this->gl_config.reset_notification != 0)) { | ||
| 818 | attribs[iattr++] = GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB; | ||
| 819 | attribs[iattr++] = | ||
| 820 | _this->gl_config.reset_notification ? GLX_LOSE_CONTEXT_ON_RESET_ARB : GLX_NO_RESET_NOTIFICATION_ARB; | ||
| 821 | } | ||
| 822 | |||
| 823 | // only set if glx extension is available and not the default setting | ||
| 824 | if ((_this->gl_data->HAS_GLX_ARB_create_context_no_error) && (_this->gl_config.no_error != 0)) { | ||
| 825 | attribs[iattr++] = GLX_CONTEXT_OPENGL_NO_ERROR_ARB; | ||
| 826 | attribs[iattr++] = _this->gl_config.no_error; | ||
| 827 | } | ||
| 828 | |||
| 829 | attribs[iattr++] = 0; | ||
| 830 | |||
| 831 | // Get a pointer to the context creation function for GL 3.0 | ||
| 832 | if (!_this->gl_data->glXCreateContextAttribsARB) { | ||
| 833 | SDL_SetError("OpenGL 3.0 and later are not supported by this system"); | ||
| 834 | } else { | ||
| 835 | int glxAttribs[64]; | ||
| 836 | |||
| 837 | // Create a GL 3.x context | ||
| 838 | GLXFBConfig *framebuffer_config = NULL; | ||
| 839 | int fbcount = 0; | ||
| 840 | int *pvistypeattr = NULL; | ||
| 841 | |||
| 842 | X11_GL_GetAttributes(_this, display, screen, glxAttribs, 64, true, &pvistypeattr, transparent); | ||
| 843 | |||
| 844 | if (_this->gl_data->glXChooseFBConfig) { | ||
| 845 | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, | ||
| 846 | DefaultScreen(display), glxAttribs, | ||
| 847 | &fbcount); | ||
| 848 | |||
| 849 | if (!framebuffer_config && (pvistypeattr != NULL)) { | ||
| 850 | *pvistypeattr = None; | ||
| 851 | framebuffer_config = _this->gl_data->glXChooseFBConfig(display, | ||
| 852 | DefaultScreen(display), glxAttribs, | ||
| 853 | &fbcount); | ||
| 854 | } | ||
| 855 | |||
| 856 | if (transparent && (framebuffer_config != NULL)) { | ||
| 857 | int i; | ||
| 858 | for (i = 0; i < fbcount; i++) { | ||
| 859 | XVisualInfo* vinfo_temp = _this->gl_data->glXGetVisualFromFBConfig(display, framebuffer_config[i]); | ||
| 860 | if ( vinfo_temp != NULL) { | ||
| 861 | Uint32 format = X11_GetPixelFormatFromVisualInfo(display, vinfo_temp); | ||
| 862 | if (SDL_ISPIXELFORMAT_ALPHA(format)) { | ||
| 863 | // found! | ||
| 864 | context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display, | ||
| 865 | framebuffer_config[i], | ||
| 866 | share_context, True, attribs); | ||
| 867 | X11_XFree(framebuffer_config); | ||
| 868 | framebuffer_config = NULL; | ||
| 869 | X11_XFree(vinfo_temp); | ||
| 870 | break; | ||
| 871 | } | ||
| 872 | X11_XFree(vinfo_temp); | ||
| 873 | } | ||
| 874 | } | ||
| 875 | } | ||
| 876 | if (framebuffer_config) { | ||
| 877 | context = (SDL_GLContext)_this->gl_data->glXCreateContextAttribsARB(display, | ||
| 878 | framebuffer_config[0], | ||
| 879 | share_context, True, attribs); | ||
| 880 | X11_XFree(framebuffer_config); | ||
| 881 | } | ||
| 882 | } | ||
| 883 | } | ||
| 884 | } | ||
| 885 | X11_XFree(vinfo); | ||
| 886 | } | ||
| 887 | X11_XSync(display, False); | ||
| 888 | X11_XSetErrorHandler(handler); | ||
| 889 | |||
| 890 | if (!context) { | ||
| 891 | if (errorCode == Success) { | ||
| 892 | SDL_SetError("Could not create GL context"); | ||
| 893 | } | ||
| 894 | return NULL; | ||
| 895 | } | ||
| 896 | |||
| 897 | if (!X11_GL_MakeCurrent(_this, window, context)) { | ||
| 898 | X11_GL_DestroyContext(_this, context); | ||
| 899 | return NULL; | ||
| 900 | } | ||
| 901 | |||
| 902 | return context; | ||
| 903 | } | ||
| 904 | |||
| 905 | bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context) | ||
| 906 | { | ||
| 907 | Display *display = _this->internal->display; | ||
| 908 | Window drawable = | ||
| 909 | (context ? window->internal->xwindow : None); | ||
| 910 | GLXContext glx_context = (GLXContext)context; | ||
| 911 | int rc; | ||
| 912 | |||
| 913 | if (!_this->gl_data) { | ||
| 914 | return SDL_SetError("OpenGL not initialized"); | ||
| 915 | } | ||
| 916 | |||
| 917 | // We do this to create a clean separation between X and GLX errors. | ||
| 918 | X11_XSync(display, False); | ||
| 919 | errorHandlerOperation = "make GL context current"; | ||
| 920 | errorBase = _this->gl_data->errorBase; | ||
| 921 | errorCode = Success; | ||
| 922 | handler = X11_XSetErrorHandler(X11_GL_ErrorHandler); | ||
| 923 | rc = _this->gl_data->glXMakeCurrent(display, drawable, glx_context); | ||
| 924 | X11_XSetErrorHandler(handler); | ||
| 925 | |||
| 926 | if (errorCode != Success) { // uhoh, an X error was thrown! | ||
| 927 | return false; // the error handler called SDL_SetError() already. | ||
| 928 | } else if (!rc) { // glXMakeCurrent() failed without throwing an X error | ||
| 929 | return SDL_SetError("Unable to make GL context current"); | ||
| 930 | } | ||
| 931 | |||
| 932 | return true; | ||
| 933 | } | ||
| 934 | |||
| 935 | /* | ||
| 936 | 0 is a valid argument to glXSwapInterval(MESA|EXT) and setting it to 0 | ||
| 937 | will undo the effect of a previous call with a value that is greater | ||
| 938 | than zero (or at least that is what the docs say). OTOH, 0 is an invalid | ||
| 939 | argument to glXSwapIntervalSGI and it returns an error if you call it | ||
| 940 | with 0 as an argument. | ||
| 941 | */ | ||
| 942 | |||
| 943 | static int swapinterval = 0; | ||
| 944 | bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval) | ||
| 945 | { | ||
| 946 | bool result = false; | ||
| 947 | |||
| 948 | if ((interval < 0) && (!_this->gl_data->HAS_GLX_EXT_swap_control_tear)) { | ||
| 949 | return SDL_SetError("Negative swap interval unsupported in this GL"); | ||
| 950 | } else if (_this->gl_data->glXSwapIntervalEXT) { | ||
| 951 | Display *display = _this->internal->display; | ||
| 952 | const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal; | ||
| 953 | |||
| 954 | Window drawable = windowdata->xwindow; | ||
| 955 | |||
| 956 | /* | ||
| 957 | * This is a workaround for a bug in NVIDIA drivers. Bug has been reported | ||
| 958 | * and will be fixed in a future release (probably 319.xx). | ||
| 959 | * | ||
| 960 | * There's a bug where glXSetSwapIntervalEXT ignores updates because | ||
| 961 | * it has the wrong value cached. To work around it, we just run a no-op | ||
| 962 | * update to the current value. | ||
| 963 | */ | ||
| 964 | int currentInterval = 0; | ||
| 965 | X11_GL_GetSwapInterval(_this, ¤tInterval); | ||
| 966 | _this->gl_data->glXSwapIntervalEXT(display, drawable, currentInterval); | ||
| 967 | _this->gl_data->glXSwapIntervalEXT(display, drawable, interval); | ||
| 968 | result = true; | ||
| 969 | swapinterval = interval; | ||
| 970 | } else if (_this->gl_data->glXSwapIntervalMESA) { | ||
| 971 | const int rc = _this->gl_data->glXSwapIntervalMESA(interval); | ||
| 972 | if (rc == 0) { | ||
| 973 | swapinterval = interval; | ||
| 974 | result = true; | ||
| 975 | } else { | ||
| 976 | result = SDL_SetError("glXSwapIntervalMESA failed"); | ||
| 977 | } | ||
| 978 | } else if (_this->gl_data->glXSwapIntervalSGI) { | ||
| 979 | const int rc = _this->gl_data->glXSwapIntervalSGI(interval); | ||
| 980 | if (rc == 0) { | ||
| 981 | swapinterval = interval; | ||
| 982 | result = true; | ||
| 983 | } else { | ||
| 984 | result = SDL_SetError("glXSwapIntervalSGI failed"); | ||
| 985 | } | ||
| 986 | } else { | ||
| 987 | return SDL_Unsupported(); | ||
| 988 | } | ||
| 989 | return result; | ||
| 990 | } | ||
| 991 | |||
| 992 | static SDL_GLSwapIntervalTearBehavior CheckSwapIntervalTearBehavior(SDL_VideoDevice *_this, Window drawable, unsigned int current_val, unsigned int current_allow_late) | ||
| 993 | { | ||
| 994 | /* Mesa and Nvidia interpret GLX_EXT_swap_control_tear differently, as of this writing, so | ||
| 995 | figure out which behavior we have. | ||
| 996 | Technical details: https://github.com/libsdl-org/SDL/issues/8004#issuecomment-1819603282 */ | ||
| 997 | if (_this->gl_data->swap_interval_tear_behavior == SDL_SWAPINTERVALTEAR_UNTESTED) { | ||
| 998 | if (!_this->gl_data->HAS_GLX_EXT_swap_control_tear) { | ||
| 999 | _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN; | ||
| 1000 | } else { | ||
| 1001 | Display *display = _this->internal->display; | ||
| 1002 | unsigned int allow_late_swap_tearing = 22; | ||
| 1003 | int original_val = (int) current_val; | ||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * This is a workaround for a bug in NVIDIA drivers. Bug has been reported | ||
| 1007 | * and will be fixed in a future release (probably 319.xx). | ||
| 1008 | * | ||
| 1009 | * There's a bug where glXSetSwapIntervalEXT ignores updates because | ||
| 1010 | * it has the wrong value cached. To work around it, we just run a no-op | ||
| 1011 | * update to the current value. | ||
| 1012 | */ | ||
| 1013 | _this->gl_data->glXSwapIntervalEXT(display, drawable, current_val); | ||
| 1014 | |||
| 1015 | // set it to no swap interval and see how it affects GLX_LATE_SWAPS_TEAR_EXT... | ||
| 1016 | _this->gl_data->glXSwapIntervalEXT(display, drawable, 0); | ||
| 1017 | _this->gl_data->glXQueryDrawable(display, drawable, GLX_LATE_SWAPS_TEAR_EXT, &allow_late_swap_tearing); | ||
| 1018 | |||
| 1019 | if (allow_late_swap_tearing == 0) { // GLX_LATE_SWAPS_TEAR_EXT says whether late swapping is currently in use | ||
| 1020 | _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_NVIDIA; | ||
| 1021 | if (current_allow_late) { | ||
| 1022 | original_val = -original_val; | ||
| 1023 | } | ||
| 1024 | } else if (allow_late_swap_tearing == 1) { // GLX_LATE_SWAPS_TEAR_EXT says whether the Drawable can use late swapping at all | ||
| 1025 | _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_MESA; | ||
| 1026 | } else { // unexpected outcome! | ||
| 1027 | _this->gl_data->swap_interval_tear_behavior = SDL_SWAPINTERVALTEAR_UNKNOWN; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | // set us back to what it was originally... | ||
| 1031 | _this->gl_data->glXSwapIntervalEXT(display, drawable, original_val); | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | return _this->gl_data->swap_interval_tear_behavior; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | |||
| 1039 | bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval) | ||
| 1040 | { | ||
| 1041 | if (_this->gl_data->glXSwapIntervalEXT) { | ||
| 1042 | Display *display = _this->internal->display; | ||
| 1043 | const SDL_WindowData *windowdata = SDL_GL_GetCurrentWindow()->internal; | ||
| 1044 | Window drawable = windowdata->xwindow; | ||
| 1045 | unsigned int allow_late_swap_tearing = 0; | ||
| 1046 | unsigned int val = 0; | ||
| 1047 | |||
| 1048 | if (_this->gl_data->HAS_GLX_EXT_swap_control_tear) { | ||
| 1049 | allow_late_swap_tearing = 22; // set this to nonsense. | ||
| 1050 | _this->gl_data->glXQueryDrawable(display, drawable, | ||
| 1051 | GLX_LATE_SWAPS_TEAR_EXT, | ||
| 1052 | &allow_late_swap_tearing); | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | _this->gl_data->glXQueryDrawable(display, drawable, | ||
| 1056 | GLX_SWAP_INTERVAL_EXT, &val); | ||
| 1057 | |||
| 1058 | *interval = (int)val; | ||
| 1059 | |||
| 1060 | switch (CheckSwapIntervalTearBehavior(_this, drawable, val, allow_late_swap_tearing)) { | ||
| 1061 | case SDL_SWAPINTERVALTEAR_MESA: | ||
| 1062 | *interval = (int)val; // unsigned int cast to signed that generates negative value if necessary. | ||
| 1063 | break; | ||
| 1064 | |||
| 1065 | case SDL_SWAPINTERVALTEAR_NVIDIA: | ||
| 1066 | default: | ||
| 1067 | if ((allow_late_swap_tearing) && (val > 0)) { | ||
| 1068 | *interval = -((int)val); | ||
| 1069 | } | ||
| 1070 | break; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | return true; | ||
| 1074 | } else if (_this->gl_data->glXGetSwapIntervalMESA) { | ||
| 1075 | int val = _this->gl_data->glXGetSwapIntervalMESA(); | ||
| 1076 | if (val == GLX_BAD_CONTEXT) { | ||
| 1077 | return SDL_SetError("GLX_BAD_CONTEXT"); | ||
| 1078 | } | ||
| 1079 | *interval = val; | ||
| 1080 | return true; | ||
| 1081 | } else { | ||
| 1082 | *interval = swapinterval; | ||
| 1083 | return true; | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1088 | { | ||
| 1089 | SDL_WindowData *data = window->internal; | ||
| 1090 | Display *display = data->videodata->display; | ||
| 1091 | |||
| 1092 | _this->gl_data->glXSwapBuffers(display, data->xwindow); | ||
| 1093 | |||
| 1094 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 1095 | X11_HandlePresent(data->window); | ||
| 1096 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 1097 | |||
| 1098 | return true; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context) | ||
| 1102 | { | ||
| 1103 | Display *display = _this->internal->display; | ||
| 1104 | GLXContext glx_context = (GLXContext)context; | ||
| 1105 | |||
| 1106 | if (!_this->gl_data) { | ||
| 1107 | return true; | ||
| 1108 | } | ||
| 1109 | _this->gl_data->glXDestroyContext(display, glx_context); | ||
| 1110 | X11_XSync(display, False); | ||
| 1111 | return true; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | #endif // SDL_VIDEO_OPENGL_GLX | ||
| 1115 | |||
| 1116 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h new file mode 100644 index 0000000..24db485 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengl.h | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11opengl_h_ | ||
| 24 | #define SDL_x11opengl_h_ | ||
| 25 | |||
| 26 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 27 | #include <SDL3/SDL_opengl.h> | ||
| 28 | #include <GL/glx.h> | ||
| 29 | |||
| 30 | typedef void (*__GLXextFuncPtr)(void); | ||
| 31 | |||
| 32 | typedef enum SDL_GLSwapIntervalTearBehavior | ||
| 33 | { | ||
| 34 | SDL_SWAPINTERVALTEAR_UNTESTED, | ||
| 35 | SDL_SWAPINTERVALTEAR_UNKNOWN, | ||
| 36 | SDL_SWAPINTERVALTEAR_MESA, | ||
| 37 | SDL_SWAPINTERVALTEAR_NVIDIA | ||
| 38 | } SDL_GLSwapIntervalTearBehavior; | ||
| 39 | |||
| 40 | struct SDL_GLDriverData | ||
| 41 | { | ||
| 42 | int errorBase, eventBase; | ||
| 43 | |||
| 44 | bool HAS_GLX_EXT_visual_rating; | ||
| 45 | bool HAS_GLX_EXT_visual_info; | ||
| 46 | bool HAS_GLX_EXT_swap_control_tear; | ||
| 47 | bool HAS_GLX_ARB_context_flush_control; | ||
| 48 | bool HAS_GLX_ARB_create_context_robustness; | ||
| 49 | bool HAS_GLX_ARB_create_context_no_error; | ||
| 50 | |||
| 51 | /* Max version of OpenGL ES context that can be created if the | ||
| 52 | implementation supports GLX_EXT_create_context_es2_profile. | ||
| 53 | major = minor = 0 when unsupported. | ||
| 54 | */ | ||
| 55 | struct | ||
| 56 | { | ||
| 57 | int major; | ||
| 58 | int minor; | ||
| 59 | } es_profile_max_supported_version; | ||
| 60 | |||
| 61 | SDL_GLSwapIntervalTearBehavior swap_interval_tear_behavior; | ||
| 62 | |||
| 63 | Bool (*glXQueryExtension)(Display *, int *, int *); | ||
| 64 | __GLXextFuncPtr (*glXGetProcAddress)(const GLubyte *); | ||
| 65 | XVisualInfo *(*glXChooseVisual)(Display *, int, int *); | ||
| 66 | GLXContext (*glXCreateContext)(Display *, XVisualInfo *, GLXContext, Bool); | ||
| 67 | GLXContext (*glXCreateContextAttribsARB)(Display *, GLXFBConfig, GLXContext, Bool, const int *); | ||
| 68 | GLXFBConfig *(*glXChooseFBConfig)(Display *, int, const int *, int *); | ||
| 69 | XVisualInfo *(*glXGetVisualFromFBConfig)(Display *, GLXFBConfig); | ||
| 70 | void (*glXDestroyContext)(Display *, GLXContext); | ||
| 71 | Bool (*glXMakeCurrent)(Display *, GLXDrawable, GLXContext); | ||
| 72 | void (*glXSwapBuffers)(Display *, GLXDrawable); | ||
| 73 | void (*glXQueryDrawable)(Display *, GLXDrawable, int, unsigned int *); | ||
| 74 | void (*glXSwapIntervalEXT)(Display *, GLXDrawable, int); | ||
| 75 | int (*glXSwapIntervalSGI)(int); | ||
| 76 | int (*glXSwapIntervalMESA)(int); | ||
| 77 | int (*glXGetSwapIntervalMESA)(void); | ||
| 78 | }; | ||
| 79 | |||
| 80 | // OpenGL functions | ||
| 81 | extern bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path); | ||
| 82 | extern SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc); | ||
| 83 | extern void X11_GL_UnloadLibrary(SDL_VideoDevice *_this); | ||
| 84 | extern bool X11_GL_UseEGL(SDL_VideoDevice *_this); | ||
| 85 | extern XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent); | ||
| 86 | extern SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 87 | extern bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, | ||
| 88 | SDL_GLContext context); | ||
| 89 | extern bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval); | ||
| 90 | extern bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval); | ||
| 91 | extern bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 92 | extern bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context); | ||
| 93 | |||
| 94 | #endif // SDL_VIDEO_OPENGL_GLX | ||
| 95 | |||
| 96 | #endif // SDL_x11opengl_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c new file mode 100644 index 0000000..9c1910f --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.c | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_OPENGL_EGL) | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | #include "SDL_x11opengles.h" | ||
| 27 | #include "SDL_x11opengl.h" | ||
| 28 | #include "SDL_x11xsync.h" | ||
| 29 | |||
| 30 | // EGL implementation of SDL OpenGL support | ||
| 31 | |||
| 32 | bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path) | ||
| 33 | { | ||
| 34 | SDL_VideoData *data = _this->internal; | ||
| 35 | |||
| 36 | // If the profile requested is not GL ES, switch over to X11_GL functions | ||
| 37 | if ((_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) && | ||
| 38 | !SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) { | ||
| 39 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 40 | X11_GLES_UnloadLibrary(_this); | ||
| 41 | _this->GL_LoadLibrary = X11_GL_LoadLibrary; | ||
| 42 | _this->GL_GetProcAddress = X11_GL_GetProcAddress; | ||
| 43 | _this->GL_UnloadLibrary = X11_GL_UnloadLibrary; | ||
| 44 | _this->GL_CreateContext = X11_GL_CreateContext; | ||
| 45 | _this->GL_MakeCurrent = X11_GL_MakeCurrent; | ||
| 46 | _this->GL_SetSwapInterval = X11_GL_SetSwapInterval; | ||
| 47 | _this->GL_GetSwapInterval = X11_GL_GetSwapInterval; | ||
| 48 | _this->GL_SwapWindow = X11_GL_SwapWindow; | ||
| 49 | _this->GL_DestroyContext = X11_GL_DestroyContext; | ||
| 50 | return X11_GL_LoadLibrary(_this, path); | ||
| 51 | #else | ||
| 52 | return SDL_SetError("SDL not configured with OpenGL/GLX support"); | ||
| 53 | #endif | ||
| 54 | } | ||
| 55 | |||
| 56 | return SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)data->display, _this->gl_config.egl_platform); | ||
| 57 | } | ||
| 58 | |||
| 59 | XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent) | ||
| 60 | { | ||
| 61 | |||
| 62 | XVisualInfo *egl_visualinfo = NULL; | ||
| 63 | EGLint visual_id = 0; | ||
| 64 | XVisualInfo vi_in; | ||
| 65 | int out_count = 0; | ||
| 66 | |||
| 67 | if (!_this->egl_data) { | ||
| 68 | // The EGL library wasn't loaded, SDL_GetError() should have info | ||
| 69 | return NULL; | ||
| 70 | } | ||
| 71 | |||
| 72 | if (_this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display, | ||
| 73 | _this->egl_data->egl_config, | ||
| 74 | EGL_NATIVE_VISUAL_ID, | ||
| 75 | &visual_id) == EGL_FALSE) { | ||
| 76 | visual_id = 0; | ||
| 77 | } | ||
| 78 | if (visual_id != 0) { | ||
| 79 | vi_in.screen = screen; | ||
| 80 | vi_in.visualid = visual_id; | ||
| 81 | egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count); | ||
| 82 | if (transparent && egl_visualinfo) { | ||
| 83 | Uint32 format = X11_GetPixelFormatFromVisualInfo(display, egl_visualinfo); | ||
| 84 | if (!SDL_ISPIXELFORMAT_ALPHA(format)) { | ||
| 85 | // not transparent! | ||
| 86 | X11_XFree(egl_visualinfo); | ||
| 87 | egl_visualinfo = NULL; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | if(!egl_visualinfo) { | ||
| 93 | // Use the default visual when all else fails | ||
| 94 | vi_in.screen = screen; | ||
| 95 | egl_visualinfo = X11_XGetVisualInfo(display, | ||
| 96 | VisualScreenMask, | ||
| 97 | &vi_in, &out_count); | ||
| 98 | |||
| 99 | // Return the first transparent Visual | ||
| 100 | if (transparent) { | ||
| 101 | int i; | ||
| 102 | for (i = 0; i < out_count; i++) { | ||
| 103 | XVisualInfo *v = &egl_visualinfo[i]; | ||
| 104 | Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v); | ||
| 105 | if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found! | ||
| 106 | // re-request it to have a copy that can be X11_XFree'ed later | ||
| 107 | vi_in.screen = screen; | ||
| 108 | vi_in.visualid = v->visualid; | ||
| 109 | X11_XFree(egl_visualinfo); | ||
| 110 | egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count); | ||
| 111 | return egl_visualinfo; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | return egl_visualinfo; | ||
| 117 | } | ||
| 118 | |||
| 119 | SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 120 | { | ||
| 121 | SDL_GLContext context; | ||
| 122 | SDL_WindowData *data = window->internal; | ||
| 123 | Display *display = data->videodata->display; | ||
| 124 | |||
| 125 | X11_XSync(display, False); | ||
| 126 | context = SDL_EGL_CreateContext(_this, data->egl_surface); | ||
| 127 | X11_XSync(display, False); | ||
| 128 | |||
| 129 | return context; | ||
| 130 | } | ||
| 131 | |||
| 132 | SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 133 | { | ||
| 134 | SDL_WindowData *data = window->internal; | ||
| 135 | return data->egl_surface; | ||
| 136 | } | ||
| 137 | |||
| 138 | bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 139 | { | ||
| 140 | const bool ret = SDL_EGL_SwapBuffers(_this, window->internal->egl_surface); \ | ||
| 141 | |||
| 142 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 143 | X11_HandlePresent(window); | ||
| 144 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 145 | |||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | |||
| 149 | SDL_EGL_MakeCurrent_impl(X11) | ||
| 150 | |||
| 151 | #endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_OPENGL_EGL | ||
| 152 | |||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h new file mode 100644 index 0000000..f8e8d3b --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11opengles.h | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11opengles_h_ | ||
| 24 | #define SDL_x11opengles_h_ | ||
| 25 | |||
| 26 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 27 | |||
| 28 | #include "../SDL_sysvideo.h" | ||
| 29 | #include "../SDL_egl_c.h" | ||
| 30 | |||
| 31 | typedef struct SDL_PrivateGLESData | ||
| 32 | { | ||
| 33 | // 1401 If the struct-declaration-list contains no named members, the behavior is undefined. | ||
| 34 | // warning: empty struct has size 0 in C, size 1 in C++ [-Wc++-compat] | ||
| 35 | int dummy; | ||
| 36 | } SDL_PrivateGLESData; | ||
| 37 | |||
| 38 | // OpenGLES functions | ||
| 39 | #define X11_GLES_GetAttribute SDL_EGL_GetAttribute | ||
| 40 | #define X11_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal | ||
| 41 | #define X11_GLES_UnloadLibrary SDL_EGL_UnloadLibrary | ||
| 42 | #define X11_GLES_SetSwapInterval SDL_EGL_SetSwapInterval | ||
| 43 | #define X11_GLES_GetSwapInterval SDL_EGL_GetSwapInterval | ||
| 44 | #define X11_GLES_DestroyContext SDL_EGL_DestroyContext | ||
| 45 | |||
| 46 | extern bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path); | ||
| 47 | extern XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent); | ||
| 48 | extern SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 49 | extern bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 50 | extern bool X11_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context); | ||
| 51 | extern SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 52 | |||
| 53 | #endif // SDL_VIDEO_OPENGL_EGL | ||
| 54 | |||
| 55 | #endif // SDL_x11opengles_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c new file mode 100644 index 0000000..f16da51 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.c | |||
| @@ -0,0 +1,437 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #include "../../events/SDL_pen_c.h" | ||
| 24 | #include "../SDL_sysvideo.h" | ||
| 25 | #include "SDL_x11pen.h" | ||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11xinput2.h" | ||
| 28 | |||
| 29 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 30 | |||
| 31 | // Does this device have a valuator for pressure sensitivity? | ||
| 32 | static bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev) | ||
| 33 | { | ||
| 34 | const SDL_VideoData *data = _this->internal; | ||
| 35 | for (int i = 0; i < dev->num_classes; i++) { | ||
| 36 | const XIAnyClassInfo *classinfo = dev->classes[i]; | ||
| 37 | if (classinfo->type == XIValuatorClass) { | ||
| 38 | const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo; | ||
| 39 | if (val_classinfo->label == data->atoms.pen_atom_abs_pressure) { | ||
| 40 | return true; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | return false; | ||
| 46 | } | ||
| 47 | |||
| 48 | // Heuristically determines if device is an eraser | ||
| 49 | static bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *devicename) | ||
| 50 | { | ||
| 51 | #define PEN_ERASER_NAME_TAG "eraser" // String constant to identify erasers | ||
| 52 | SDL_VideoData *data = _this->internal; | ||
| 53 | |||
| 54 | if (data->atoms.pen_atom_wacom_tool_type != None) { | ||
| 55 | Atom type_return; | ||
| 56 | int format_return; | ||
| 57 | unsigned long num_items_return; | ||
| 58 | unsigned long bytes_after_return; | ||
| 59 | unsigned char *tooltype_name_info = NULL; | ||
| 60 | |||
| 61 | // Try Wacom-specific method | ||
| 62 | if (Success == X11_XIGetProperty(data->display, deviceid, | ||
| 63 | data->atoms.pen_atom_wacom_tool_type, | ||
| 64 | 0, 32, False, | ||
| 65 | AnyPropertyType, &type_return, &format_return, | ||
| 66 | &num_items_return, &bytes_after_return, | ||
| 67 | &tooltype_name_info) && | ||
| 68 | tooltype_name_info != NULL && num_items_return > 0) { | ||
| 69 | |||
| 70 | bool result = false; | ||
| 71 | char *tooltype_name = NULL; | ||
| 72 | |||
| 73 | if (type_return == XA_ATOM) { | ||
| 74 | // Atom instead of string? Un-intern | ||
| 75 | Atom atom = *((Atom *)tooltype_name_info); | ||
| 76 | if (atom != None) { | ||
| 77 | tooltype_name = X11_XGetAtomName(data->display, atom); | ||
| 78 | } | ||
| 79 | } else if (type_return == XA_STRING && format_return == 8) { | ||
| 80 | tooltype_name = (char *)tooltype_name_info; | ||
| 81 | } | ||
| 82 | |||
| 83 | if (tooltype_name) { | ||
| 84 | if (SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG) == 0) { | ||
| 85 | result = true; | ||
| 86 | } | ||
| 87 | if (tooltype_name != (char *)tooltype_name_info) { | ||
| 88 | X11_XFree(tooltype_name_info); | ||
| 89 | } | ||
| 90 | X11_XFree(tooltype_name); | ||
| 91 | |||
| 92 | return result; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | // Non-Wacom device? | ||
| 98 | |||
| 99 | /* We assume that a device is an eraser if its name contains the string "eraser". | ||
| 100 | * Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */ | ||
| 101 | return (SDL_strcasestr(devicename, PEN_ERASER_NAME_TAG)) ? true : false; | ||
| 102 | } | ||
| 103 | |||
| 104 | // Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably. | ||
| 105 | // Returns number of Sint32s written (<= max_words), or 0 on error. | ||
| 106 | static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words) | ||
| 107 | { | ||
| 108 | const SDL_VideoData *data = _this->internal; | ||
| 109 | Atom type_return; | ||
| 110 | int format_return; | ||
| 111 | unsigned long num_items_return; | ||
| 112 | unsigned long bytes_after_return; | ||
| 113 | unsigned char *output; | ||
| 114 | |||
| 115 | if (property == None) { | ||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | if (Success != X11_XIGetProperty(data->display, deviceid, | ||
| 120 | property, | ||
| 121 | 0, max_words, False, | ||
| 122 | XA_INTEGER, &type_return, &format_return, | ||
| 123 | &num_items_return, &bytes_after_return, | ||
| 124 | &output) || | ||
| 125 | num_items_return == 0 || output == NULL) { | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | if (type_return == XA_INTEGER) { | ||
| 130 | int k; | ||
| 131 | const int to_copy = SDL_min(max_words, num_items_return); | ||
| 132 | |||
| 133 | if (format_return == 8) { | ||
| 134 | Sint8 *numdata = (Sint8 *)output; | ||
| 135 | for (k = 0; k < to_copy; ++k) { | ||
| 136 | dest[k] = numdata[k]; | ||
| 137 | } | ||
| 138 | } else if (format_return == 16) { | ||
| 139 | Sint16 *numdata = (Sint16 *)output; | ||
| 140 | for (k = 0; k < to_copy; ++k) { | ||
| 141 | dest[k] = numdata[k]; | ||
| 142 | } | ||
| 143 | } else { | ||
| 144 | SDL_memcpy(dest, output, sizeof(Sint32) * to_copy); | ||
| 145 | } | ||
| 146 | X11_XFree(output); | ||
| 147 | return to_copy; | ||
| 148 | } | ||
| 149 | |||
| 150 | return 0; // type mismatch | ||
| 151 | } | ||
| 152 | |||
| 153 | // Identify Wacom devices (if true is returned) and extract their device type and serial IDs | ||
| 154 | static bool X11_XInput2PenWacomDeviceID(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial) | ||
| 155 | { | ||
| 156 | SDL_VideoData *data = _this->internal; | ||
| 157 | Sint32 serial_id_buf[3]; | ||
| 158 | int result; | ||
| 159 | |||
| 160 | if ((result = X11_XInput2PenGetIntProperty(_this, deviceid, data->atoms.pen_atom_wacom_serial_ids, serial_id_buf, 3)) == 3) { | ||
| 161 | *wacom_devicetype_id = serial_id_buf[2]; | ||
| 162 | *wacom_serial = serial_id_buf[1]; | ||
| 163 | return true; | ||
| 164 | } | ||
| 165 | |||
| 166 | *wacom_devicetype_id = *wacom_serial = 0; | ||
| 167 | return false; | ||
| 168 | } | ||
| 169 | |||
| 170 | |||
| 171 | typedef struct FindPenByDeviceIDData | ||
| 172 | { | ||
| 173 | int x11_deviceid; | ||
| 174 | void *handle; | ||
| 175 | } FindPenByDeviceIDData; | ||
| 176 | |||
| 177 | static bool FindPenByDeviceID(void *handle, void *userdata) | ||
| 178 | { | ||
| 179 | const X11_PenHandle *x11_handle = (const X11_PenHandle *) handle; | ||
| 180 | FindPenByDeviceIDData *data = (FindPenByDeviceIDData *) userdata; | ||
| 181 | if (x11_handle->x11_deviceid != data->x11_deviceid) { | ||
| 182 | return false; | ||
| 183 | } | ||
| 184 | data->handle = handle; | ||
| 185 | return true; | ||
| 186 | } | ||
| 187 | |||
| 188 | X11_PenHandle *X11_FindPenByDeviceID(int deviceid) | ||
| 189 | { | ||
| 190 | FindPenByDeviceIDData data; | ||
| 191 | data.x11_deviceid = deviceid; | ||
| 192 | data.handle = NULL; | ||
| 193 | SDL_FindPenByCallback(FindPenByDeviceID, &data); | ||
| 194 | return (X11_PenHandle *) data.handle; | ||
| 195 | } | ||
| 196 | |||
| 197 | static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev) | ||
| 198 | { | ||
| 199 | SDL_VideoData *data = _this->internal; | ||
| 200 | SDL_PenCapabilityFlags capabilities = 0; | ||
| 201 | X11_PenHandle *handle = NULL; | ||
| 202 | |||
| 203 | if ((dev->use != XISlavePointer && (dev->use != XIFloatingSlave)) || dev->enabled == 0 || !X11_XInput2DeviceIsPen(_this, dev)) { | ||
| 204 | return NULL; // Only track physical devices that are enabled and look like pens | ||
| 205 | } else if ((handle = X11_FindPenByDeviceID(dev->deviceid)) != 0) { | ||
| 206 | return handle; // already have this pen, skip it. | ||
| 207 | } else if ((handle = SDL_calloc(1, sizeof (*handle))) == NULL) { | ||
| 208 | return NULL; // oh well. | ||
| 209 | } | ||
| 210 | |||
| 211 | for (int i = 0; i < SDL_arraysize(handle->valuator_for_axis); i++) { | ||
| 212 | handle->valuator_for_axis[i] = SDL_X11_PEN_AXIS_VALUATOR_MISSING; // until proven otherwise | ||
| 213 | } | ||
| 214 | |||
| 215 | int total_buttons = 0; | ||
| 216 | for (int i = 0; i < dev->num_classes; i++) { | ||
| 217 | const XIAnyClassInfo *classinfo = dev->classes[i]; | ||
| 218 | if (classinfo->type == XIButtonClass) { | ||
| 219 | const XIButtonClassInfo *button_classinfo = (const XIButtonClassInfo *)classinfo; | ||
| 220 | total_buttons += button_classinfo->num_buttons; | ||
| 221 | } else if (classinfo->type == XIValuatorClass) { | ||
| 222 | const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo; | ||
| 223 | const Sint8 valuator_nr = val_classinfo->number; | ||
| 224 | const Atom vname = val_classinfo->label; | ||
| 225 | const float min = (float)val_classinfo->min; | ||
| 226 | const float max = (float)val_classinfo->max; | ||
| 227 | bool use_this_axis = true; | ||
| 228 | SDL_PenAxis axis = SDL_PEN_AXIS_COUNT; | ||
| 229 | |||
| 230 | // afaict, SDL_PEN_AXIS_DISTANCE is never reported by XInput2 (Wayland can offer it, though) | ||
| 231 | if (vname == data->atoms.pen_atom_abs_pressure) { | ||
| 232 | axis = SDL_PEN_AXIS_PRESSURE; | ||
| 233 | } else if (vname == data->atoms.pen_atom_abs_tilt_x) { | ||
| 234 | axis = SDL_PEN_AXIS_XTILT; | ||
| 235 | } else if (vname == data->atoms.pen_atom_abs_tilt_y) { | ||
| 236 | axis = SDL_PEN_AXIS_YTILT; | ||
| 237 | } else { | ||
| 238 | use_this_axis = false; | ||
| 239 | } | ||
| 240 | |||
| 241 | // !!! FIXME: there are wacom-specific hacks for getting SDL_PEN_AXIS_(ROTATION|SLIDER) on some devices, but for simplicity, we're skipping all that for now. | ||
| 242 | |||
| 243 | if (use_this_axis) { | ||
| 244 | capabilities |= SDL_GetPenCapabilityFromAxis(axis); | ||
| 245 | handle->valuator_for_axis[axis] = valuator_nr; | ||
| 246 | handle->axis_min[axis] = min; | ||
| 247 | handle->axis_max[axis] = max; | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | // We have a pen if and only if the device measures pressure. | ||
| 253 | // We checked this in X11_XInput2DeviceIsPen, so just assert it here. | ||
| 254 | SDL_assert(capabilities & SDL_PEN_CAPABILITY_PRESSURE); | ||
| 255 | |||
| 256 | const bool is_eraser = X11_XInput2PenIsEraser(_this, dev->deviceid, dev->name); | ||
| 257 | Uint32 wacom_devicetype_id = 0; | ||
| 258 | Uint32 wacom_serial = 0; | ||
| 259 | X11_XInput2PenWacomDeviceID(_this, dev->deviceid, &wacom_devicetype_id, &wacom_serial); | ||
| 260 | |||
| 261 | SDL_PenInfo peninfo; | ||
| 262 | SDL_zero(peninfo); | ||
| 263 | peninfo.capabilities = capabilities; | ||
| 264 | peninfo.max_tilt = -1; | ||
| 265 | peninfo.wacom_id = wacom_devicetype_id; | ||
| 266 | peninfo.num_buttons = total_buttons; | ||
| 267 | peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN; | ||
| 268 | if (is_eraser) { | ||
| 269 | peninfo.capabilities |= SDL_PEN_CAPABILITY_ERASER; | ||
| 270 | } | ||
| 271 | |||
| 272 | handle->is_eraser = is_eraser; | ||
| 273 | handle->x11_deviceid = dev->deviceid; | ||
| 274 | |||
| 275 | handle->pen = SDL_AddPenDevice(0, dev->name, &peninfo, handle); | ||
| 276 | if (!handle->pen) { | ||
| 277 | SDL_free(handle); | ||
| 278 | return NULL; | ||
| 279 | } | ||
| 280 | |||
| 281 | return handle; | ||
| 282 | } | ||
| 283 | |||
| 284 | X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid) | ||
| 285 | { | ||
| 286 | SDL_VideoData *data = _this->internal; | ||
| 287 | int num_device_info = 0; | ||
| 288 | XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info); | ||
| 289 | if (device_info) { | ||
| 290 | SDL_assert(num_device_info == 1); | ||
| 291 | X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info); | ||
| 292 | X11_XIFreeDeviceInfo(device_info); | ||
| 293 | return handle; | ||
| 294 | } | ||
| 295 | return NULL; | ||
| 296 | } | ||
| 297 | |||
| 298 | void X11_RemovePenByDeviceID(int deviceid) | ||
| 299 | { | ||
| 300 | X11_PenHandle *handle = X11_FindPenByDeviceID(deviceid); | ||
| 301 | if (handle) { | ||
| 302 | SDL_RemovePenDevice(0, handle->pen); | ||
| 303 | SDL_free(handle); | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | void X11_InitPen(SDL_VideoDevice *_this) | ||
| 308 | { | ||
| 309 | SDL_VideoData *data = _this->internal; | ||
| 310 | |||
| 311 | #define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False) | ||
| 312 | data->atoms.pen_atom_device_product_id = LOOKUP_PEN_ATOM("Device Product ID"); | ||
| 313 | data->atoms.pen_atom_wacom_serial_ids = LOOKUP_PEN_ATOM("Wacom Serial IDs"); | ||
| 314 | data->atoms.pen_atom_wacom_tool_type = LOOKUP_PEN_ATOM("Wacom Tool Type"); | ||
| 315 | data->atoms.pen_atom_abs_pressure = LOOKUP_PEN_ATOM("Abs Pressure"); | ||
| 316 | data->atoms.pen_atom_abs_tilt_x = LOOKUP_PEN_ATOM("Abs Tilt X"); | ||
| 317 | data->atoms.pen_atom_abs_tilt_y = LOOKUP_PEN_ATOM("Abs Tilt Y"); | ||
| 318 | #undef LOOKUP_PEN_ATOM | ||
| 319 | |||
| 320 | // Do an initial check on devices. After this, we'll add/remove individual pens when XI_HierarchyChanged events alert us. | ||
| 321 | int num_device_info = 0; | ||
| 322 | XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info); | ||
| 323 | if (device_info) { | ||
| 324 | for (int i = 0; i < num_device_info; i++) { | ||
| 325 | X11_MaybeAddPen(_this, &device_info[i]); | ||
| 326 | } | ||
| 327 | X11_XIFreeDeviceInfo(device_info); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | static void X11_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata) | ||
| 332 | { | ||
| 333 | SDL_free(handle); | ||
| 334 | } | ||
| 335 | |||
| 336 | void X11_QuitPen(SDL_VideoDevice *_this) | ||
| 337 | { | ||
| 338 | SDL_RemoveAllPenDevices(X11_FreePenHandle, NULL); | ||
| 339 | } | ||
| 340 | |||
| 341 | static void X11_XInput2NormalizePenAxes(const X11_PenHandle *pen, float *coords) | ||
| 342 | { | ||
| 343 | // Normalise axes | ||
| 344 | for (int axis = 0; axis < SDL_PEN_AXIS_COUNT; ++axis) { | ||
| 345 | const int valuator = pen->valuator_for_axis[axis]; | ||
| 346 | if (valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) { | ||
| 347 | continue; | ||
| 348 | } | ||
| 349 | |||
| 350 | float value = coords[axis]; | ||
| 351 | const float min = pen->axis_min[axis]; | ||
| 352 | const float max = pen->axis_max[axis]; | ||
| 353 | |||
| 354 | if (axis == SDL_PEN_AXIS_SLIDER) { | ||
| 355 | value += pen->slider_bias; | ||
| 356 | } | ||
| 357 | |||
| 358 | // min ... 0 ... max | ||
| 359 | if (min < 0.0) { | ||
| 360 | // Normalise so that 0 remains 0.0 | ||
| 361 | if (value < 0) { | ||
| 362 | value = value / (-min); | ||
| 363 | } else { | ||
| 364 | if (max == 0.0f) { | ||
| 365 | value = 0.0f; | ||
| 366 | } else { | ||
| 367 | value = value / max; | ||
| 368 | } | ||
| 369 | } | ||
| 370 | } else { | ||
| 371 | // 0 ... min ... max | ||
| 372 | // including 0.0 = min | ||
| 373 | if (max == 0.0f) { | ||
| 374 | value = 0.0f; | ||
| 375 | } else { | ||
| 376 | value = (value - min) / max; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | switch (axis) { | ||
| 381 | case SDL_PEN_AXIS_XTILT: | ||
| 382 | case SDL_PEN_AXIS_YTILT: | ||
| 383 | //if (peninfo->info.max_tilt > 0.0f) { | ||
| 384 | // value *= peninfo->info.max_tilt; // normalize to physical max | ||
| 385 | //} | ||
| 386 | break; | ||
| 387 | |||
| 388 | case SDL_PEN_AXIS_ROTATION: | ||
| 389 | // normalised to -1..1, so let's convert to degrees | ||
| 390 | value *= 180.0f; | ||
| 391 | value += pen->rotation_bias; | ||
| 392 | |||
| 393 | // handle simple over/underflow | ||
| 394 | if (value >= 180.0f) { | ||
| 395 | value -= 360.0f; | ||
| 396 | } else if (value < -180.0f) { | ||
| 397 | value += 360.0f; | ||
| 398 | } | ||
| 399 | break; | ||
| 400 | |||
| 401 | default: | ||
| 402 | break; | ||
| 403 | } | ||
| 404 | |||
| 405 | coords[axis] = value; | ||
| 406 | } | ||
| 407 | } | ||
| 408 | |||
| 409 | void X11_PenAxesFromValuators(const X11_PenHandle *pen, | ||
| 410 | const double *input_values, const unsigned char *mask, int mask_len, | ||
| 411 | float axis_values[SDL_PEN_AXIS_COUNT]) | ||
| 412 | { | ||
| 413 | for (int i = 0; i < SDL_PEN_AXIS_COUNT; i++) { | ||
| 414 | const int valuator = pen->valuator_for_axis[i]; | ||
| 415 | if ((valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) || (valuator >= mask_len * 8) || !(XIMaskIsSet(mask, valuator))) { | ||
| 416 | axis_values[i] = 0.0f; | ||
| 417 | } else { | ||
| 418 | axis_values[i] = (float)input_values[valuator]; | ||
| 419 | } | ||
| 420 | } | ||
| 421 | X11_XInput2NormalizePenAxes(pen, axis_values); | ||
| 422 | } | ||
| 423 | |||
| 424 | #else | ||
| 425 | |||
| 426 | void X11_InitPen(SDL_VideoDevice *_this) | ||
| 427 | { | ||
| 428 | (void) _this; | ||
| 429 | } | ||
| 430 | |||
| 431 | void X11_QuitPen(SDL_VideoDevice *_this) | ||
| 432 | { | ||
| 433 | (void) _this; | ||
| 434 | } | ||
| 435 | |||
| 436 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 437 | |||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h new file mode 100644 index 0000000..de75181 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11pen.h | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11pen_h_ | ||
| 24 | #define SDL_x11pen_h_ | ||
| 25 | |||
| 26 | // Pressure-sensitive pen support for X11. | ||
| 27 | |||
| 28 | #include "SDL_x11video.h" | ||
| 29 | #include "../../events/SDL_pen_c.h" | ||
| 30 | |||
| 31 | // Prep pen support (never fails; pens simply won't be added if there's a problem). | ||
| 32 | extern void X11_InitPen(SDL_VideoDevice *_this); | ||
| 33 | |||
| 34 | // Clean up pen support. | ||
| 35 | extern void X11_QuitPen(SDL_VideoDevice *_this); | ||
| 36 | |||
| 37 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 38 | |||
| 39 | // Forward definition for SDL_x11video.h | ||
| 40 | struct SDL_VideoData; | ||
| 41 | |||
| 42 | #define SDL_X11_PEN_AXIS_VALUATOR_MISSING -1 | ||
| 43 | |||
| 44 | typedef struct X11_PenHandle | ||
| 45 | { | ||
| 46 | SDL_PenID pen; | ||
| 47 | bool is_eraser; | ||
| 48 | int x11_deviceid; | ||
| 49 | int valuator_for_axis[SDL_PEN_AXIS_COUNT]; | ||
| 50 | float slider_bias; // shift value to add to PEN_AXIS_SLIDER (before normalisation) | ||
| 51 | float rotation_bias; // rotation to add to PEN_AXIS_ROTATION (after normalisation) | ||
| 52 | float axis_min[SDL_PEN_AXIS_COUNT]; | ||
| 53 | float axis_max[SDL_PEN_AXIS_COUNT]; | ||
| 54 | } X11_PenHandle; | ||
| 55 | |||
| 56 | // Converts XINPUT2 valuators into pen axis information, including normalisation. | ||
| 57 | extern void X11_PenAxesFromValuators(const X11_PenHandle *pen, | ||
| 58 | const double *input_values, const unsigned char *mask, int mask_len, | ||
| 59 | float axis_values[SDL_PEN_AXIS_COUNT]); | ||
| 60 | |||
| 61 | // Add a pen (if this function's further checks validate it). | ||
| 62 | extern X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid); | ||
| 63 | |||
| 64 | // Remove a pen. It's okay if deviceid is bogus or not a pen, we'll check it. | ||
| 65 | extern void X11_RemovePenByDeviceID(int deviceid); | ||
| 66 | |||
| 67 | // Map X11 device ID to pen ID. | ||
| 68 | extern X11_PenHandle *X11_FindPenByDeviceID(int deviceid); | ||
| 69 | |||
| 70 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 71 | |||
| 72 | #endif // SDL_x11pen_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c new file mode 100644 index 0000000..7a7ae01 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.c | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright 2024 Igalia S.L. | ||
| 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #if defined(SDL_VIDEO_DRIVER_X11) | ||
| 25 | |||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11settings.h" | ||
| 28 | |||
| 29 | #define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor" | ||
| 30 | #define SDL_XSETTINGS_XFT_DPI "Xft/DPI" | ||
| 31 | |||
| 32 | static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data) | ||
| 33 | { | ||
| 34 | SDL_VideoDevice *_this = data; | ||
| 35 | float scale_factor = 1.0; | ||
| 36 | int i; | ||
| 37 | |||
| 38 | if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) != 0 || | ||
| 39 | SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) != 0) { | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | |||
| 43 | if (setting->type != XSETTINGS_TYPE_INT) { | ||
| 44 | return; | ||
| 45 | } | ||
| 46 | |||
| 47 | switch (action) { | ||
| 48 | case XSETTINGS_ACTION_NEW: | ||
| 49 | SDL_FALLTHROUGH; | ||
| 50 | case XSETTINGS_ACTION_CHANGED: | ||
| 51 | scale_factor = setting->data.v_int; | ||
| 52 | if (SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) { | ||
| 53 | scale_factor = scale_factor / 1024.0f / 96.0f; | ||
| 54 | } | ||
| 55 | break; | ||
| 56 | case XSETTINGS_ACTION_DELETED: | ||
| 57 | scale_factor = 1.0; | ||
| 58 | break; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (_this) { | ||
| 62 | for (i = 0; i < _this->num_displays; ++i) { | ||
| 63 | SDL_SetDisplayContentScale(_this->displays[i], scale_factor); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | void X11_InitXsettings(SDL_VideoDevice *_this) | ||
| 69 | { | ||
| 70 | SDL_VideoData *data = _this->internal; | ||
| 71 | SDLX11_SettingsData *xsettings_data = &data->xsettings_data; | ||
| 72 | |||
| 73 | xsettings_data->xsettings = xsettings_client_new(data->display, | ||
| 74 | DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this); | ||
| 75 | |||
| 76 | } | ||
| 77 | |||
| 78 | void X11_QuitXsettings(SDL_VideoDevice *_this) | ||
| 79 | { | ||
| 80 | SDL_VideoData *data = _this->internal; | ||
| 81 | SDLX11_SettingsData *xsettings_data = &data->xsettings_data; | ||
| 82 | |||
| 83 | if (xsettings_data->xsettings) { | ||
| 84 | xsettings_client_destroy(xsettings_data->xsettings); | ||
| 85 | xsettings_data->xsettings = NULL; | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent) | ||
| 90 | { | ||
| 91 | SDL_VideoData *data = _this->internal; | ||
| 92 | SDLX11_SettingsData *xsettings_data = &data->xsettings_data; | ||
| 93 | |||
| 94 | if (xsettings_data->xsettings) { | ||
| 95 | if (!xsettings_client_process_event(xsettings_data->xsettings, xevent)) { | ||
| 96 | xsettings_client_destroy(xsettings_data->xsettings); | ||
| 97 | xsettings_data->xsettings = NULL; | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value) { | ||
| 103 | SDL_VideoData *data = _this->internal; | ||
| 104 | SDLX11_SettingsData *xsettings_data = &data->xsettings_data; | ||
| 105 | XSettingsSetting *setting = NULL; | ||
| 106 | int res = fallback_value; | ||
| 107 | |||
| 108 | |||
| 109 | if (xsettings_data->xsettings) { | ||
| 110 | if (xsettings_client_get_setting(xsettings_data->xsettings, key, &setting) != XSETTINGS_SUCCESS) { | ||
| 111 | goto no_key; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (setting->type != XSETTINGS_TYPE_INT) { | ||
| 115 | goto no_key; | ||
| 116 | } | ||
| 117 | |||
| 118 | res = setting->data.v_int; | ||
| 119 | } | ||
| 120 | |||
| 121 | no_key: | ||
| 122 | if (setting) { | ||
| 123 | xsettings_setting_free(setting); | ||
| 124 | } | ||
| 125 | |||
| 126 | return res; | ||
| 127 | } | ||
| 128 | |||
| 129 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h new file mode 100644 index 0000000..5b36884 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11settings.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright 2024 Igalia S.L. | ||
| 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #ifndef SDL_x11settings_h_ | ||
| 25 | #define SDL_x11settings_h_ | ||
| 26 | |||
| 27 | #include <X11/Xlib.h> | ||
| 28 | #include "xsettings-client.h" | ||
| 29 | |||
| 30 | typedef struct X11_SettingsData { | ||
| 31 | XSettingsClient *xsettings; | ||
| 32 | } SDLX11_SettingsData; | ||
| 33 | |||
| 34 | extern void X11_InitXsettings(SDL_VideoDevice *_this); | ||
| 35 | extern void X11_QuitXsettings(SDL_VideoDevice *_this); | ||
| 36 | extern void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent); | ||
| 37 | extern int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value); | ||
| 38 | |||
| 39 | #endif // SDL_x11settings_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c new file mode 100644 index 0000000..e433598 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.c | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | #include "SDL_x11shape.h" | ||
| 27 | |||
| 28 | |||
| 29 | #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 30 | static Uint8 *GenerateShapeMask(SDL_Surface *shape) | ||
| 31 | { | ||
| 32 | int x, y; | ||
| 33 | const size_t ppb = 8; | ||
| 34 | const size_t bytes_per_scanline = (shape->w + (ppb - 1)) / ppb; | ||
| 35 | const Uint8 *a; | ||
| 36 | Uint8 *mask; | ||
| 37 | Uint8 *mask_scanline; | ||
| 38 | Uint8 mask_value; | ||
| 39 | |||
| 40 | mask = (Uint8 *)SDL_calloc(1, shape->h * bytes_per_scanline); | ||
| 41 | if (mask) { | ||
| 42 | for (y = 0; y < shape->h; y++) { | ||
| 43 | a = (const Uint8 *)shape->pixels + y * shape->pitch; | ||
| 44 | mask_scanline = mask + y * bytes_per_scanline; | ||
| 45 | for (x = 0; x < shape->w; x++) { | ||
| 46 | mask_value = (*a == SDL_ALPHA_TRANSPARENT) ? 0 : 1; | ||
| 47 | mask_scanline[x / ppb] |= mask_value << (x % ppb); | ||
| 48 | a += 4; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | return mask; | ||
| 53 | } | ||
| 54 | #endif // SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 55 | |||
| 56 | bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape) | ||
| 57 | { | ||
| 58 | bool result = false; | ||
| 59 | |||
| 60 | #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 61 | SDL_WindowData *windowdata = window->internal; | ||
| 62 | |||
| 63 | // Generate a set of spans for the region | ||
| 64 | if (shape) { | ||
| 65 | SDL_Surface *stretched = NULL; | ||
| 66 | Uint8 *mask; | ||
| 67 | Pixmap pixmap; | ||
| 68 | |||
| 69 | if (shape->w != window->w || shape->h != window->h) { | ||
| 70 | stretched = SDL_CreateSurface(window->w, window->h, SDL_PIXELFORMAT_ARGB32); | ||
| 71 | if (!stretched) { | ||
| 72 | return false; | ||
| 73 | } | ||
| 74 | if (!SDL_StretchSurface(shape, NULL, stretched, NULL, SDL_SCALEMODE_LINEAR)) { | ||
| 75 | SDL_DestroySurface(stretched); | ||
| 76 | return false; | ||
| 77 | } | ||
| 78 | shape = stretched; | ||
| 79 | } | ||
| 80 | |||
| 81 | mask = GenerateShapeMask(shape); | ||
| 82 | if (mask) { | ||
| 83 | pixmap = X11_XCreateBitmapFromData(windowdata->videodata->display, windowdata->xwindow, (const char *)mask, shape->w, shape->h); | ||
| 84 | X11_XShapeCombineMask(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, pixmap, ShapeSet); | ||
| 85 | SDL_free(mask); | ||
| 86 | |||
| 87 | result = true; | ||
| 88 | } | ||
| 89 | |||
| 90 | if (stretched) { | ||
| 91 | SDL_DestroySurface(stretched); | ||
| 92 | } | ||
| 93 | } else { | ||
| 94 | Region region = X11_XCreateRegion(); | ||
| 95 | XRectangle rect; | ||
| 96 | |||
| 97 | rect.x = 0; | ||
| 98 | rect.y = 0; | ||
| 99 | rect.width = window->w; | ||
| 100 | rect.height = window->h; | ||
| 101 | X11_XUnionRectWithRegion(&rect, region, region); | ||
| 102 | X11_XShapeCombineRegion(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, region, ShapeSet); | ||
| 103 | X11_XDestroyRegion(region); | ||
| 104 | result = true; | ||
| 105 | } | ||
| 106 | #endif // SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 107 | |||
| 108 | return result; | ||
| 109 | } | ||
| 110 | |||
| 111 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h new file mode 100644 index 0000000..d47d103 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11shape.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11shape_h_ | ||
| 24 | #define SDL_x11shape_h_ | ||
| 25 | |||
| 26 | extern bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape); | ||
| 27 | |||
| 28 | #endif // SDL_x11shape_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h new file mode 100644 index 0000000..68d70cd --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11sym.h | |||
| @@ -0,0 +1,354 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | /* *INDENT-OFF* */ // clang-format off | ||
| 23 | |||
| 24 | #ifndef SDL_X11_MODULE | ||
| 25 | #define SDL_X11_MODULE(modname) | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #ifndef SDL_X11_SYM | ||
| 29 | #define SDL_X11_SYM(rc,fn,params,args,ret) | ||
| 30 | #endif | ||
| 31 | |||
| 32 | SDL_X11_MODULE(BASEXLIB) | ||
| 33 | SDL_X11_SYM(XSizeHints*,XAllocSizeHints,(void),(),return) | ||
| 34 | SDL_X11_SYM(XWMHints*,XAllocWMHints,(void),(),return) | ||
| 35 | SDL_X11_SYM(XClassHint*,XAllocClassHint,(void),(),return) | ||
| 36 | SDL_X11_SYM(int,XChangePointerControl,(Display* a,Bool b,Bool c,int d,int e,int f),(a,b,c,d,e,f),return) | ||
| 37 | SDL_X11_SYM(int,XChangeProperty,(Display* a,Window b,Atom c,Atom d,int e,int f,_Xconst unsigned char* g,int h),(a,b,c,d,e,f,g,h),return) | ||
| 38 | SDL_X11_SYM(Bool,XCheckIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return) | ||
| 39 | SDL_X11_SYM(int,XClearWindow,(Display* a,Window b),(a,b),return) | ||
| 40 | SDL_X11_SYM(int,XCloseDisplay,(Display* a),(a),return) | ||
| 41 | SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time f),(a,b,c,d,e,f),return) | ||
| 42 | SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height),(dpy,d,data,width,height),return) | ||
| 43 | SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d),(a,b,c,d),return) | ||
| 44 | SDL_X11_SYM(Cursor,XCreatePixmapCursor,(Display* a,Pixmap b,Pixmap c,XColor* d,XColor* e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return) | ||
| 45 | SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b),(a,b),return) | ||
| 46 | SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e),(a,b,c,d,e),return) | ||
| 47 | SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d),(a,b,c,d),return) | ||
| 48 | SDL_X11_SYM(XImage*,XCreateImage,(Display* a,Visual* b,unsigned int c,int d,int e,char* f,unsigned int g,unsigned int h,int i,int j),(a,b,c,d,e,f,g,h,i,j),return) | ||
| 49 | SDL_X11_SYM(Window,XCreateWindow,(Display* a,Window b,int c,int d,unsigned int e,unsigned int f,unsigned int g,int h,unsigned int i,Visual* j,unsigned long k,XSetWindowAttributes* l),(a,b,c,d,e,f,g,h,i,j,k,l),return) | ||
| 50 | SDL_X11_SYM(int,XDefineCursor,(Display* a,Window b,Cursor c),(a,b,c),return) | ||
| 51 | SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c),(a,b,c),return) | ||
| 52 | SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b),(a,b),return) | ||
| 53 | SDL_X11_SYM(int,XDisplayKeycodes,(Display* a,int* b,int* c),(a,b,c),return) | ||
| 54 | SDL_X11_SYM(int,XDrawRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return) | ||
| 55 | SDL_X11_SYM(char*,XDisplayName,(_Xconst char* a),(a),return) | ||
| 56 | SDL_X11_SYM(int,XDrawString,(Display* a,Drawable b,GC c,int d,int e,_Xconst char* f,int g),(a,b,c,d,e,f,g),return) | ||
| 57 | SDL_X11_SYM(int,XEventsQueued,(Display* a,int b),(a,b),return) | ||
| 58 | SDL_X11_SYM(int,XFillRectangle,(Display* a,Drawable b,GC c,int d,int e,unsigned int f,unsigned int g),(a,b,c,d,e,f,g),return) | ||
| 59 | SDL_X11_SYM(Bool,XFilterEvent,(XEvent *event,Window w),(event,w),return) | ||
| 60 | SDL_X11_SYM(int,XFlush,(Display* a),(a),return) | ||
| 61 | SDL_X11_SYM(int,XFree,(void*a),(a),return) | ||
| 62 | SDL_X11_SYM(int,XFreeCursor,(Display* a,Cursor b),(a,b),return) | ||
| 63 | SDL_X11_SYM(void,XFreeFontSet,(Display* a, XFontSet b),(a,b),) | ||
| 64 | SDL_X11_SYM(int,XFreeGC,(Display* a,GC b),(a,b),return) | ||
| 65 | SDL_X11_SYM(int,XFreeFont,(Display* a, XFontStruct* b),(a,b),return) | ||
| 66 | SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a),(a),return) | ||
| 67 | SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b),(a,b),return) | ||
| 68 | SDL_X11_SYM(void,XFreeStringList,(char** a),(a),) | ||
| 69 | SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b),(a,b),return) | ||
| 70 | SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c),(a,b,c),return) | ||
| 71 | SDL_X11_SYM(int,XGetErrorDatabaseText,(Display* a,_Xconst char* b,_Xconst char* c,_Xconst char* d,char* e,int f),(a,b,c,d,e,f),return) | ||
| 72 | SDL_X11_SYM(XModifierKeymap*,XGetModifierMapping,(Display* a),(a),return) | ||
| 73 | SDL_X11_SYM(int,XGetPointerControl,(Display* a,int* b,int* c,int* d),(a,b,c,d),return) | ||
| 74 | SDL_X11_SYM(Window,XGetSelectionOwner,(Display* a,Atom b),(a,b),return) | ||
| 75 | SDL_X11_SYM(XVisualInfo*,XGetVisualInfo,(Display* a,long b,XVisualInfo* c,int* d),(a,b,c,d),return) | ||
| 76 | SDL_X11_SYM(Status,XGetWindowAttributes,(Display* a,Window b,XWindowAttributes* c),(a,b,c),return) | ||
| 77 | SDL_X11_SYM(int,XGetWindowProperty,(Display* a,Window b,Atom c,long d,long e,Bool f,Atom g,Atom* h,int* i,unsigned long* j,unsigned long *k,unsigned char **l),(a,b,c,d,e,f,g,h,i,j,k,l),return) | ||
| 78 | SDL_X11_SYM(XWMHints*,XGetWMHints,(Display* a,Window b),(a,b),return) | ||
| 79 | SDL_X11_SYM(Status,XGetWMNormalHints,(Display *a,Window b, XSizeHints *c, long *d),(a,b,c,d),return) | ||
| 80 | SDL_X11_SYM(int,XIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return) | ||
| 81 | SDL_X11_SYM(int,XGrabKeyboard,(Display* a,Window b,Bool c,int d,int e,Time f),(a,b,c,d,e,f),return) | ||
| 82 | SDL_X11_SYM(int,XGrabPointer,(Display* a,Window b,Bool c,unsigned int d,int e,int f,Window g,Cursor h,Time i),(a,b,c,d,e,f,g,h,i),return) | ||
| 83 | SDL_X11_SYM(int,XGrabServer,(Display* a),(a),return) | ||
| 84 | SDL_X11_SYM(Status,XIconifyWindow,(Display* a,Window b,int c),(a,b,c),return) | ||
| 85 | SDL_X11_SYM(KeyCode,XKeysymToKeycode,(Display* a,KeySym b),(a,b),return) | ||
| 86 | SDL_X11_SYM(char*,XKeysymToString,(KeySym a),(a),return) | ||
| 87 | SDL_X11_SYM(int,XInstallColormap,(Display* a,Colormap b),(a,b),return) | ||
| 88 | SDL_X11_SYM(Atom,XInternAtom,(Display* a,_Xconst char* b,Bool c),(a,b,c),return) | ||
| 89 | SDL_X11_SYM(XPixmapFormatValues*,XListPixmapFormats,(Display* a,int* b),(a,b),return) | ||
| 90 | SDL_X11_SYM(XFontStruct*,XLoadQueryFont,(Display* a,_Xconst char* b),(a,b),return) | ||
| 91 | SDL_X11_SYM(KeySym,XLookupKeysym,(XKeyEvent* a,int b),(a,b),return) | ||
| 92 | SDL_X11_SYM(int,XLookupString,(XKeyEvent* a,char* b,int c,KeySym* d,XComposeStatus* e),(a,b,c,d,e),return) | ||
| 93 | SDL_X11_SYM(int,XMapRaised,(Display* a,Window b),(a,b),return) | ||
| 94 | SDL_X11_SYM(Status,XMatchVisualInfo,(Display* a,int b,int c,int d,XVisualInfo* e),(a,b,c,d,e),return) | ||
| 95 | SDL_X11_SYM(int,XMissingExtension,(Display* a,_Xconst char* b),(a,b),return) | ||
| 96 | SDL_X11_SYM(int,XMoveWindow,(Display* a,Window b,int c,int d),(a,b,c,d),return) | ||
| 97 | SDL_X11_SYM(Display*,XOpenDisplay,(_Xconst char* a),(a),return) | ||
| 98 | SDL_X11_SYM(Status,XInitThreads,(void),(),return) | ||
| 99 | SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b),(a,b),return) | ||
| 100 | SDL_X11_SYM(int,XPending,(Display* a),(a),return) | ||
| 101 | SDL_X11_SYM(int,XPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j),(a,b,c,d,e,f,g,h,i,j),return) | ||
| 102 | SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32]),(a,b),return) | ||
| 103 | SDL_X11_SYM(Bool,XQueryPointer,(Display* a,Window b,Window* c,Window* d,int* e,int* f,int* g,int* h,unsigned int* i),(a,b,c,d,e,f,g,h,i),return) | ||
| 104 | SDL_X11_SYM(int,XRaiseWindow,(Display* a,Window b),(a,b),return) | ||
| 105 | SDL_X11_SYM(int,XReparentWindow,(Display* a,Window b,Window c,int d,int e),(a,b,c,d,e),return) | ||
| 106 | SDL_X11_SYM(int,XResetScreenSaver,(Display* a),(a),return) | ||
| 107 | SDL_X11_SYM(int,XResizeWindow,(Display* a,Window b,unsigned int c,unsigned int d),(a,b,c,d),return) | ||
| 108 | SDL_X11_SYM(int,XScreenNumberOfScreen,(Screen* a),(a),return) | ||
| 109 | SDL_X11_SYM(int,XSelectInput,(Display* a,Window b,long c),(a,b,c),return) | ||
| 110 | SDL_X11_SYM(Status,XSendEvent,(Display* a,Window b,Bool c,long d,XEvent* e),(a,b,c,d,e),return) | ||
| 111 | SDL_X11_SYM(XErrorHandler,XSetErrorHandler,(XErrorHandler a),(a),return) | ||
| 112 | SDL_X11_SYM(int,XSetForeground,(Display* a,GC b,unsigned long c),(a,b,c),return) | ||
| 113 | SDL_X11_SYM(XIOErrorHandler,XSetIOErrorHandler,(XIOErrorHandler a),(a),return) | ||
| 114 | SDL_X11_SYM(int,XSetInputFocus,(Display *a,Window b,int c,Time d),(a,b,c,d),return) | ||
| 115 | SDL_X11_SYM(int,XSetSelectionOwner,(Display* a,Atom b,Window c,Time d),(a,b,c,d),return) | ||
| 116 | SDL_X11_SYM(int,XSetTransientForHint,(Display* a,Window b,Window c),(a,b,c),return) | ||
| 117 | SDL_X11_SYM(void,XSetTextProperty,(Display* a,Window b,XTextProperty* c,Atom d),(a,b,c,d),) | ||
| 118 | SDL_X11_SYM(int,XSetWindowBackground,(Display* a,Window b,unsigned long c),(a,b,c),return) | ||
| 119 | SDL_X11_SYM(void,XSetWMHints,(Display* a,Window b,XWMHints* c),(a,b,c),) | ||
| 120 | SDL_X11_SYM(void,XSetWMNormalHints,(Display* a,Window b,XSizeHints* c),(a,b,c),) | ||
| 121 | SDL_X11_SYM(void,XSetWMProperties,(Display* a,Window b,XTextProperty* c,XTextProperty* d,char** e,int f,XSizeHints* g,XWMHints* h,XClassHint* i),(a,b,c,d,e,f,g,h,i),) | ||
| 122 | SDL_X11_SYM(Status,XSetWMProtocols,(Display* a,Window b,Atom* c,int d),(a,b,c,d),return) | ||
| 123 | SDL_X11_SYM(int,XStoreColors,(Display* a,Colormap b,XColor* c,int d),(a,b,c,d),return) | ||
| 124 | SDL_X11_SYM(int,XStoreName,(Display* a,Window b,_Xconst char* c),(a,b,c),return) | ||
| 125 | SDL_X11_SYM(Status,XStringListToTextProperty,(char** a,int b,XTextProperty* c),(a,b,c),return) | ||
| 126 | SDL_X11_SYM(int,XSync,(Display* a,Bool b),(a,b),return) | ||
| 127 | SDL_X11_SYM(int,XTextExtents,(XFontStruct* a,_Xconst char* b,int c,int* d,int* e,int* f,XCharStruct* g),(a,b,c,d,e,f,g),return) | ||
| 128 | SDL_X11_SYM(Bool,XTranslateCoordinates,(Display *a,Window b,Window c,int d,int e,int* f,int* g,Window* h),(a,b,c,d,e,f,g,h),return) | ||
| 129 | SDL_X11_SYM(int,XUndefineCursor,(Display* a,Window b),(a,b),return) | ||
| 130 | SDL_X11_SYM(int,XUngrabKeyboard,(Display* a,Time b),(a,b),return) | ||
| 131 | SDL_X11_SYM(int,XUngrabPointer,(Display* a,Time b),(a,b),return) | ||
| 132 | SDL_X11_SYM(int,XUngrabServer,(Display* a),(a),return) | ||
| 133 | SDL_X11_SYM(int,XUninstallColormap,(Display* a,Colormap b),(a,b),return) | ||
| 134 | SDL_X11_SYM(int,XUnloadFont,(Display* a,Font b),(a,b),return) | ||
| 135 | SDL_X11_SYM(int,XWarpPointer,(Display* a,Window b,Window c,int d,int e,unsigned int f,unsigned int g,int h,int i),(a,b,c,d,e,f,g,h,i),return) | ||
| 136 | SDL_X11_SYM(int,XWindowEvent,(Display* a,Window b,long c,XEvent* d),(a,b,c,d),return) | ||
| 137 | SDL_X11_SYM(Status,XWithdrawWindow,(Display* a,Window b,int c),(a,b,c),return) | ||
| 138 | SDL_X11_SYM(VisualID,XVisualIDFromVisual,(Visual* a),(a),return) | ||
| 139 | SDL_X11_SYM(char*,XGetDefault,(Display* a,_Xconst char* b, _Xconst char* c),(a,b,c),return) | ||
| 140 | SDL_X11_SYM(Bool,XQueryExtension,(Display* a,_Xconst char* b,int* c,int* d,int* e),(a,b,c,d,e),return) | ||
| 141 | SDL_X11_SYM(char *,XDisplayString,(Display* a),(a),return) | ||
| 142 | SDL_X11_SYM(int,XGetErrorText,(Display* a,int b,char* c,int d),(a,b,c,d),return) | ||
| 143 | SDL_X11_SYM(void,_XEatData,(Display* a,unsigned long b),(a,b),) | ||
| 144 | SDL_X11_SYM(void,_XFlush,(Display* a),(a),) | ||
| 145 | SDL_X11_SYM(void,_XFlushGCCache,(Display* a,GC b),(a,b),) | ||
| 146 | SDL_X11_SYM(int,_XRead,(Display* a,char* b,long c),(a,b,c),return) | ||
| 147 | SDL_X11_SYM(void,_XReadPad,(Display* a,char* b,long c),(a,b,c),) | ||
| 148 | SDL_X11_SYM(void,_XSend,(Display* a,_Xconst char* b,long c),(a,b,c),) | ||
| 149 | SDL_X11_SYM(Status,_XReply,(Display* a,xReply* b,int c,Bool d),(a,b,c,d),return) | ||
| 150 | SDL_X11_SYM(unsigned long,_XSetLastRequestRead,(Display* a,xGenericReply* b),(a,b),return) | ||
| 151 | SDL_X11_SYM(SDL_X11_XSynchronizeRetType,XSynchronize,(Display* a,Bool b),(a,b),return) | ||
| 152 | SDL_X11_SYM(SDL_X11_XESetWireToEventRetType,XESetWireToEvent,(Display* a,int b,SDL_X11_XESetWireToEventRetType c),(a,b,c),return) | ||
| 153 | SDL_X11_SYM(SDL_X11_XESetEventToWireRetType,XESetEventToWire,(Display* a,int b,SDL_X11_XESetEventToWireRetType c),(a,b,c),return) | ||
| 154 | SDL_X11_SYM(void,XRefreshKeyboardMapping,(XMappingEvent *a),(a),) | ||
| 155 | SDL_X11_SYM(int,XQueryTree,(Display* a,Window b,Window* c,Window* d,Window** e,unsigned int* f),(a,b,c,d,e,f),return) | ||
| 156 | SDL_X11_SYM(Bool,XSupportsLocale,(void),(),return) | ||
| 157 | SDL_X11_SYM(Status,XmbTextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return) | ||
| 158 | SDL_X11_SYM(Region,XCreateRegion,(void),(),return) | ||
| 159 | SDL_X11_SYM(int,XUnionRectWithRegion,(XRectangle *a, Region b, Region c),(a,b,c), return) | ||
| 160 | SDL_X11_SYM(void,XDestroyRegion,(Region),(a),) | ||
| 161 | SDL_X11_SYM(void,XrmInitialize,(void),(),) | ||
| 162 | SDL_X11_SYM(char*,XResourceManagerString,(Display *display),(display),) | ||
| 163 | SDL_X11_SYM(XrmDatabase,XrmGetStringDatabase,(char *data),(data),) | ||
| 164 | SDL_X11_SYM(void,XrmDestroyDatabase,(XrmDatabase db),(db),) | ||
| 165 | SDL_X11_SYM(Bool,XrmGetResource,(XrmDatabase db, char* str_name, char* str_class, char **str_type_return, XrmValue *),(db, str_name, str_class,str_type_return,value_return),) | ||
| 166 | |||
| 167 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 168 | SDL_X11_MODULE(XFIXES) | ||
| 169 | SDL_X11_SYM(PointerBarrier, XFixesCreatePointerBarrier, (Display* a, Window b, int c, int d, int e, int f, int g, int h, int *i),(a,b,c,d,e,f,g,h,i),return) | ||
| 170 | SDL_X11_SYM(void, XFixesDestroyPointerBarrier, (Display* a, PointerBarrier b), (a,b),) | ||
| 171 | SDL_X11_SYM(int, XIBarrierReleasePointer,(Display* a, int b, PointerBarrier c, BarrierEventID d), (a,b,c,d), return) // this is actually Xinput2 | ||
| 172 | SDL_X11_SYM(Status, XFixesQueryVersion,(Display* a, int* b, int* c), (a,b,c), return) | ||
| 173 | SDL_X11_SYM(Status, XFixesSelectSelectionInput, (Display* a, Window b, Atom c, unsigned long d), (a,b,c,d), return) | ||
| 174 | #endif | ||
| 175 | |||
| 176 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 177 | SDL_X11_MODULE(XSYNC) | ||
| 178 | SDL_X11_SYM(Status, XSyncQueryExtension, (Display* a, int* b, int* c), (a, b, c), return) | ||
| 179 | SDL_X11_SYM(Status, XSyncInitialize, (Display* a, int* b, int* c), (a, b, c), return) | ||
| 180 | SDL_X11_SYM(XSyncCounter, XSyncCreateCounter, (Display* a, XSyncValue b), (a, b), return) | ||
| 181 | SDL_X11_SYM(Status, XSyncDestroyCounter, (Display* a, XSyncCounter b), (a, b), return) | ||
| 182 | SDL_X11_SYM(Status, XSyncSetCounter, (Display* a, XSyncCounter b, XSyncValue c), (a, b, c), return) | ||
| 183 | #endif | ||
| 184 | |||
| 185 | #ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS | ||
| 186 | SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return) | ||
| 187 | SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),) | ||
| 188 | #endif | ||
| 189 | |||
| 190 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 191 | SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f),(a,b,c,d,e,f),return) | ||
| 192 | #if NeedWidePrototypes | ||
| 193 | SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return) | ||
| 194 | #else | ||
| 195 | SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return) | ||
| 196 | #endif | ||
| 197 | SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c),(a,b,c),return) | ||
| 198 | SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c),(a,b,c),return) | ||
| 199 | SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c),(a,b,c),return) | ||
| 200 | SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),) | ||
| 201 | SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),) | ||
| 202 | SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c),(a,b,c),return) | ||
| 203 | #endif | ||
| 204 | |||
| 205 | // XKeycodeToKeysym is a deprecated function | ||
| 206 | #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA | ||
| 207 | #pragma GCC diagnostic push | ||
| 208 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
| 209 | #endif | ||
| 210 | #if NeedWidePrototypes | ||
| 211 | SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c),(a,b,c),return) | ||
| 212 | #else | ||
| 213 | SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c),(a,b,c),return) | ||
| 214 | #endif | ||
| 215 | #ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA | ||
| 216 | #pragma GCC diagnostic pop | ||
| 217 | #endif | ||
| 218 | |||
| 219 | #ifdef X_HAVE_UTF8_STRING | ||
| 220 | SDL_X11_MODULE(UTF8) | ||
| 221 | SDL_X11_SYM(int,Xutf8TextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return) | ||
| 222 | SDL_X11_SYM(int,Xutf8LookupString,(XIC a,XKeyPressedEvent* b,char* c,int d,KeySym* e,Status* f),(a,b,c,d,e,f),return) | ||
| 223 | // SDL_X11_SYM(XIC,XCreateIC,(XIM, ...),return) !!! ARGH! | ||
| 224 | SDL_X11_SYM(void,XDestroyIC,(XIC a),(a),) | ||
| 225 | /* SDL_X11_SYM(char*,XGetICValues,(XIC, ...),return) !!! ARGH! */ | ||
| 226 | /* SDL_X11_SYM(char*,XSetICValues,(XIC, ...),return) !!! ARGH! */ | ||
| 227 | /* SDL_X11_SYM(XVaNestedList,XVaCreateNestedList,(int, ...),return) !!! ARGH! */ | ||
| 228 | SDL_X11_SYM(void,XSetICFocus,(XIC a),(a),) | ||
| 229 | SDL_X11_SYM(void,XUnsetICFocus,(XIC a),(a),) | ||
| 230 | SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d),(a,b,c,d),return) | ||
| 231 | SDL_X11_SYM(Status,XCloseIM,(XIM a),(a),return) | ||
| 232 | SDL_X11_SYM(void,Xutf8DrawString,(Display *a, Drawable b, XFontSet c, GC d, int e, int f, _Xconst char *g, int h),(a,b,c,d,e,f,g,h),) | ||
| 233 | SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e),(a,b,c,d,e),return) | ||
| 234 | SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a),(a),return) | ||
| 235 | SDL_X11_SYM(char*,Xutf8ResetIC,(XIC a),(a),return) | ||
| 236 | #endif | ||
| 237 | |||
| 238 | #ifndef NO_SHARED_MEMORY | ||
| 239 | SDL_X11_MODULE(SHM) | ||
| 240 | SDL_X11_SYM(Status,XShmAttach,(Display* a,XShmSegmentInfo* b),(a,b),return) | ||
| 241 | SDL_X11_SYM(Status,XShmDetach,(Display* a,XShmSegmentInfo* b),(a,b),return) | ||
| 242 | SDL_X11_SYM(Status,XShmPutImage,(Display* a,Drawable b,GC c,XImage* d,int e,int f,int g,int h,unsigned int i,unsigned int j,Bool k),(a,b,c,d,e,f,g,h,i,j,k),return) | ||
| 243 | SDL_X11_SYM(XImage*,XShmCreateImage,(Display* a,Visual* b,unsigned int c,int d,char* e,XShmSegmentInfo* f,unsigned int g,unsigned int h),(a,b,c,d,e,f,g,h),return) | ||
| 244 | SDL_X11_SYM(Pixmap,XShmCreatePixmap,(Display *a,Drawable b,char* c,XShmSegmentInfo* d, unsigned int e, unsigned int f, unsigned int g),(a,b,c,d,e,f,g),return) | ||
| 245 | SDL_X11_SYM(Bool,XShmQueryExtension,(Display* a),(a),return) | ||
| 246 | #endif | ||
| 247 | |||
| 248 | /* | ||
| 249 | * Not required...these only exist in code in headers on some 64-bit platforms, | ||
| 250 | * and are removed via macros elsewhere, so it's safe for them to be missing. | ||
| 251 | */ | ||
| 252 | #ifdef LONG64 | ||
| 253 | SDL_X11_MODULE(IO_32BIT) | ||
| 254 | SDL_X11_SYM(int,_XData32,(Display *dpy,register _Xconst long *data,unsigned len),(dpy,data,len),return) | ||
| 255 | SDL_X11_SYM(void,_XRead32,(Display *dpy,register long *data,long len),(dpy,data,len),) | ||
| 256 | #endif | ||
| 257 | |||
| 258 | /* | ||
| 259 | * These only show up on some variants of Unix. | ||
| 260 | */ | ||
| 261 | #ifdef SDL_PLATFORM_OSF | ||
| 262 | SDL_X11_MODULE(OSF_ENTRY_POINTS) | ||
| 263 | SDL_X11_SYM(void,_SmtBufferOverflow,(Display *dpy,register smtDisplayPtr p),(dpy,p),) | ||
| 264 | SDL_X11_SYM(void,_SmtIpError,(Display *dpy,register smtDisplayPtr p,int i),(dpy,p,i),) | ||
| 265 | SDL_X11_SYM(int,ipAllocateData,(ChannelPtr a,IPCard b,IPDataPtr * c),(a,b,c),return) | ||
| 266 | SDL_X11_SYM(int,ipUnallocateAndSendData,(ChannelPtr a,IPCard b),(a,b),return) | ||
| 267 | #endif | ||
| 268 | |||
| 269 | // XCursor support | ||
| 270 | #ifdef SDL_VIDEO_DRIVER_X11_XCURSOR | ||
| 271 | SDL_X11_MODULE(XCURSOR) | ||
| 272 | SDL_X11_SYM(XcursorImage*,XcursorImageCreate,(int a,int b),(a,b),return) | ||
| 273 | SDL_X11_SYM(void,XcursorImageDestroy,(XcursorImage *a),(a),) | ||
| 274 | SDL_X11_SYM(Cursor,XcursorImageLoadCursor,(Display *a,const XcursorImage *b),(a,b),return) | ||
| 275 | SDL_X11_SYM(Cursor,XcursorLibraryLoadCursor,(Display *a, const char *b),(a,b),return) | ||
| 276 | #endif | ||
| 277 | |||
| 278 | // Xdbe support | ||
| 279 | #ifdef SDL_VIDEO_DRIVER_X11_XDBE | ||
| 280 | SDL_X11_MODULE(XDBE) | ||
| 281 | SDL_X11_SYM(Status,XdbeQueryExtension,(Display *dpy,int *major_version_return,int *minor_version_return),(dpy,major_version_return,minor_version_return),return) | ||
| 282 | SDL_X11_SYM(XdbeBackBuffer,XdbeAllocateBackBufferName,(Display *dpy,Window window,XdbeSwapAction swap_action),(dpy,window,swap_action),return) | ||
| 283 | SDL_X11_SYM(Status,XdbeDeallocateBackBufferName,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return) | ||
| 284 | SDL_X11_SYM(Status,XdbeSwapBuffers,(Display *dpy,XdbeSwapInfo *swap_info,int num_windows),(dpy,swap_info,num_windows),return) | ||
| 285 | SDL_X11_SYM(Status,XdbeBeginIdiom,(Display *dpy),(dpy),return) | ||
| 286 | SDL_X11_SYM(Status,XdbeEndIdiom,(Display *dpy),(dpy),return) | ||
| 287 | SDL_X11_SYM(XdbeScreenVisualInfo*,XdbeGetVisualInfo,(Display *dpy,Drawable *screen_specifiers,int *num_screens),(dpy,screen_specifiers,num_screens),return) | ||
| 288 | SDL_X11_SYM(void,XdbeFreeVisualInfo,(XdbeScreenVisualInfo *visual_info),(visual_info),) | ||
| 289 | SDL_X11_SYM(XdbeBackBufferAttributes*,XdbeGetBackBufferAttributes,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return) | ||
| 290 | #endif | ||
| 291 | |||
| 292 | // XInput2 support for multiple mice, tablets, etc. | ||
| 293 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 294 | SDL_X11_MODULE(XINPUT2) | ||
| 295 | SDL_X11_SYM(XIDeviceInfo*,XIQueryDevice,(Display *a,int b,int *c),(a,b,c),return) | ||
| 296 | SDL_X11_SYM(void,XIFreeDeviceInfo,(XIDeviceInfo *a),(a),) | ||
| 297 | SDL_X11_SYM(int,XISelectEvents,(Display *a,Window b,XIEventMask *c,int d),(a,b,c,d),return) | ||
| 298 | SDL_X11_SYM(int,XIGrabTouchBegin,(Display *a,int b,Window c,int d,XIEventMask *e,int f,XIGrabModifiers *g),(a,b,c,d,e,f,g),return) | ||
| 299 | SDL_X11_SYM(int,XIUngrabTouchBegin, (Display *a,int b,Window c, int d,XIGrabModifiers *e),(a, b, c, d, e),return) | ||
| 300 | SDL_X11_SYM(Status,XIQueryVersion,(Display *a,int *b,int *c),(a,b,c),return) | ||
| 301 | SDL_X11_SYM(XIEventMask*,XIGetSelectedEvents,(Display *a,Window b,int *c),(a,b,c),return) | ||
| 302 | SDL_X11_SYM(Bool,XIGetClientPointer,(Display *a,Window b,int *c),(a,b,c),return) | ||
| 303 | SDL_X11_SYM(Bool,XIWarpPointer,(Display *a,int b,Window c,Window d,double e,double f,int g,int h,double i,double j),(a,b,c,d,e,f,g,h,i,j),return) | ||
| 304 | SDL_X11_SYM(Status,XIGetProperty,(Display *a,int b,Atom c,long d,long e,Bool f, Atom g, Atom *h, int *i, unsigned long *j, unsigned long *k, unsigned char **l),(a,b,c,d,e,f,g,h,i,j,k,l),return) | ||
| 305 | #endif | ||
| 306 | |||
| 307 | // XRandR support | ||
| 308 | #ifdef SDL_VIDEO_DRIVER_X11_XRANDR | ||
| 309 | SDL_X11_MODULE(XRANDR) | ||
| 310 | SDL_X11_SYM(Status,XRRQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return) | ||
| 311 | SDL_X11_SYM(Bool,XRRQueryExtension,(Display *dpy,int *event_base_return,int *error_base_return),(dpy,event_base_return,error_base_return),return) | ||
| 312 | SDL_X11_SYM(XRRScreenConfiguration *,XRRGetScreenInfo,(Display *dpy,Drawable draw),(dpy,draw),return) | ||
| 313 | SDL_X11_SYM(SizeID,XRRConfigCurrentConfiguration,(XRRScreenConfiguration *config,Rotation *rotation),(config,rotation),return) | ||
| 314 | SDL_X11_SYM(short,XRRConfigCurrentRate,(XRRScreenConfiguration *config),(config),return) | ||
| 315 | SDL_X11_SYM(short *,XRRConfigRates,(XRRScreenConfiguration *config,int sizeID,int *nrates),(config,sizeID,nrates),return) | ||
| 316 | SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config,int *nsizes),(config,nsizes),return) | ||
| 317 | SDL_X11_SYM(Status,XRRSetScreenConfigAndRate,(Display *dpy,XRRScreenConfiguration *config,Drawable draw,int size_index,Rotation rotation,short rate,Time timestamp),(dpy,config,draw,size_index,rotation,rate,timestamp),return) | ||
| 318 | SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),) | ||
| 319 | SDL_X11_SYM(void,XRRSetScreenSize,(Display *dpy, Window window,int width, int height,int mmWidth, int mmHeight),(dpy,window,width,height,mmWidth,mmHeight),) | ||
| 320 | SDL_X11_SYM(Status,XRRGetScreenSizeRange,(Display *dpy, Window window,int *minWidth, int *minHeight, int *maxWidth, int *maxHeight),(dpy,window,minWidth,minHeight,maxWidth,maxHeight),return) | ||
| 321 | SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResources,(Display *dpy, Window window),(dpy, window),return) | ||
| 322 | SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResourcesCurrent,(Display *dpy, Window window),(dpy, window),return) | ||
| 323 | SDL_X11_SYM(void,XRRFreeScreenResources,(XRRScreenResources *resources),(resources),) | ||
| 324 | SDL_X11_SYM(XRROutputInfo *,XRRGetOutputInfo,(Display *dpy, XRRScreenResources *resources, RROutput output),(dpy,resources,output),return) | ||
| 325 | SDL_X11_SYM(void,XRRFreeOutputInfo,(XRROutputInfo *outputInfo),(outputInfo),) | ||
| 326 | SDL_X11_SYM(XRRCrtcInfo *,XRRGetCrtcInfo,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc),(dpy,resources,crtc),return) | ||
| 327 | SDL_X11_SYM(void,XRRFreeCrtcInfo,(XRRCrtcInfo *crtcInfo),(crtcInfo),) | ||
| 328 | SDL_X11_SYM(Status,XRRSetCrtcConfig,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc, Time timestamp, int x, int y, RRMode mode, Rotation rotation, RROutput *outputs, int noutputs),(dpy,resources,crtc,timestamp,x,y,mode,rotation,outputs,noutputs),return) | ||
| 329 | SDL_X11_SYM(Atom*,XRRListOutputProperties,(Display *dpy, RROutput output, int *nprop),(dpy,output,nprop),return) | ||
| 330 | SDL_X11_SYM(XRRPropertyInfo*,XRRQueryOutputProperty,(Display *dpy,RROutput output, Atom property),(dpy,output,property),return) | ||
| 331 | SDL_X11_SYM(int,XRRGetOutputProperty,(Display *dpy,RROutput output, Atom property, long offset, long length, Bool _delete, Bool pending, Atom req_type, Atom *actual_type, int *actual_format, unsigned long *nitems, unsigned long *bytes_after, unsigned char **prop),(dpy,output,property,offset,length, _delete, pending, req_type, actual_type, actual_format, nitems, bytes_after, prop),return) | ||
| 332 | SDL_X11_SYM(RROutput,XRRGetOutputPrimary,(Display *dpy,Window window),(dpy,window),return) | ||
| 333 | SDL_X11_SYM(void,XRRSelectInput,(Display *dpy, Window window, int mask),(dpy,window,mask),) | ||
| 334 | SDL_X11_SYM(Status,XRRGetCrtcTransform,(Display *dpy,RRCrtc crtc,XRRCrtcTransformAttributes **attributes),(dpy,crtc,attributes),return) | ||
| 335 | #endif | ||
| 336 | |||
| 337 | // MIT-SCREEN-SAVER support | ||
| 338 | #ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER | ||
| 339 | SDL_X11_MODULE(XSS) | ||
| 340 | SDL_X11_SYM(Bool,XScreenSaverQueryExtension,(Display *dpy,int *event_base,int *error_base),(dpy,event_base,error_base),return) | ||
| 341 | SDL_X11_SYM(Status,XScreenSaverQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return) | ||
| 342 | SDL_X11_SYM(void,XScreenSaverSuspend,(Display *dpy,Bool suspend),(dpy,suspend),return) | ||
| 343 | #endif | ||
| 344 | |||
| 345 | #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 346 | SDL_X11_MODULE(XSHAPE) | ||
| 347 | SDL_X11_SYM(void,XShapeCombineMask,(Display *dpy,Window dest,int dest_kind,int x_off,int y_off,Pixmap src,int op),(dpy,dest,dest_kind,x_off,y_off,src,op),) | ||
| 348 | SDL_X11_SYM(void,XShapeCombineRegion,(Display *a,Window b,int c,int d,int e,Region f,int g),(a,b,c,d,e,f,g),) | ||
| 349 | #endif | ||
| 350 | |||
| 351 | #undef SDL_X11_MODULE | ||
| 352 | #undef SDL_X11_SYM | ||
| 353 | |||
| 354 | /* *INDENT-ON* */ // clang-format on | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c new file mode 100644 index 0000000..5b42c97 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.c | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11video.h" | ||
| 26 | #include "SDL_x11touch.h" | ||
| 27 | #include "SDL_x11xinput2.h" | ||
| 28 | #include "../../events/SDL_touch_c.h" | ||
| 29 | |||
| 30 | void X11_InitTouch(SDL_VideoDevice *_this) | ||
| 31 | { | ||
| 32 | X11_InitXinput2Multitouch(_this); | ||
| 33 | } | ||
| 34 | |||
| 35 | void X11_QuitTouch(SDL_VideoDevice *_this) | ||
| 36 | { | ||
| 37 | SDL_QuitTouch(); | ||
| 38 | } | ||
| 39 | |||
| 40 | void X11_ResetTouch(SDL_VideoDevice *_this) | ||
| 41 | { | ||
| 42 | X11_QuitTouch(_this); | ||
| 43 | X11_InitTouch(_this); | ||
| 44 | } | ||
| 45 | |||
| 46 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h new file mode 100644 index 0000000..548bf07 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11touch.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11touch_h_ | ||
| 24 | #define SDL_x11touch_h_ | ||
| 25 | |||
| 26 | extern void X11_InitTouch(SDL_VideoDevice *_this); | ||
| 27 | extern void X11_QuitTouch(SDL_VideoDevice *_this); | ||
| 28 | extern void X11_ResetTouch(SDL_VideoDevice *_this); | ||
| 29 | |||
| 30 | #endif // SDL_x11touch_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c new file mode 100644 index 0000000..75862db --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.c | |||
| @@ -0,0 +1,505 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include <unistd.h> // For getpid() and readlink() | ||
| 26 | |||
| 27 | #include "../../core/linux/SDL_system_theme.h" | ||
| 28 | #include "../../events/SDL_keyboard_c.h" | ||
| 29 | #include "../../events/SDL_mouse_c.h" | ||
| 30 | #include "../SDL_pixels_c.h" | ||
| 31 | #include "../SDL_sysvideo.h" | ||
| 32 | |||
| 33 | #include "SDL_x11framebuffer.h" | ||
| 34 | #include "SDL_x11pen.h" | ||
| 35 | #include "SDL_x11touch.h" | ||
| 36 | #include "SDL_x11video.h" | ||
| 37 | #include "SDL_x11xfixes.h" | ||
| 38 | #include "SDL_x11xinput2.h" | ||
| 39 | #include "SDL_x11messagebox.h" | ||
| 40 | #include "SDL_x11shape.h" | ||
| 41 | #include "SDL_x11xsync.h" | ||
| 42 | |||
| 43 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 44 | #include "SDL_x11opengles.h" | ||
| 45 | #endif | ||
| 46 | |||
| 47 | // Initialization/Query functions | ||
| 48 | static bool X11_VideoInit(SDL_VideoDevice *_this); | ||
| 49 | static void X11_VideoQuit(SDL_VideoDevice *_this); | ||
| 50 | |||
| 51 | // X11 driver bootstrap functions | ||
| 52 | |||
| 53 | static void X11_DeleteDevice(SDL_VideoDevice *device) | ||
| 54 | { | ||
| 55 | SDL_VideoData *data = device->internal; | ||
| 56 | if (device->vulkan_config.loader_handle) { | ||
| 57 | device->Vulkan_UnloadLibrary(device); | ||
| 58 | } | ||
| 59 | if (data->display) { | ||
| 60 | X11_XCloseDisplay(data->display); | ||
| 61 | } | ||
| 62 | if (data->request_display) { | ||
| 63 | X11_XCloseDisplay(data->request_display); | ||
| 64 | } | ||
| 65 | SDL_free(data->windowlist); | ||
| 66 | if (device->wakeup_lock) { | ||
| 67 | SDL_DestroyMutex(device->wakeup_lock); | ||
| 68 | } | ||
| 69 | SDL_free(device->internal); | ||
| 70 | SDL_free(device); | ||
| 71 | |||
| 72 | SDL_X11_UnloadSymbols(); | ||
| 73 | } | ||
| 74 | |||
| 75 | static bool X11_IsXWayland(Display *d) | ||
| 76 | { | ||
| 77 | int opcode, event, error; | ||
| 78 | return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True; | ||
| 79 | } | ||
| 80 | |||
| 81 | static bool X11_CheckCurrentDesktop(const char *name) | ||
| 82 | { | ||
| 83 | SDL_Environment *env = SDL_GetEnvironment(); | ||
| 84 | |||
| 85 | const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION"); | ||
| 86 | if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP"); | ||
| 91 | if (desktopVar && SDL_strcasestr(desktopVar, name)) { | ||
| 92 | return true; | ||
| 93 | } | ||
| 94 | |||
| 95 | return false; | ||
| 96 | } | ||
| 97 | |||
| 98 | static SDL_VideoDevice *X11_CreateDevice(void) | ||
| 99 | { | ||
| 100 | SDL_VideoDevice *device; | ||
| 101 | SDL_VideoData *data; | ||
| 102 | const char *display = NULL; // Use the DISPLAY environment variable | ||
| 103 | Display *x11_display = NULL; | ||
| 104 | |||
| 105 | if (!SDL_X11_LoadSymbols()) { | ||
| 106 | return NULL; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* Need for threading gl calls. This is also required for the proprietary | ||
| 110 | nVidia driver to be threaded. */ | ||
| 111 | X11_XInitThreads(); | ||
| 112 | |||
| 113 | // Open the display first to be sure that X11 is available | ||
| 114 | x11_display = X11_XOpenDisplay(display); | ||
| 115 | |||
| 116 | if (!x11_display) { | ||
| 117 | SDL_X11_UnloadSymbols(); | ||
| 118 | return NULL; | ||
| 119 | } | ||
| 120 | |||
| 121 | // Initialize all variables that we clean on shutdown | ||
| 122 | device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice)); | ||
| 123 | if (!device) { | ||
| 124 | return NULL; | ||
| 125 | } | ||
| 126 | data = (struct SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData)); | ||
| 127 | if (!data) { | ||
| 128 | SDL_free(device); | ||
| 129 | return NULL; | ||
| 130 | } | ||
| 131 | device->internal = data; | ||
| 132 | |||
| 133 | data->global_mouse_changed = true; | ||
| 134 | |||
| 135 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 136 | data->active_cursor_confined_window = NULL; | ||
| 137 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 138 | |||
| 139 | data->display = x11_display; | ||
| 140 | data->request_display = X11_XOpenDisplay(display); | ||
| 141 | if (!data->request_display) { | ||
| 142 | X11_XCloseDisplay(data->display); | ||
| 143 | SDL_free(device->internal); | ||
| 144 | SDL_free(device); | ||
| 145 | SDL_X11_UnloadSymbols(); | ||
| 146 | return NULL; | ||
| 147 | } | ||
| 148 | |||
| 149 | device->wakeup_lock = SDL_CreateMutex(); | ||
| 150 | |||
| 151 | #ifdef X11_DEBUG | ||
| 152 | X11_XSynchronize(data->display, True); | ||
| 153 | #endif | ||
| 154 | |||
| 155 | /* Steam Deck will have an on-screen keyboard, so check their environment | ||
| 156 | * variable so we can make use of SDL_StartTextInput. | ||
| 157 | */ | ||
| 158 | data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", false); | ||
| 159 | |||
| 160 | // Set the function pointers | ||
| 161 | device->VideoInit = X11_VideoInit; | ||
| 162 | device->VideoQuit = X11_VideoQuit; | ||
| 163 | device->ResetTouch = X11_ResetTouch; | ||
| 164 | device->GetDisplayModes = X11_GetDisplayModes; | ||
| 165 | device->GetDisplayBounds = X11_GetDisplayBounds; | ||
| 166 | device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds; | ||
| 167 | device->GetWindowICCProfile = X11_GetWindowICCProfile; | ||
| 168 | device->SetDisplayMode = X11_SetDisplayMode; | ||
| 169 | device->SuspendScreenSaver = X11_SuspendScreenSaver; | ||
| 170 | device->PumpEvents = X11_PumpEvents; | ||
| 171 | device->WaitEventTimeout = X11_WaitEventTimeout; | ||
| 172 | device->SendWakeupEvent = X11_SendWakeupEvent; | ||
| 173 | |||
| 174 | device->CreateSDLWindow = X11_CreateWindow; | ||
| 175 | device->SetWindowTitle = X11_SetWindowTitle; | ||
| 176 | device->SetWindowIcon = X11_SetWindowIcon; | ||
| 177 | device->SetWindowPosition = X11_SetWindowPosition; | ||
| 178 | device->SetWindowSize = X11_SetWindowSize; | ||
| 179 | device->SetWindowMinimumSize = X11_SetWindowMinimumSize; | ||
| 180 | device->SetWindowMaximumSize = X11_SetWindowMaximumSize; | ||
| 181 | device->SetWindowAspectRatio = X11_SetWindowAspectRatio; | ||
| 182 | device->GetWindowBordersSize = X11_GetWindowBordersSize; | ||
| 183 | device->SetWindowOpacity = X11_SetWindowOpacity; | ||
| 184 | device->SetWindowParent = X11_SetWindowParent; | ||
| 185 | device->SetWindowModal = X11_SetWindowModal; | ||
| 186 | device->ShowWindow = X11_ShowWindow; | ||
| 187 | device->HideWindow = X11_HideWindow; | ||
| 188 | device->RaiseWindow = X11_RaiseWindow; | ||
| 189 | device->MaximizeWindow = X11_MaximizeWindow; | ||
| 190 | device->MinimizeWindow = X11_MinimizeWindow; | ||
| 191 | device->RestoreWindow = X11_RestoreWindow; | ||
| 192 | device->SetWindowBordered = X11_SetWindowBordered; | ||
| 193 | device->SetWindowResizable = X11_SetWindowResizable; | ||
| 194 | device->SetWindowAlwaysOnTop = X11_SetWindowAlwaysOnTop; | ||
| 195 | device->SetWindowFullscreen = X11_SetWindowFullscreen; | ||
| 196 | device->SetWindowMouseGrab = X11_SetWindowMouseGrab; | ||
| 197 | device->SetWindowKeyboardGrab = X11_SetWindowKeyboardGrab; | ||
| 198 | device->DestroyWindow = X11_DestroyWindow; | ||
| 199 | device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer; | ||
| 200 | device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer; | ||
| 201 | device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer; | ||
| 202 | device->SetWindowHitTest = X11_SetWindowHitTest; | ||
| 203 | device->AcceptDragAndDrop = X11_AcceptDragAndDrop; | ||
| 204 | device->UpdateWindowShape = X11_UpdateWindowShape; | ||
| 205 | device->FlashWindow = X11_FlashWindow; | ||
| 206 | device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu; | ||
| 207 | device->SetWindowFocusable = X11_SetWindowFocusable; | ||
| 208 | device->SyncWindow = X11_SyncWindow; | ||
| 209 | |||
| 210 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 211 | device->SetWindowMouseRect = X11_SetWindowMouseRect; | ||
| 212 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 213 | |||
| 214 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 215 | device->GL_LoadLibrary = X11_GL_LoadLibrary; | ||
| 216 | device->GL_GetProcAddress = X11_GL_GetProcAddress; | ||
| 217 | device->GL_UnloadLibrary = X11_GL_UnloadLibrary; | ||
| 218 | device->GL_CreateContext = X11_GL_CreateContext; | ||
| 219 | device->GL_MakeCurrent = X11_GL_MakeCurrent; | ||
| 220 | device->GL_SetSwapInterval = X11_GL_SetSwapInterval; | ||
| 221 | device->GL_GetSwapInterval = X11_GL_GetSwapInterval; | ||
| 222 | device->GL_SwapWindow = X11_GL_SwapWindow; | ||
| 223 | device->GL_DestroyContext = X11_GL_DestroyContext; | ||
| 224 | device->GL_GetEGLSurface = NULL; | ||
| 225 | #endif | ||
| 226 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 227 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 228 | if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) { | ||
| 229 | #endif | ||
| 230 | device->GL_LoadLibrary = X11_GLES_LoadLibrary; | ||
| 231 | device->GL_GetProcAddress = X11_GLES_GetProcAddress; | ||
| 232 | device->GL_UnloadLibrary = X11_GLES_UnloadLibrary; | ||
| 233 | device->GL_CreateContext = X11_GLES_CreateContext; | ||
| 234 | device->GL_MakeCurrent = X11_GLES_MakeCurrent; | ||
| 235 | device->GL_SetSwapInterval = X11_GLES_SetSwapInterval; | ||
| 236 | device->GL_GetSwapInterval = X11_GLES_GetSwapInterval; | ||
| 237 | device->GL_SwapWindow = X11_GLES_SwapWindow; | ||
| 238 | device->GL_DestroyContext = X11_GLES_DestroyContext; | ||
| 239 | device->GL_GetEGLSurface = X11_GLES_GetEGLSurface; | ||
| 240 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 241 | } | ||
| 242 | #endif | ||
| 243 | #endif | ||
| 244 | |||
| 245 | device->GetTextMimeTypes = X11_GetTextMimeTypes; | ||
| 246 | device->SetClipboardData = X11_SetClipboardData; | ||
| 247 | device->GetClipboardData = X11_GetClipboardData; | ||
| 248 | device->HasClipboardData = X11_HasClipboardData; | ||
| 249 | device->SetPrimarySelectionText = X11_SetPrimarySelectionText; | ||
| 250 | device->GetPrimarySelectionText = X11_GetPrimarySelectionText; | ||
| 251 | device->HasPrimarySelectionText = X11_HasPrimarySelectionText; | ||
| 252 | device->StartTextInput = X11_StartTextInput; | ||
| 253 | device->StopTextInput = X11_StopTextInput; | ||
| 254 | device->UpdateTextInputArea = X11_UpdateTextInputArea; | ||
| 255 | device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport; | ||
| 256 | device->ShowScreenKeyboard = X11_ShowScreenKeyboard; | ||
| 257 | device->HideScreenKeyboard = X11_HideScreenKeyboard; | ||
| 258 | device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown; | ||
| 259 | |||
| 260 | device->free = X11_DeleteDevice; | ||
| 261 | |||
| 262 | #ifdef SDL_VIDEO_VULKAN | ||
| 263 | device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary; | ||
| 264 | device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary; | ||
| 265 | device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions; | ||
| 266 | device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface; | ||
| 267 | device->Vulkan_DestroySurface = X11_Vulkan_DestroySurface; | ||
| 268 | device->Vulkan_GetPresentationSupport = X11_Vulkan_GetPresentationSupport; | ||
| 269 | #endif | ||
| 270 | |||
| 271 | #ifdef SDL_USE_LIBDBUS | ||
| 272 | if (SDL_SystemTheme_Init()) | ||
| 273 | device->system_theme = SDL_SystemTheme_Get(); | ||
| 274 | #endif | ||
| 275 | |||
| 276 | device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT; | ||
| 277 | |||
| 278 | /* Openbox doesn't send the new window dimensions when entering fullscreen, so the events must be synthesized. | ||
| 279 | * This is otherwise not wanted, as it can break fullscreen window positioning on multi-monitor configurations. | ||
| 280 | */ | ||
| 281 | if (!X11_CheckCurrentDesktop("openbox")) { | ||
| 282 | device->device_caps |= VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES; | ||
| 283 | } | ||
| 284 | |||
| 285 | data->is_xwayland = X11_IsXWayland(x11_display); | ||
| 286 | if (data->is_xwayland) { | ||
| 287 | device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED | | ||
| 288 | VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS; | ||
| 289 | } | ||
| 290 | |||
| 291 | return device; | ||
| 292 | } | ||
| 293 | |||
| 294 | VideoBootStrap X11_bootstrap = { | ||
| 295 | "x11", "SDL X11 video driver", | ||
| 296 | X11_CreateDevice, | ||
| 297 | X11_ShowMessageBox, | ||
| 298 | false | ||
| 299 | }; | ||
| 300 | |||
| 301 | static int (*handler)(Display *, XErrorEvent *) = NULL; | ||
| 302 | static int X11_CheckWindowManagerErrorHandler(Display *d, XErrorEvent *e) | ||
| 303 | { | ||
| 304 | if (e->error_code == BadWindow) { | ||
| 305 | return 0; | ||
| 306 | } else { | ||
| 307 | return handler(d, e); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | static void X11_CheckWindowManager(SDL_VideoDevice *_this) | ||
| 312 | { | ||
| 313 | SDL_VideoData *data = _this->internal; | ||
| 314 | Display *display = data->display; | ||
| 315 | Atom _NET_SUPPORTING_WM_CHECK; | ||
| 316 | int status, real_format; | ||
| 317 | Atom real_type; | ||
| 318 | unsigned long items_read = 0, items_left = 0; | ||
| 319 | unsigned char *propdata = NULL; | ||
| 320 | Window wm_window = 0; | ||
| 321 | #ifdef DEBUG_WINDOW_MANAGER | ||
| 322 | char *wm_name; | ||
| 323 | #endif | ||
| 324 | |||
| 325 | // Set up a handler to gracefully catch errors | ||
| 326 | X11_XSync(display, False); | ||
| 327 | handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler); | ||
| 328 | |||
| 329 | _NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); | ||
| 330 | status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); | ||
| 331 | if (status == Success) { | ||
| 332 | if (items_read) { | ||
| 333 | wm_window = ((Window *)propdata)[0]; | ||
| 334 | } | ||
| 335 | if (propdata) { | ||
| 336 | X11_XFree(propdata); | ||
| 337 | propdata = NULL; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | if (wm_window) { | ||
| 342 | status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata); | ||
| 343 | if (status != Success || !items_read || wm_window != ((Window *)propdata)[0]) { | ||
| 344 | wm_window = None; | ||
| 345 | } | ||
| 346 | if (status == Success && propdata) { | ||
| 347 | X11_XFree(propdata); | ||
| 348 | propdata = NULL; | ||
| 349 | } | ||
| 350 | } | ||
| 351 | |||
| 352 | // Reset the error handler, we're done checking | ||
| 353 | X11_XSync(display, False); | ||
| 354 | X11_XSetErrorHandler(handler); | ||
| 355 | |||
| 356 | if (!wm_window) { | ||
| 357 | #ifdef DEBUG_WINDOW_MANAGER | ||
| 358 | printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n"); | ||
| 359 | #endif | ||
| 360 | return; | ||
| 361 | } | ||
| 362 | data->net_wm = true; | ||
| 363 | |||
| 364 | #ifdef DEBUG_WINDOW_MANAGER | ||
| 365 | wm_name = X11_GetWindowTitle(_this, wm_window); | ||
| 366 | printf("Window manager: %s\n", wm_name); | ||
| 367 | SDL_free(wm_name); | ||
| 368 | #endif | ||
| 369 | } | ||
| 370 | |||
| 371 | static bool X11_VideoInit(SDL_VideoDevice *_this) | ||
| 372 | { | ||
| 373 | SDL_VideoData *data = _this->internal; | ||
| 374 | |||
| 375 | // Get the process PID to be associated to the window | ||
| 376 | data->pid = getpid(); | ||
| 377 | |||
| 378 | // I have no idea how random this actually is, or has to be. | ||
| 379 | data->window_group = (XID)(((size_t)data->pid) ^ ((size_t)_this)); | ||
| 380 | |||
| 381 | // Look up some useful Atoms | ||
| 382 | #define GET_ATOM(X) data->atoms.X = X11_XInternAtom(data->display, #X, False) | ||
| 383 | GET_ATOM(WM_PROTOCOLS); | ||
| 384 | GET_ATOM(WM_DELETE_WINDOW); | ||
| 385 | GET_ATOM(WM_TAKE_FOCUS); | ||
| 386 | GET_ATOM(WM_NAME); | ||
| 387 | GET_ATOM(WM_TRANSIENT_FOR); | ||
| 388 | GET_ATOM(_NET_WM_STATE); | ||
| 389 | GET_ATOM(_NET_WM_STATE_HIDDEN); | ||
| 390 | GET_ATOM(_NET_WM_STATE_FOCUSED); | ||
| 391 | GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT); | ||
| 392 | GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ); | ||
| 393 | GET_ATOM(_NET_WM_STATE_FULLSCREEN); | ||
| 394 | GET_ATOM(_NET_WM_STATE_ABOVE); | ||
| 395 | GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR); | ||
| 396 | GET_ATOM(_NET_WM_STATE_SKIP_PAGER); | ||
| 397 | GET_ATOM(_NET_WM_MOVERESIZE); | ||
| 398 | GET_ATOM(_NET_WM_STATE_MODAL); | ||
| 399 | GET_ATOM(_NET_WM_ALLOWED_ACTIONS); | ||
| 400 | GET_ATOM(_NET_WM_ACTION_FULLSCREEN); | ||
| 401 | GET_ATOM(_NET_WM_NAME); | ||
| 402 | GET_ATOM(_NET_WM_ICON_NAME); | ||
| 403 | GET_ATOM(_NET_WM_ICON); | ||
| 404 | GET_ATOM(_NET_WM_PING); | ||
| 405 | GET_ATOM(_NET_WM_SYNC_REQUEST); | ||
| 406 | GET_ATOM(_NET_WM_SYNC_REQUEST_COUNTER); | ||
| 407 | GET_ATOM(_NET_WM_WINDOW_OPACITY); | ||
| 408 | GET_ATOM(_NET_WM_USER_TIME); | ||
| 409 | GET_ATOM(_NET_ACTIVE_WINDOW); | ||
| 410 | GET_ATOM(_NET_FRAME_EXTENTS); | ||
| 411 | GET_ATOM(_SDL_WAKEUP); | ||
| 412 | GET_ATOM(UTF8_STRING); | ||
| 413 | GET_ATOM(PRIMARY); | ||
| 414 | GET_ATOM(CLIPBOARD); | ||
| 415 | GET_ATOM(INCR); | ||
| 416 | GET_ATOM(SDL_SELECTION); | ||
| 417 | GET_ATOM(TARGETS); | ||
| 418 | GET_ATOM(SDL_FORMATS); | ||
| 419 | GET_ATOM(XdndAware); | ||
| 420 | GET_ATOM(XdndEnter); | ||
| 421 | GET_ATOM(XdndLeave); | ||
| 422 | GET_ATOM(XdndPosition); | ||
| 423 | GET_ATOM(XdndStatus); | ||
| 424 | GET_ATOM(XdndTypeList); | ||
| 425 | GET_ATOM(XdndActionCopy); | ||
| 426 | GET_ATOM(XdndDrop); | ||
| 427 | GET_ATOM(XdndFinished); | ||
| 428 | GET_ATOM(XdndSelection); | ||
| 429 | GET_ATOM(XKLAVIER_STATE); | ||
| 430 | |||
| 431 | // Detect the window manager | ||
| 432 | X11_CheckWindowManager(_this); | ||
| 433 | |||
| 434 | if (!X11_InitModes(_this)) { | ||
| 435 | return false; | ||
| 436 | } | ||
| 437 | |||
| 438 | if (!X11_InitXinput2(_this)) { | ||
| 439 | // Assume a mouse and keyboard are attached | ||
| 440 | SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false); | ||
| 441 | SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false); | ||
| 442 | } | ||
| 443 | |||
| 444 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 445 | X11_InitXfixes(_this); | ||
| 446 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 447 | |||
| 448 | X11_InitXsettings(_this); | ||
| 449 | |||
| 450 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 451 | X11_InitXsync(_this); | ||
| 452 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 453 | |||
| 454 | #ifndef X_HAVE_UTF8_STRING | ||
| 455 | #warning X server does not support UTF8_STRING, a feature introduced in 2000! This is likely to become a hard error in a future libSDL3. | ||
| 456 | #endif | ||
| 457 | |||
| 458 | if (!X11_InitKeyboard(_this)) { | ||
| 459 | return false; | ||
| 460 | } | ||
| 461 | X11_InitMouse(_this); | ||
| 462 | |||
| 463 | X11_InitTouch(_this); | ||
| 464 | |||
| 465 | X11_InitPen(_this); | ||
| 466 | |||
| 467 | return true; | ||
| 468 | } | ||
| 469 | |||
| 470 | void X11_VideoQuit(SDL_VideoDevice *_this) | ||
| 471 | { | ||
| 472 | SDL_VideoData *data = _this->internal; | ||
| 473 | |||
| 474 | if (data->clipboard_window) { | ||
| 475 | X11_XDestroyWindow(data->display, data->clipboard_window); | ||
| 476 | } | ||
| 477 | |||
| 478 | if (data->xsettings_window) { | ||
| 479 | X11_XDestroyWindow(data->display, data->xsettings_window); | ||
| 480 | } | ||
| 481 | |||
| 482 | #ifdef X_HAVE_UTF8_STRING | ||
| 483 | if (data->im) { | ||
| 484 | X11_XCloseIM(data->im); | ||
| 485 | } | ||
| 486 | #endif | ||
| 487 | |||
| 488 | X11_QuitModes(_this); | ||
| 489 | X11_QuitKeyboard(_this); | ||
| 490 | X11_QuitMouse(_this); | ||
| 491 | X11_QuitTouch(_this); | ||
| 492 | X11_QuitPen(_this); | ||
| 493 | X11_QuitClipboard(_this); | ||
| 494 | X11_QuitXsettings(_this); | ||
| 495 | } | ||
| 496 | |||
| 497 | bool X11_UseDirectColorVisuals(void) | ||
| 498 | { | ||
| 499 | if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NODIRECTCOLOR, false)) { | ||
| 500 | return false; | ||
| 501 | } | ||
| 502 | return true; | ||
| 503 | } | ||
| 504 | |||
| 505 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h new file mode 100644 index 0000000..a336a80 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11video.h | |||
| @@ -0,0 +1,177 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11video_h_ | ||
| 24 | #define SDL_x11video_h_ | ||
| 25 | |||
| 26 | #include "../SDL_sysvideo.h" | ||
| 27 | |||
| 28 | #include "../../core/linux/SDL_dbus.h" | ||
| 29 | #include "../../core/linux/SDL_ime.h" | ||
| 30 | |||
| 31 | #include "SDL_x11dyn.h" | ||
| 32 | |||
| 33 | #include "SDL_x11clipboard.h" | ||
| 34 | #include "SDL_x11events.h" | ||
| 35 | #include "SDL_x11keyboard.h" | ||
| 36 | #include "SDL_x11modes.h" | ||
| 37 | #include "SDL_x11mouse.h" | ||
| 38 | #include "SDL_x11opengl.h" | ||
| 39 | #include "SDL_x11settings.h" | ||
| 40 | #include "SDL_x11window.h" | ||
| 41 | #include "SDL_x11vulkan.h" | ||
| 42 | |||
| 43 | // Private display data | ||
| 44 | |||
| 45 | struct SDL_VideoData | ||
| 46 | { | ||
| 47 | Display *display; | ||
| 48 | Display *request_display; | ||
| 49 | pid_t pid; | ||
| 50 | XIM im; | ||
| 51 | Uint64 screensaver_activity; | ||
| 52 | int numwindows; | ||
| 53 | SDL_WindowData **windowlist; | ||
| 54 | int windowlistlength; | ||
| 55 | XID window_group; | ||
| 56 | Window clipboard_window; | ||
| 57 | SDLX11_ClipboardData clipboard; | ||
| 58 | SDLX11_ClipboardData primary_selection; | ||
| 59 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 60 | SDL_Window *active_cursor_confined_window; | ||
| 61 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 62 | Window xsettings_window; | ||
| 63 | SDLX11_SettingsData xsettings_data; | ||
| 64 | |||
| 65 | // This is true for ICCCM2.0-compliant window managers | ||
| 66 | bool net_wm; | ||
| 67 | |||
| 68 | // Useful atoms | ||
| 69 | struct { | ||
| 70 | Atom WM_PROTOCOLS; | ||
| 71 | Atom WM_DELETE_WINDOW; | ||
| 72 | Atom WM_TAKE_FOCUS; | ||
| 73 | Atom WM_NAME; | ||
| 74 | Atom WM_TRANSIENT_FOR; | ||
| 75 | Atom _NET_WM_STATE; | ||
| 76 | Atom _NET_WM_STATE_HIDDEN; | ||
| 77 | Atom _NET_WM_STATE_FOCUSED; | ||
| 78 | Atom _NET_WM_STATE_MAXIMIZED_VERT; | ||
| 79 | Atom _NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 80 | Atom _NET_WM_STATE_FULLSCREEN; | ||
| 81 | Atom _NET_WM_STATE_ABOVE; | ||
| 82 | Atom _NET_WM_STATE_SKIP_TASKBAR; | ||
| 83 | Atom _NET_WM_STATE_SKIP_PAGER; | ||
| 84 | Atom _NET_WM_STATE_MODAL; | ||
| 85 | Atom _NET_WM_MOVERESIZE; | ||
| 86 | Atom _NET_WM_ALLOWED_ACTIONS; | ||
| 87 | Atom _NET_WM_ACTION_FULLSCREEN; | ||
| 88 | Atom _NET_WM_NAME; | ||
| 89 | Atom _NET_WM_ICON_NAME; | ||
| 90 | Atom _NET_WM_ICON; | ||
| 91 | Atom _NET_WM_PING; | ||
| 92 | Atom _NET_WM_SYNC_REQUEST; | ||
| 93 | Atom _NET_WM_SYNC_REQUEST_COUNTER; | ||
| 94 | Atom _NET_WM_WINDOW_OPACITY; | ||
| 95 | Atom _NET_WM_USER_TIME; | ||
| 96 | Atom _NET_ACTIVE_WINDOW; | ||
| 97 | Atom _NET_FRAME_EXTENTS; | ||
| 98 | Atom _SDL_WAKEUP; | ||
| 99 | Atom UTF8_STRING; | ||
| 100 | Atom PRIMARY; | ||
| 101 | Atom CLIPBOARD; | ||
| 102 | Atom INCR; | ||
| 103 | Atom SDL_SELECTION; | ||
| 104 | Atom TARGETS; | ||
| 105 | Atom SDL_FORMATS; | ||
| 106 | Atom XdndAware; | ||
| 107 | Atom XdndEnter; | ||
| 108 | Atom XdndLeave; | ||
| 109 | Atom XdndPosition; | ||
| 110 | Atom XdndStatus; | ||
| 111 | Atom XdndTypeList; | ||
| 112 | Atom XdndActionCopy; | ||
| 113 | Atom XdndDrop; | ||
| 114 | Atom XdndFinished; | ||
| 115 | Atom XdndSelection; | ||
| 116 | Atom XKLAVIER_STATE; | ||
| 117 | |||
| 118 | // Pen atoms (these have names that don't map well to C symbols) | ||
| 119 | Atom pen_atom_device_product_id; | ||
| 120 | Atom pen_atom_abs_pressure; | ||
| 121 | Atom pen_atom_abs_tilt_x; | ||
| 122 | Atom pen_atom_abs_tilt_y; | ||
| 123 | Atom pen_atom_wacom_serial_ids; | ||
| 124 | Atom pen_atom_wacom_tool_type; | ||
| 125 | } atoms; | ||
| 126 | |||
| 127 | SDL_Scancode key_layout[256]; | ||
| 128 | bool selection_waiting; | ||
| 129 | bool selection_incr_waiting; | ||
| 130 | |||
| 131 | bool broken_pointer_grab; // true if XGrabPointer seems unreliable. | ||
| 132 | |||
| 133 | Uint64 last_mode_change_deadline; | ||
| 134 | |||
| 135 | bool global_mouse_changed; | ||
| 136 | SDL_Point global_mouse_position; | ||
| 137 | Uint32 global_mouse_buttons; | ||
| 138 | |||
| 139 | SDL_XInput2DeviceInfo *mouse_device_info; | ||
| 140 | int xinput_master_pointer_device; | ||
| 141 | bool xinput_hierarchy_changed; | ||
| 142 | |||
| 143 | int xrandr_event_base; | ||
| 144 | struct | ||
| 145 | { | ||
| 146 | #ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM | ||
| 147 | XkbDescPtr desc_ptr; | ||
| 148 | #endif | ||
| 149 | int event; | ||
| 150 | unsigned int current_group; | ||
| 151 | unsigned int xkb_modifiers; | ||
| 152 | |||
| 153 | SDL_Keymod sdl_modifiers; | ||
| 154 | |||
| 155 | Uint32 numlock_mask; | ||
| 156 | Uint32 scrolllock_mask; | ||
| 157 | } xkb; | ||
| 158 | |||
| 159 | KeyCode filter_code; | ||
| 160 | Time filter_time; | ||
| 161 | |||
| 162 | #ifdef SDL_VIDEO_VULKAN | ||
| 163 | // Vulkan variables only valid if _this->vulkan_config.loader_handle is not NULL | ||
| 164 | SDL_SharedObject *vulkan_xlib_xcb_library; | ||
| 165 | PFN_XGetXCBConnection vulkan_XGetXCBConnection; | ||
| 166 | #endif | ||
| 167 | |||
| 168 | // Used to interact with the on-screen keyboard | ||
| 169 | bool is_steam_deck; | ||
| 170 | bool steam_keyboard_open; | ||
| 171 | |||
| 172 | bool is_xwayland; | ||
| 173 | }; | ||
| 174 | |||
| 175 | extern bool X11_UseDirectColorVisuals(void); | ||
| 176 | |||
| 177 | #endif // SDL_x11video_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c new file mode 100644 index 0000000..065549b --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.c | |||
| @@ -0,0 +1,290 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11) | ||
| 24 | |||
| 25 | #include "../SDL_vulkan_internal.h" | ||
| 26 | |||
| 27 | #include "SDL_x11video.h" | ||
| 28 | |||
| 29 | #include "SDL_x11vulkan.h" | ||
| 30 | |||
| 31 | #include <X11/Xlib.h> | ||
| 32 | // #include <xcb/xcb.h> | ||
| 33 | |||
| 34 | #ifdef SDL_PLATFORM_OPENBSD | ||
| 35 | #define DEFAULT_VULKAN "libvulkan.so" | ||
| 36 | #define DEFAULT_X11_XCB "libX11-xcb.so" | ||
| 37 | #else | ||
| 38 | #define DEFAULT_VULKAN "libvulkan.so.1" | ||
| 39 | #define DEFAULT_X11_XCB "libX11-xcb.so.1" | ||
| 40 | #endif | ||
| 41 | |||
| 42 | /* | ||
| 43 | typedef uint32_t xcb_window_t; | ||
| 44 | typedef uint32_t xcb_visualid_t; | ||
| 45 | */ | ||
| 46 | |||
| 47 | bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path) | ||
| 48 | { | ||
| 49 | SDL_VideoData *videoData = _this->internal; | ||
| 50 | VkExtensionProperties *extensions = NULL; | ||
| 51 | Uint32 extensionCount = 0; | ||
| 52 | bool hasSurfaceExtension = false; | ||
| 53 | bool hasXlibSurfaceExtension = false; | ||
| 54 | bool hasXCBSurfaceExtension = false; | ||
| 55 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; | ||
| 56 | Uint32 i; | ||
| 57 | if (_this->vulkan_config.loader_handle) { | ||
| 58 | return SDL_SetError("Vulkan already loaded"); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Load the Vulkan loader library | ||
| 62 | if (!path) { | ||
| 63 | path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY); | ||
| 64 | } | ||
| 65 | if (!path) { | ||
| 66 | path = DEFAULT_VULKAN; | ||
| 67 | } | ||
| 68 | _this->vulkan_config.loader_handle = SDL_LoadObject(path); | ||
| 69 | if (!_this->vulkan_config.loader_handle) { | ||
| 70 | return false; | ||
| 71 | } | ||
| 72 | SDL_strlcpy(_this->vulkan_config.loader_path, path, SDL_arraysize(_this->vulkan_config.loader_path)); | ||
| 73 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( | ||
| 74 | _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr"); | ||
| 75 | if (!vkGetInstanceProcAddr) { | ||
| 76 | goto fail; | ||
| 77 | } | ||
| 78 | _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; | ||
| 79 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties = | ||
| 80 | (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( | ||
| 81 | VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties"); | ||
| 82 | if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { | ||
| 83 | goto fail; | ||
| 84 | } | ||
| 85 | extensions = SDL_Vulkan_CreateInstanceExtensionsList( | ||
| 86 | (PFN_vkEnumerateInstanceExtensionProperties) | ||
| 87 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties, | ||
| 88 | &extensionCount); | ||
| 89 | if (!extensions) { | ||
| 90 | goto fail; | ||
| 91 | } | ||
| 92 | for (i = 0; i < extensionCount; i++) { | ||
| 93 | if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { | ||
| 94 | hasSurfaceExtension = true; | ||
| 95 | } else if (SDL_strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { | ||
| 96 | hasXCBSurfaceExtension = true; | ||
| 97 | } else if (SDL_strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { | ||
| 98 | hasXlibSurfaceExtension = true; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | SDL_free(extensions); | ||
| 102 | if (!hasSurfaceExtension) { | ||
| 103 | SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension"); | ||
| 104 | goto fail; | ||
| 105 | } | ||
| 106 | if (hasXlibSurfaceExtension) { | ||
| 107 | videoData->vulkan_xlib_xcb_library = NULL; | ||
| 108 | } else if (!hasXCBSurfaceExtension) { | ||
| 109 | SDL_SetError("Installed Vulkan doesn't implement either the " VK_KHR_XCB_SURFACE_EXTENSION_NAME "extension or the " VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension"); | ||
| 110 | goto fail; | ||
| 111 | } else { | ||
| 112 | const char *libX11XCBLibraryName = SDL_GetHint(SDL_HINT_X11_XCB_LIBRARY); | ||
| 113 | if (!libX11XCBLibraryName || !*libX11XCBLibraryName) { | ||
| 114 | libX11XCBLibraryName = DEFAULT_X11_XCB; | ||
| 115 | } | ||
| 116 | videoData->vulkan_xlib_xcb_library = SDL_LoadObject(libX11XCBLibraryName); | ||
| 117 | if (!videoData->vulkan_xlib_xcb_library) { | ||
| 118 | goto fail; | ||
| 119 | } | ||
| 120 | videoData->vulkan_XGetXCBConnection = | ||
| 121 | (PFN_XGetXCBConnection)SDL_LoadFunction(videoData->vulkan_xlib_xcb_library, "XGetXCBConnection"); | ||
| 122 | if (!videoData->vulkan_XGetXCBConnection) { | ||
| 123 | SDL_UnloadObject(videoData->vulkan_xlib_xcb_library); | ||
| 124 | goto fail; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | return true; | ||
| 128 | |||
| 129 | fail: | ||
| 130 | SDL_UnloadObject(_this->vulkan_config.loader_handle); | ||
| 131 | _this->vulkan_config.loader_handle = NULL; | ||
| 132 | return false; | ||
| 133 | } | ||
| 134 | |||
| 135 | void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this) | ||
| 136 | { | ||
| 137 | SDL_VideoData *videoData = _this->internal; | ||
| 138 | if (_this->vulkan_config.loader_handle) { | ||
| 139 | if (videoData->vulkan_xlib_xcb_library) { | ||
| 140 | SDL_UnloadObject(videoData->vulkan_xlib_xcb_library); | ||
| 141 | } | ||
| 142 | SDL_UnloadObject(_this->vulkan_config.loader_handle); | ||
| 143 | _this->vulkan_config.loader_handle = NULL; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, | ||
| 148 | Uint32 *count) | ||
| 149 | { | ||
| 150 | SDL_VideoData *videoData = _this->internal; | ||
| 151 | if (videoData->vulkan_xlib_xcb_library) { | ||
| 152 | static const char *const extensionsForXCB[] = { | ||
| 153 | VK_KHR_SURFACE_EXTENSION_NAME, | ||
| 154 | VK_KHR_XCB_SURFACE_EXTENSION_NAME, | ||
| 155 | }; | ||
| 156 | if(count) { | ||
| 157 | *count = SDL_arraysize(extensionsForXCB); | ||
| 158 | } | ||
| 159 | return extensionsForXCB; | ||
| 160 | } else { | ||
| 161 | static const char *const extensionsForXlib[] = { | ||
| 162 | VK_KHR_SURFACE_EXTENSION_NAME, | ||
| 163 | VK_KHR_XLIB_SURFACE_EXTENSION_NAME, | ||
| 164 | }; | ||
| 165 | if(count) { | ||
| 166 | *count = SDL_arraysize(extensionsForXlib); | ||
| 167 | } | ||
| 168 | return extensionsForXlib; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this, | ||
| 173 | SDL_Window *window, | ||
| 174 | VkInstance instance, | ||
| 175 | const struct VkAllocationCallbacks *allocator, | ||
| 176 | VkSurfaceKHR *surface) | ||
| 177 | { | ||
| 178 | SDL_VideoData *videoData = _this->internal; | ||
| 179 | SDL_WindowData *windowData = window->internal; | ||
| 180 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | ||
| 181 | if (!_this->vulkan_config.loader_handle) { | ||
| 182 | return SDL_SetError("Vulkan is not loaded"); | ||
| 183 | } | ||
| 184 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; | ||
| 185 | if (videoData->vulkan_xlib_xcb_library) { | ||
| 186 | PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR = | ||
| 187 | (PFN_vkCreateXcbSurfaceKHR)vkGetInstanceProcAddr(instance, | ||
| 188 | "vkCreateXcbSurfaceKHR"); | ||
| 189 | VkXcbSurfaceCreateInfoKHR createInfo; | ||
| 190 | VkResult result; | ||
| 191 | if (!vkCreateXcbSurfaceKHR) { | ||
| 192 | return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); | ||
| 193 | } | ||
| 194 | SDL_zero(createInfo); | ||
| 195 | createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; | ||
| 196 | createInfo.connection = videoData->vulkan_XGetXCBConnection(videoData->display); | ||
| 197 | if (!createInfo.connection) { | ||
| 198 | return SDL_SetError("XGetXCBConnection failed"); | ||
| 199 | } | ||
| 200 | createInfo.window = (xcb_window_t)windowData->xwindow; | ||
| 201 | result = vkCreateXcbSurfaceKHR(instance, &createInfo, allocator, surface); | ||
| 202 | if (result != VK_SUCCESS) { | ||
| 203 | return SDL_SetError("vkCreateXcbSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result)); | ||
| 204 | } | ||
| 205 | } else { | ||
| 206 | PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = | ||
| 207 | (PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance, | ||
| 208 | "vkCreateXlibSurfaceKHR"); | ||
| 209 | VkXlibSurfaceCreateInfoKHR createInfo; | ||
| 210 | VkResult result; | ||
| 211 | if (!vkCreateXlibSurfaceKHR) { | ||
| 212 | return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); | ||
| 213 | } | ||
| 214 | SDL_zero(createInfo); | ||
| 215 | createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; | ||
| 216 | createInfo.dpy = videoData->display; | ||
| 217 | createInfo.window = (xcb_window_t)windowData->xwindow; | ||
| 218 | result = vkCreateXlibSurfaceKHR(instance, &createInfo, allocator, surface); | ||
| 219 | if (result != VK_SUCCESS) { | ||
| 220 | return SDL_SetError("vkCreateXlibSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result)); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | return true; // success! | ||
| 225 | } | ||
| 226 | |||
| 227 | void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this, | ||
| 228 | VkInstance instance, | ||
| 229 | VkSurfaceKHR surface, | ||
| 230 | const struct VkAllocationCallbacks *allocator) | ||
| 231 | { | ||
| 232 | if (_this->vulkan_config.loader_handle) { | ||
| 233 | SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator); | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, | ||
| 238 | VkInstance instance, | ||
| 239 | VkPhysicalDevice physicalDevice, | ||
| 240 | Uint32 queueFamilyIndex) | ||
| 241 | { | ||
| 242 | SDL_VideoData *videoData = _this->internal; | ||
| 243 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; | ||
| 244 | const char *forced_visual_id; | ||
| 245 | VisualID visualid; | ||
| 246 | |||
| 247 | if (!_this->vulkan_config.loader_handle) { | ||
| 248 | return SDL_SetError("Vulkan is not loaded"); | ||
| 249 | } | ||
| 250 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; | ||
| 251 | |||
| 252 | forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID); | ||
| 253 | if (forced_visual_id) { | ||
| 254 | visualid = SDL_strtol(forced_visual_id, NULL, 0); | ||
| 255 | } else { | ||
| 256 | visualid = X11_XVisualIDFromVisual(DefaultVisual(videoData->display, DefaultScreen(videoData->display))); | ||
| 257 | } | ||
| 258 | |||
| 259 | if (videoData->vulkan_xlib_xcb_library) { | ||
| 260 | PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = | ||
| 261 | (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)vkGetInstanceProcAddr( | ||
| 262 | instance, | ||
| 263 | "vkGetPhysicalDeviceXcbPresentationSupportKHR"); | ||
| 264 | |||
| 265 | if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) { | ||
| 266 | return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); | ||
| 267 | } | ||
| 268 | |||
| 269 | return vkGetPhysicalDeviceXcbPresentationSupportKHR(physicalDevice, | ||
| 270 | queueFamilyIndex, | ||
| 271 | videoData->vulkan_XGetXCBConnection(videoData->display), | ||
| 272 | visualid); | ||
| 273 | } else { | ||
| 274 | PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = | ||
| 275 | (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)vkGetInstanceProcAddr( | ||
| 276 | instance, | ||
| 277 | "vkGetPhysicalDeviceXlibPresentationSupportKHR"); | ||
| 278 | |||
| 279 | if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) { | ||
| 280 | return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance."); | ||
| 281 | } | ||
| 282 | |||
| 283 | return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice, | ||
| 284 | queueFamilyIndex, | ||
| 285 | videoData->display, | ||
| 286 | visualid); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | #endif | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h new file mode 100644 index 0000000..2a62596 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11vulkan.h | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11vulkan_h_ | ||
| 24 | #define SDL_x11vulkan_h_ | ||
| 25 | |||
| 26 | #include <SDL3/SDL_vulkan.h> | ||
| 27 | |||
| 28 | #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11) | ||
| 29 | |||
| 30 | typedef struct xcb_connection_t xcb_connection_t; | ||
| 31 | typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy); | ||
| 32 | |||
| 33 | extern bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path); | ||
| 34 | extern void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this); | ||
| 35 | extern char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count); | ||
| 36 | extern bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this, | ||
| 37 | SDL_Window *window, | ||
| 38 | VkInstance instance, | ||
| 39 | const struct VkAllocationCallbacks *allocator, | ||
| 40 | VkSurfaceKHR *surface); | ||
| 41 | extern void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this, | ||
| 42 | VkInstance instance, | ||
| 43 | VkSurfaceKHR surface, | ||
| 44 | const struct VkAllocationCallbacks *allocator); | ||
| 45 | extern bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this, | ||
| 46 | VkInstance instance, | ||
| 47 | VkPhysicalDevice physicalDevice, | ||
| 48 | Uint32 queueFamilyIndex); | ||
| 49 | |||
| 50 | #endif | ||
| 51 | |||
| 52 | #endif // SDL_x11vulkan_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c new file mode 100644 index 0000000..5562053 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.c | |||
| @@ -0,0 +1,2243 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "../SDL_sysvideo.h" | ||
| 26 | #include "../SDL_pixels_c.h" | ||
| 27 | #include "../../events/SDL_keyboard_c.h" | ||
| 28 | #include "../../events/SDL_mouse_c.h" | ||
| 29 | #include "../../events/SDL_events_c.h" | ||
| 30 | #include "../../core/unix/SDL_appid.h" | ||
| 31 | |||
| 32 | #include "SDL_x11video.h" | ||
| 33 | #include "SDL_x11mouse.h" | ||
| 34 | #include "SDL_x11xinput2.h" | ||
| 35 | #include "SDL_x11xfixes.h" | ||
| 36 | |||
| 37 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 38 | #include "SDL_x11opengles.h" | ||
| 39 | #endif | ||
| 40 | |||
| 41 | #include "SDL_x11xsync.h" | ||
| 42 | |||
| 43 | #define _NET_WM_STATE_REMOVE 0l | ||
| 44 | #define _NET_WM_STATE_ADD 1l | ||
| 45 | |||
| 46 | #define CHECK_WINDOW_DATA(window) \ | ||
| 47 | if (!window) { \ | ||
| 48 | return SDL_SetError("Invalid window"); \ | ||
| 49 | } \ | ||
| 50 | if (!window->internal) { \ | ||
| 51 | return SDL_SetError("Invalid window driver data"); \ | ||
| 52 | } | ||
| 53 | |||
| 54 | #define CHECK_DISPLAY_DATA(display) \ | ||
| 55 | if (!_display) { \ | ||
| 56 | return SDL_SetError("Invalid display"); \ | ||
| 57 | } \ | ||
| 58 | if (!_display->internal) { \ | ||
| 59 | return SDL_SetError("Invalid display driver data"); \ | ||
| 60 | } | ||
| 61 | |||
| 62 | static Bool isMapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef | ||
| 63 | { | ||
| 64 | return ev->type == MapNotify && ev->xmap.window == *((Window *)win); | ||
| 65 | } | ||
| 66 | static Bool isUnmapNotify(Display *dpy, XEvent *ev, XPointer win) // NOLINT(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef | ||
| 67 | { | ||
| 68 | return ev->type == UnmapNotify && ev->xunmap.window == *((Window *)win); | ||
| 69 | } | ||
| 70 | |||
| 71 | /* | ||
| 72 | static Bool isConfigureNotify(Display *dpy, XEvent *ev, XPointer win) | ||
| 73 | { | ||
| 74 | return ev->type == ConfigureNotify && ev->xconfigure.window == *((Window*)win); | ||
| 75 | } | ||
| 76 | static Bool X11_XIfEventTimeout(Display *display, XEvent *event_return, Bool (*predicate)(), XPointer arg, int timeoutMS) | ||
| 77 | { | ||
| 78 | Uint64 start = SDL_GetTicks(); | ||
| 79 | |||
| 80 | while (!X11_XCheckIfEvent(display, event_return, predicate, arg)) { | ||
| 81 | if (SDL_GetTicks() >= (start + timeoutMS)) { | ||
| 82 | return False; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | return True; | ||
| 86 | } | ||
| 87 | */ | ||
| 88 | |||
| 89 | static bool X11_IsWindowMapped(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 90 | { | ||
| 91 | SDL_WindowData *data = window->internal; | ||
| 92 | SDL_VideoData *videodata = _this->internal; | ||
| 93 | XWindowAttributes attr; | ||
| 94 | |||
| 95 | X11_XGetWindowAttributes(videodata->display, data->xwindow, &attr); | ||
| 96 | if (attr.map_state != IsUnmapped) { | ||
| 97 | return true; | ||
| 98 | } else { | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | static bool X11_IsDisplayOk(Display *display) | ||
| 104 | { | ||
| 105 | if (display->flags & XlibDisplayIOError) { | ||
| 106 | return false; | ||
| 107 | } | ||
| 108 | return true; | ||
| 109 | } | ||
| 110 | |||
| 111 | #if 0 | ||
| 112 | static bool X11_IsActionAllowed(SDL_Window *window, Atom action) | ||
| 113 | { | ||
| 114 | SDL_WindowData *data = window->internal; | ||
| 115 | Atom _NET_WM_ALLOWED_ACTIONS = data->videodata->_NET_WM_ALLOWED_ACTIONS; | ||
| 116 | Atom type; | ||
| 117 | Display *display = data->videodata->display; | ||
| 118 | int form; | ||
| 119 | unsigned long remain; | ||
| 120 | unsigned long len, i; | ||
| 121 | Atom *list; | ||
| 122 | bool ret = false; | ||
| 123 | |||
| 124 | if (X11_XGetWindowProperty(display, data->xwindow, _NET_WM_ALLOWED_ACTIONS, 0, 1024, False, XA_ATOM, &type, &form, &len, &remain, (unsigned char **)&list) == Success) { | ||
| 125 | for (i=0; i<len; ++i) { | ||
| 126 | if (list[i] == action) { | ||
| 127 | ret = true; | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | X11_XFree(list); | ||
| 132 | } | ||
| 133 | return ret; | ||
| 134 | } | ||
| 135 | #endif // 0 | ||
| 136 | |||
| 137 | static void X11_FlushPendingEvents(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 138 | { | ||
| 139 | // Serialize and restore the pending flags, as they may be overwritten while flushing. | ||
| 140 | const bool last_position_pending = window->last_position_pending; | ||
| 141 | const bool last_size_pending = window->last_size_pending; | ||
| 142 | |||
| 143 | X11_SyncWindow(_this, window); | ||
| 144 | |||
| 145 | window->last_position_pending = last_position_pending; | ||
| 146 | window->last_size_pending = last_size_pending; | ||
| 147 | } | ||
| 148 | |||
| 149 | void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags) | ||
| 150 | { | ||
| 151 | SDL_VideoData *videodata = _this->internal; | ||
| 152 | Display *display = videodata->display; | ||
| 153 | // !!! FIXME: just dereference videodata below instead of copying to locals. | ||
| 154 | Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE; | ||
| 155 | // Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN; | ||
| 156 | Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED; | ||
| 157 | Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; | ||
| 158 | Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 159 | Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN; | ||
| 160 | Atom _NET_WM_STATE_ABOVE = videodata->atoms._NET_WM_STATE_ABOVE; | ||
| 161 | Atom _NET_WM_STATE_SKIP_TASKBAR = videodata->atoms._NET_WM_STATE_SKIP_TASKBAR; | ||
| 162 | Atom _NET_WM_STATE_SKIP_PAGER = videodata->atoms._NET_WM_STATE_SKIP_PAGER; | ||
| 163 | Atom _NET_WM_STATE_MODAL = videodata->atoms._NET_WM_STATE_MODAL; | ||
| 164 | Atom atoms[16]; | ||
| 165 | int count = 0; | ||
| 166 | |||
| 167 | /* The window manager sets this property, we shouldn't set it. | ||
| 168 | If we did, this would indicate to the window manager that we don't | ||
| 169 | actually want to be mapped during X11_XMapRaised(), which would be bad. | ||
| 170 | * | ||
| 171 | if ((flags & SDL_WINDOW_HIDDEN) != 0) { | ||
| 172 | atoms[count++] = _NET_WM_STATE_HIDDEN; | ||
| 173 | } | ||
| 174 | */ | ||
| 175 | |||
| 176 | if (flags & SDL_WINDOW_ALWAYS_ON_TOP) { | ||
| 177 | atoms[count++] = _NET_WM_STATE_ABOVE; | ||
| 178 | } | ||
| 179 | if (flags & SDL_WINDOW_UTILITY) { | ||
| 180 | atoms[count++] = _NET_WM_STATE_SKIP_TASKBAR; | ||
| 181 | atoms[count++] = _NET_WM_STATE_SKIP_PAGER; | ||
| 182 | } | ||
| 183 | if (flags & SDL_WINDOW_INPUT_FOCUS) { | ||
| 184 | atoms[count++] = _NET_WM_STATE_FOCUSED; | ||
| 185 | } | ||
| 186 | if (flags & SDL_WINDOW_MAXIMIZED) { | ||
| 187 | atoms[count++] = _NET_WM_STATE_MAXIMIZED_VERT; | ||
| 188 | atoms[count++] = _NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 189 | } | ||
| 190 | if (flags & SDL_WINDOW_FULLSCREEN) { | ||
| 191 | atoms[count++] = _NET_WM_STATE_FULLSCREEN; | ||
| 192 | } | ||
| 193 | if (flags & SDL_WINDOW_MODAL) { | ||
| 194 | atoms[count++] = _NET_WM_STATE_MODAL; | ||
| 195 | } | ||
| 196 | |||
| 197 | SDL_assert(count <= SDL_arraysize(atoms)); | ||
| 198 | |||
| 199 | if (count > 0) { | ||
| 200 | X11_XChangeProperty(display, xwindow, _NET_WM_STATE, XA_ATOM, 32, | ||
| 201 | PropModeReplace, (unsigned char *)atoms, count); | ||
| 202 | } else { | ||
| 203 | X11_XDeleteProperty(display, xwindow, _NET_WM_STATE); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | static void X11_ConstrainPopup(SDL_Window *window, bool output_to_pending) | ||
| 208 | { | ||
| 209 | // Clamp popup windows to the output borders | ||
| 210 | if (SDL_WINDOW_IS_POPUP(window)) { | ||
| 211 | SDL_Window *w; | ||
| 212 | SDL_DisplayID displayID; | ||
| 213 | SDL_Rect rect; | ||
| 214 | int abs_x = window->last_position_pending ? window->pending.x : window->floating.x; | ||
| 215 | int abs_y = window->last_position_pending ? window->pending.y : window->floating.y; | ||
| 216 | int offset_x = 0, offset_y = 0; | ||
| 217 | |||
| 218 | // Calculate the total offset from the parents | ||
| 219 | for (w = window->parent; SDL_WINDOW_IS_POPUP(w); w = w->parent) { | ||
| 220 | offset_x += w->x; | ||
| 221 | offset_y += w->y; | ||
| 222 | } | ||
| 223 | |||
| 224 | offset_x += w->x; | ||
| 225 | offset_y += w->y; | ||
| 226 | abs_x += offset_x; | ||
| 227 | abs_y += offset_y; | ||
| 228 | |||
| 229 | displayID = SDL_GetDisplayForWindow(w); | ||
| 230 | |||
| 231 | SDL_GetDisplayBounds(displayID, &rect); | ||
| 232 | if (abs_x + window->w > rect.x + rect.w) { | ||
| 233 | abs_x -= (abs_x + window->w) - (rect.x + rect.w); | ||
| 234 | } | ||
| 235 | if (abs_y + window->h > rect.y + rect.h) { | ||
| 236 | abs_y -= (abs_y + window->h) - (rect.y + rect.h); | ||
| 237 | } | ||
| 238 | abs_x = SDL_max(abs_x, rect.x); | ||
| 239 | abs_y = SDL_max(abs_y, rect.y); | ||
| 240 | |||
| 241 | if (output_to_pending) { | ||
| 242 | window->pending.x = abs_x - offset_x; | ||
| 243 | window->pending.y = abs_y - offset_y; | ||
| 244 | } else { | ||
| 245 | window->floating.x = window->windowed.x = abs_x - offset_x; | ||
| 246 | window->floating.y = window->windowed.y = abs_y - offset_y; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | static void X11_SetKeyboardFocus(SDL_Window *window, bool set_active_focus) | ||
| 252 | { | ||
| 253 | SDL_Window *toplevel = window; | ||
| 254 | |||
| 255 | // Find the toplevel parent | ||
| 256 | while (SDL_WINDOW_IS_POPUP(toplevel)) { | ||
| 257 | toplevel = toplevel->parent; | ||
| 258 | } | ||
| 259 | |||
| 260 | toplevel->internal->keyboard_focus = window; | ||
| 261 | |||
| 262 | if (set_active_focus && !window->is_hiding && !window->is_destroying) { | ||
| 263 | SDL_SetKeyboardFocus(window); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow) | ||
| 268 | { | ||
| 269 | SDL_VideoData *videodata = _this->internal; | ||
| 270 | Display *display = videodata->display; | ||
| 271 | Atom _NET_WM_STATE = videodata->atoms._NET_WM_STATE; | ||
| 272 | Atom _NET_WM_STATE_HIDDEN = videodata->atoms._NET_WM_STATE_HIDDEN; | ||
| 273 | Atom _NET_WM_STATE_FOCUSED = videodata->atoms._NET_WM_STATE_FOCUSED; | ||
| 274 | Atom _NET_WM_STATE_MAXIMIZED_VERT = videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; | ||
| 275 | Atom _NET_WM_STATE_MAXIMIZED_HORZ = videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 276 | Atom _NET_WM_STATE_FULLSCREEN = videodata->atoms._NET_WM_STATE_FULLSCREEN; | ||
| 277 | Atom actualType; | ||
| 278 | int actualFormat; | ||
| 279 | unsigned long i, numItems, bytesAfter; | ||
| 280 | unsigned char *propertyValue = NULL; | ||
| 281 | long maxLength = 1024; | ||
| 282 | SDL_WindowFlags flags = 0; | ||
| 283 | |||
| 284 | if (X11_XGetWindowProperty(display, xwindow, _NET_WM_STATE, | ||
| 285 | 0l, maxLength, False, XA_ATOM, &actualType, | ||
| 286 | &actualFormat, &numItems, &bytesAfter, | ||
| 287 | &propertyValue) == Success) { | ||
| 288 | Atom *atoms = (Atom *)propertyValue; | ||
| 289 | int maximized = 0; | ||
| 290 | int fullscreen = 0; | ||
| 291 | |||
| 292 | for (i = 0; i < numItems; ++i) { | ||
| 293 | if (atoms[i] == _NET_WM_STATE_HIDDEN) { | ||
| 294 | flags |= SDL_WINDOW_MINIMIZED | SDL_WINDOW_OCCLUDED; | ||
| 295 | } else if (atoms[i] == _NET_WM_STATE_FOCUSED) { | ||
| 296 | flags |= SDL_WINDOW_INPUT_FOCUS; | ||
| 297 | } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_VERT) { | ||
| 298 | maximized |= 1; | ||
| 299 | } else if (atoms[i] == _NET_WM_STATE_MAXIMIZED_HORZ) { | ||
| 300 | maximized |= 2; | ||
| 301 | } else if (atoms[i] == _NET_WM_STATE_FULLSCREEN) { | ||
| 302 | fullscreen = 1; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | if (fullscreen == 1) { | ||
| 307 | if (window->flags & SDL_WINDOW_FULLSCREEN) { | ||
| 308 | // Pick whatever state the window expects | ||
| 309 | flags |= (window->flags & SDL_WINDOW_FULLSCREEN); | ||
| 310 | } else { | ||
| 311 | // Assume we're fullscreen desktop | ||
| 312 | flags |= SDL_WINDOW_FULLSCREEN; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | if (maximized == 3) { | ||
| 317 | /* Fullscreen windows are maximized on some window managers, | ||
| 318 | and this is functional behavior - if maximized is removed, | ||
| 319 | the windows remain floating centered and not covering the | ||
| 320 | rest of the desktop. So we just won't change the maximize | ||
| 321 | state for fullscreen windows here, otherwise SDL would think | ||
| 322 | we're always maximized when fullscreen and not restore the | ||
| 323 | correct state when leaving fullscreen. | ||
| 324 | */ | ||
| 325 | if (fullscreen) { | ||
| 326 | flags |= (window->flags & SDL_WINDOW_MAXIMIZED); | ||
| 327 | } else { | ||
| 328 | flags |= SDL_WINDOW_MAXIMIZED; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | /* If the window is unmapped, numItems will be zero and _NET_WM_STATE_HIDDEN | ||
| 333 | * will not be set. Do an additional check to see if the window is unmapped | ||
| 334 | * and mark it as SDL_WINDOW_HIDDEN if it is. | ||
| 335 | */ | ||
| 336 | { | ||
| 337 | XWindowAttributes attr; | ||
| 338 | SDL_memset(&attr, 0, sizeof(attr)); | ||
| 339 | X11_XGetWindowAttributes(videodata->display, xwindow, &attr); | ||
| 340 | if (attr.map_state == IsUnmapped) { | ||
| 341 | flags |= SDL_WINDOW_HIDDEN; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | X11_XFree(propertyValue); | ||
| 345 | } | ||
| 346 | |||
| 347 | // FIXME, check the size hints for resizable | ||
| 348 | // flags |= SDL_WINDOW_RESIZABLE; | ||
| 349 | |||
| 350 | return flags; | ||
| 351 | } | ||
| 352 | |||
| 353 | static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, Window w) | ||
| 354 | { | ||
| 355 | SDL_VideoData *videodata = _this->internal; | ||
| 356 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 357 | SDL_WindowData *data; | ||
| 358 | int numwindows = videodata->numwindows; | ||
| 359 | int windowlistlength = videodata->windowlistlength; | ||
| 360 | SDL_WindowData **windowlist = videodata->windowlist; | ||
| 361 | |||
| 362 | // Allocate the window data | ||
| 363 | data = (SDL_WindowData *)SDL_calloc(1, sizeof(*data)); | ||
| 364 | if (!data) { | ||
| 365 | return false; | ||
| 366 | } | ||
| 367 | data->videodata = videodata; | ||
| 368 | data->window = window; | ||
| 369 | data->xwindow = w; | ||
| 370 | data->hit_test_result = SDL_HITTEST_NORMAL; | ||
| 371 | |||
| 372 | X11_CreateInputContext(data); | ||
| 373 | |||
| 374 | // Associate the data with the window | ||
| 375 | |||
| 376 | if (numwindows < windowlistlength) { | ||
| 377 | windowlist[numwindows] = data; | ||
| 378 | videodata->numwindows++; | ||
| 379 | } else { | ||
| 380 | SDL_WindowData ** new_windowlist = (SDL_WindowData **)SDL_realloc(windowlist, (numwindows + 1) * sizeof(*windowlist)); | ||
| 381 | if (!new_windowlist) { | ||
| 382 | SDL_free(data); | ||
| 383 | return false; | ||
| 384 | } | ||
| 385 | windowlist = new_windowlist; | ||
| 386 | windowlist[numwindows] = data; | ||
| 387 | videodata->numwindows++; | ||
| 388 | videodata->windowlistlength++; | ||
| 389 | videodata->windowlist = windowlist; | ||
| 390 | } | ||
| 391 | |||
| 392 | // Fill in the SDL window with the window data | ||
| 393 | { | ||
| 394 | XWindowAttributes attrib; | ||
| 395 | |||
| 396 | X11_XGetWindowAttributes(data->videodata->display, w, &attrib); | ||
| 397 | if (!SDL_WINDOW_IS_POPUP(window)) { | ||
| 398 | window->x = window->windowed.x = window->floating.x = attrib.x; | ||
| 399 | window->y = window->windowed.y = window->floating.y = attrib.y - data->border_top; | ||
| 400 | } | ||
| 401 | window->w = window->windowed.w = window->floating.w = attrib.width; | ||
| 402 | window->h = window->windowed.h = window->floating.h = attrib.height; | ||
| 403 | if (attrib.map_state != IsUnmapped) { | ||
| 404 | window->flags &= ~SDL_WINDOW_HIDDEN; | ||
| 405 | } else { | ||
| 406 | window->flags |= SDL_WINDOW_HIDDEN; | ||
| 407 | } | ||
| 408 | data->visual = attrib.visual; | ||
| 409 | data->colormap = attrib.colormap; | ||
| 410 | } | ||
| 411 | |||
| 412 | window->flags |= X11_GetNetWMState(_this, window, w); | ||
| 413 | |||
| 414 | { | ||
| 415 | Window FocalWindow; | ||
| 416 | int RevertTo = 0; | ||
| 417 | X11_XGetInputFocus(data->videodata->display, &FocalWindow, &RevertTo); | ||
| 418 | if (FocalWindow == w) { | ||
| 419 | window->flags |= SDL_WINDOW_INPUT_FOCUS; | ||
| 420 | } | ||
| 421 | |||
| 422 | if (window->flags & SDL_WINDOW_INPUT_FOCUS) { | ||
| 423 | SDL_SetKeyboardFocus(data->window); | ||
| 424 | } | ||
| 425 | |||
| 426 | if (window->flags & SDL_WINDOW_MOUSE_GRABBED) { | ||
| 427 | // Tell x11 to clip mouse | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | if (window->flags & SDL_WINDOW_EXTERNAL) { | ||
| 432 | // Query the title from the existing window | ||
| 433 | window->title = X11_GetWindowTitle(_this, w); | ||
| 434 | } | ||
| 435 | |||
| 436 | SDL_PropertiesID props = SDL_GetWindowProperties(window); | ||
| 437 | int screen = (displaydata ? displaydata->screen : 0); | ||
| 438 | SDL_SetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, data->videodata->display); | ||
| 439 | SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, screen); | ||
| 440 | SDL_SetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, data->xwindow); | ||
| 441 | |||
| 442 | // All done! | ||
| 443 | window->internal = data; | ||
| 444 | return true; | ||
| 445 | } | ||
| 446 | |||
| 447 | static void SetupWindowInput(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 448 | { | ||
| 449 | long fevent = 0; | ||
| 450 | SDL_WindowData *data = window->internal; | ||
| 451 | Window xwindow = data->xwindow; | ||
| 452 | |||
| 453 | #ifdef X_HAVE_UTF8_STRING | ||
| 454 | if (SDL_X11_HAVE_UTF8 && data->ic) { | ||
| 455 | X11_XGetICValues(data->ic, XNFilterEvents, &fevent, NULL); | ||
| 456 | } | ||
| 457 | #endif | ||
| 458 | |||
| 459 | X11_Xinput2SelectTouch(_this, window); | ||
| 460 | |||
| 461 | { | ||
| 462 | unsigned int x11_keyboard_events = KeyPressMask | KeyReleaseMask; | ||
| 463 | unsigned int x11_pointer_events = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; | ||
| 464 | |||
| 465 | X11_Xinput2SelectMouseAndKeyboard(_this, window); | ||
| 466 | |||
| 467 | // If XInput2 can handle pointer and keyboard events, we don't track them here | ||
| 468 | if (data->xinput2_keyboard_enabled) { | ||
| 469 | x11_keyboard_events = 0; | ||
| 470 | } | ||
| 471 | if (data->xinput2_mouse_enabled) { | ||
| 472 | x11_pointer_events = 0; | ||
| 473 | } | ||
| 474 | |||
| 475 | X11_XSelectInput(data->videodata->display, xwindow, | ||
| 476 | (FocusChangeMask | EnterWindowMask | LeaveWindowMask | ExposureMask | | ||
| 477 | x11_keyboard_events | x11_pointer_events | | ||
| 478 | PropertyChangeMask | StructureNotifyMask | | ||
| 479 | KeymapStateMask | fevent)); | ||
| 480 | } | ||
| 481 | } | ||
| 482 | |||
| 483 | static void SetWindowBordered(Display *display, int screen, Window window, bool border) | ||
| 484 | { | ||
| 485 | /* | ||
| 486 | * this code used to check for KWM_WIN_DECORATION, but KDE hasn't | ||
| 487 | * supported it for years and years. It now respects _MOTIF_WM_HINTS. | ||
| 488 | * Gnome is similar: just use the Motif atom. | ||
| 489 | */ | ||
| 490 | |||
| 491 | Atom WM_HINTS = X11_XInternAtom(display, "_MOTIF_WM_HINTS", True); | ||
| 492 | if (WM_HINTS != None) { | ||
| 493 | // Hints used by Motif compliant window managers | ||
| 494 | struct | ||
| 495 | { | ||
| 496 | unsigned long flags; | ||
| 497 | unsigned long functions; | ||
| 498 | unsigned long decorations; | ||
| 499 | long input_mode; | ||
| 500 | unsigned long status; | ||
| 501 | } MWMHints = { | ||
| 502 | (1L << 1), 0, border ? 1 : 0, 0, 0 | ||
| 503 | }; | ||
| 504 | |||
| 505 | X11_XChangeProperty(display, window, WM_HINTS, WM_HINTS, 32, | ||
| 506 | PropModeReplace, (unsigned char *)&MWMHints, | ||
| 507 | sizeof(MWMHints) / sizeof(long)); | ||
| 508 | } else { // set the transient hints instead, if necessary | ||
| 509 | X11_XSetTransientForHint(display, window, RootWindow(display, screen)); | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 513 | bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props) | ||
| 514 | { | ||
| 515 | Window w = (Window)SDL_GetNumberProperty(create_props, SDL_PROP_WINDOW_CREATE_X11_WINDOW_NUMBER, | ||
| 516 | (Window)SDL_GetPointerProperty(create_props, "sdl2-compat.external_window", NULL)); | ||
| 517 | if (w) { | ||
| 518 | window->flags |= SDL_WINDOW_EXTERNAL; | ||
| 519 | |||
| 520 | if (!SetupWindowData(_this, window, w)) { | ||
| 521 | return false; | ||
| 522 | } | ||
| 523 | |||
| 524 | SetupWindowInput(_this, window); | ||
| 525 | return true; | ||
| 526 | } | ||
| 527 | |||
| 528 | SDL_VideoData *data = _this->internal; | ||
| 529 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 530 | if (!displaydata) { | ||
| 531 | return SDL_SetError("Could not find display info"); | ||
| 532 | } | ||
| 533 | |||
| 534 | const bool force_override_redirect = SDL_GetHintBoolean(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, false); | ||
| 535 | const bool use_resize_sync = (window->flags & SDL_WINDOW_VULKAN); /* doesn't work well with Vulkan */ | ||
| 536 | SDL_WindowData *windowdata; | ||
| 537 | Display *display = data->display; | ||
| 538 | int screen = displaydata->screen; | ||
| 539 | Visual *visual; | ||
| 540 | int depth; | ||
| 541 | XSetWindowAttributes xattr; | ||
| 542 | XSizeHints *sizehints; | ||
| 543 | XWMHints *wmhints; | ||
| 544 | XClassHint *classhints; | ||
| 545 | Atom _NET_WM_BYPASS_COMPOSITOR; | ||
| 546 | Atom _NET_WM_WINDOW_TYPE; | ||
| 547 | Atom wintype; | ||
| 548 | const char *wintype_name = NULL; | ||
| 549 | long compositor = 1; | ||
| 550 | Atom _NET_WM_PID; | ||
| 551 | const char *hint = NULL; | ||
| 552 | int win_x, win_y; | ||
| 553 | bool undefined_position = false; | ||
| 554 | |||
| 555 | #if defined(SDL_VIDEO_OPENGL_GLX) || defined(SDL_VIDEO_OPENGL_EGL) | ||
| 556 | const int transparent = (window->flags & SDL_WINDOW_TRANSPARENT) ? true : false; | ||
| 557 | const char *forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID); | ||
| 558 | const char *display_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_VISUALID); | ||
| 559 | |||
| 560 | if (forced_visual_id && *forced_visual_id) { | ||
| 561 | XVisualInfo *vi, template; | ||
| 562 | int nvis; | ||
| 563 | |||
| 564 | SDL_zero(template); | ||
| 565 | template.visualid = SDL_strtol(forced_visual_id, NULL, 0); | ||
| 566 | vi = X11_XGetVisualInfo(display, VisualIDMask, &template, &nvis); | ||
| 567 | if (vi) { | ||
| 568 | visual = vi->visual; | ||
| 569 | depth = vi->depth; | ||
| 570 | X11_XFree(vi); | ||
| 571 | } else { | ||
| 572 | return false; | ||
| 573 | } | ||
| 574 | } else if ((window->flags & SDL_WINDOW_OPENGL) && | ||
| 575 | (!display_visual_id || !*display_visual_id)) { | ||
| 576 | XVisualInfo *vinfo = NULL; | ||
| 577 | |||
| 578 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 579 | if (((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || | ||
| 580 | SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) | ||
| 581 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 582 | && (!_this->gl_data || X11_GL_UseEGL(_this)) | ||
| 583 | #endif | ||
| 584 | ) { | ||
| 585 | vinfo = X11_GLES_GetVisual(_this, display, screen, transparent); | ||
| 586 | } else | ||
| 587 | #endif | ||
| 588 | { | ||
| 589 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 590 | vinfo = X11_GL_GetVisual(_this, display, screen, transparent); | ||
| 591 | #endif | ||
| 592 | } | ||
| 593 | |||
| 594 | if (!vinfo) { | ||
| 595 | return false; | ||
| 596 | } | ||
| 597 | visual = vinfo->visual; | ||
| 598 | depth = vinfo->depth; | ||
| 599 | X11_XFree(vinfo); | ||
| 600 | } else | ||
| 601 | #endif | ||
| 602 | { | ||
| 603 | visual = displaydata->visual; | ||
| 604 | depth = displaydata->depth; | ||
| 605 | } | ||
| 606 | |||
| 607 | xattr.override_redirect = ((window->flags & SDL_WINDOW_TOOLTIP) || (window->flags & SDL_WINDOW_POPUP_MENU) || force_override_redirect) ? True : False; | ||
| 608 | xattr.backing_store = NotUseful; | ||
| 609 | xattr.background_pixmap = None; | ||
| 610 | xattr.border_pixel = 0; | ||
| 611 | |||
| 612 | if (visual->class == DirectColor) { | ||
| 613 | XColor *colorcells; | ||
| 614 | int i; | ||
| 615 | int ncolors; | ||
| 616 | int rmax, gmax, bmax; | ||
| 617 | int rmask, gmask, bmask; | ||
| 618 | int rshift, gshift, bshift; | ||
| 619 | |||
| 620 | xattr.colormap = | ||
| 621 | X11_XCreateColormap(display, RootWindow(display, screen), | ||
| 622 | visual, AllocAll); | ||
| 623 | |||
| 624 | // If we can't create a colormap, then we must die | ||
| 625 | if (!xattr.colormap) { | ||
| 626 | return SDL_SetError("Could not create writable colormap"); | ||
| 627 | } | ||
| 628 | |||
| 629 | // OK, we got a colormap, now fill it in as best as we can | ||
| 630 | colorcells = SDL_malloc(visual->map_entries * sizeof(XColor)); | ||
| 631 | if (!colorcells) { | ||
| 632 | return false; | ||
| 633 | } | ||
| 634 | ncolors = visual->map_entries; | ||
| 635 | rmax = 0xffff; | ||
| 636 | gmax = 0xffff; | ||
| 637 | bmax = 0xffff; | ||
| 638 | |||
| 639 | rshift = 0; | ||
| 640 | rmask = visual->red_mask; | ||
| 641 | while (0 == (rmask & 1)) { | ||
| 642 | rshift++; | ||
| 643 | rmask >>= 1; | ||
| 644 | } | ||
| 645 | |||
| 646 | gshift = 0; | ||
| 647 | gmask = visual->green_mask; | ||
| 648 | while (0 == (gmask & 1)) { | ||
| 649 | gshift++; | ||
| 650 | gmask >>= 1; | ||
| 651 | } | ||
| 652 | |||
| 653 | bshift = 0; | ||
| 654 | bmask = visual->blue_mask; | ||
| 655 | while (0 == (bmask & 1)) { | ||
| 656 | bshift++; | ||
| 657 | bmask >>= 1; | ||
| 658 | } | ||
| 659 | |||
| 660 | // build the color table pixel values | ||
| 661 | for (i = 0; i < ncolors; i++) { | ||
| 662 | Uint32 red = (rmax * i) / (ncolors - 1); | ||
| 663 | Uint32 green = (gmax * i) / (ncolors - 1); | ||
| 664 | Uint32 blue = (bmax * i) / (ncolors - 1); | ||
| 665 | |||
| 666 | Uint32 rbits = (rmask * i) / (ncolors - 1); | ||
| 667 | Uint32 gbits = (gmask * i) / (ncolors - 1); | ||
| 668 | Uint32 bbits = (bmask * i) / (ncolors - 1); | ||
| 669 | |||
| 670 | Uint32 pix = | ||
| 671 | (rbits << rshift) | (gbits << gshift) | (bbits << bshift); | ||
| 672 | |||
| 673 | colorcells[i].pixel = pix; | ||
| 674 | |||
| 675 | colorcells[i].red = red; | ||
| 676 | colorcells[i].green = green; | ||
| 677 | colorcells[i].blue = blue; | ||
| 678 | |||
| 679 | colorcells[i].flags = DoRed | DoGreen | DoBlue; | ||
| 680 | } | ||
| 681 | |||
| 682 | X11_XStoreColors(display, xattr.colormap, colorcells, ncolors); | ||
| 683 | |||
| 684 | SDL_free(colorcells); | ||
| 685 | } else { | ||
| 686 | xattr.colormap = | ||
| 687 | X11_XCreateColormap(display, RootWindow(display, screen), | ||
| 688 | visual, AllocNone); | ||
| 689 | } | ||
| 690 | |||
| 691 | if (window->undefined_x && window->undefined_y && | ||
| 692 | window->last_displayID == SDL_GetPrimaryDisplay()) { | ||
| 693 | undefined_position = true; | ||
| 694 | } | ||
| 695 | |||
| 696 | if (SDL_WINDOW_IS_POPUP(window)) { | ||
| 697 | X11_ConstrainPopup(window, false); | ||
| 698 | } | ||
| 699 | SDL_RelativeToGlobalForWindow(window, | ||
| 700 | window->floating.x, window->floating.y, | ||
| 701 | &win_x, &win_y); | ||
| 702 | |||
| 703 | /* Always create this with the window->floating.* fields; if we're creating a windowed mode window, | ||
| 704 | * that's fine. If we're creating a maximized or fullscreen window, the window manager will want to | ||
| 705 | * know these values so it can use them if we go _back_ to the base floating windowed mode. SDL manages | ||
| 706 | * migration to fullscreen after CreateSDLWindow returns, which will put all the SDL_Window fields and | ||
| 707 | * system state as expected. | ||
| 708 | */ | ||
| 709 | w = X11_XCreateWindow(display, RootWindow(display, screen), | ||
| 710 | win_x, win_y, window->floating.w, window->floating.h, | ||
| 711 | 0, depth, InputOutput, visual, | ||
| 712 | (CWOverrideRedirect | CWBackPixmap | CWBorderPixel | | ||
| 713 | CWBackingStore | CWColormap), | ||
| 714 | &xattr); | ||
| 715 | if (!w) { | ||
| 716 | return SDL_SetError("Couldn't create window"); | ||
| 717 | } | ||
| 718 | |||
| 719 | /* Don't set the borderless flag if we're about to go fullscreen. | ||
| 720 | * This prevents the window manager from moving a full-screen borderless | ||
| 721 | * window to a different display before we actually go fullscreen. | ||
| 722 | */ | ||
| 723 | if (!(window->pending_flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 724 | SetWindowBordered(display, screen, w, !(window->flags & SDL_WINDOW_BORDERLESS)); | ||
| 725 | } | ||
| 726 | |||
| 727 | sizehints = X11_XAllocSizeHints(); | ||
| 728 | // Setup the normal size hints | ||
| 729 | sizehints->flags = 0; | ||
| 730 | if (!(window->flags & SDL_WINDOW_RESIZABLE)) { | ||
| 731 | sizehints->min_width = sizehints->max_width = window->floating.w; | ||
| 732 | sizehints->min_height = sizehints->max_height = window->floating.h; | ||
| 733 | sizehints->flags |= (PMaxSize | PMinSize); | ||
| 734 | } | ||
| 735 | if (!undefined_position) { | ||
| 736 | sizehints->x = win_x; | ||
| 737 | sizehints->y = win_y; | ||
| 738 | sizehints->flags |= USPosition; | ||
| 739 | } | ||
| 740 | |||
| 741 | // Setup the input hints so we get keyboard input | ||
| 742 | wmhints = X11_XAllocWMHints(); | ||
| 743 | wmhints->input = !(window->flags & SDL_WINDOW_NOT_FOCUSABLE) ? True : False; | ||
| 744 | wmhints->window_group = data->window_group; | ||
| 745 | wmhints->flags = InputHint | WindowGroupHint; | ||
| 746 | |||
| 747 | // Setup the class hints so we can get an icon (AfterStep) | ||
| 748 | classhints = X11_XAllocClassHint(); | ||
| 749 | classhints->res_name = (char *)SDL_GetExeName(); | ||
| 750 | classhints->res_class = (char *)SDL_GetAppID(); | ||
| 751 | |||
| 752 | // Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME | ||
| 753 | X11_XSetWMProperties(display, w, NULL, NULL, NULL, 0, sizehints, wmhints, classhints); | ||
| 754 | |||
| 755 | X11_XFree(sizehints); | ||
| 756 | X11_XFree(wmhints); | ||
| 757 | X11_XFree(classhints); | ||
| 758 | // Set the PID related to the window for the given hostname, if possible | ||
| 759 | if (data->pid > 0) { | ||
| 760 | long pid = (long)data->pid; | ||
| 761 | _NET_WM_PID = X11_XInternAtom(display, "_NET_WM_PID", False); | ||
| 762 | X11_XChangeProperty(display, w, _NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, | ||
| 763 | (unsigned char *)&pid, 1); | ||
| 764 | } | ||
| 765 | |||
| 766 | // Set the window manager state | ||
| 767 | X11_SetNetWMState(_this, w, window->flags); | ||
| 768 | |||
| 769 | compositor = 2; // don't disable compositing except for "normal" windows | ||
| 770 | hint = SDL_GetHint(SDL_HINT_X11_WINDOW_TYPE); | ||
| 771 | if (window->flags & SDL_WINDOW_UTILITY) { | ||
| 772 | wintype_name = "_NET_WM_WINDOW_TYPE_UTILITY"; | ||
| 773 | } else if (window->flags & SDL_WINDOW_TOOLTIP) { | ||
| 774 | wintype_name = "_NET_WM_WINDOW_TYPE_TOOLTIP"; | ||
| 775 | } else if (window->flags & SDL_WINDOW_POPUP_MENU) { | ||
| 776 | wintype_name = "_NET_WM_WINDOW_TYPE_POPUP_MENU"; | ||
| 777 | } else if (hint && *hint) { | ||
| 778 | wintype_name = hint; | ||
| 779 | } else { | ||
| 780 | wintype_name = "_NET_WM_WINDOW_TYPE_NORMAL"; | ||
| 781 | compositor = 1; // disable compositing for "normal" windows | ||
| 782 | } | ||
| 783 | |||
| 784 | // Let the window manager know what type of window we are. | ||
| 785 | _NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False); | ||
| 786 | wintype = X11_XInternAtom(display, wintype_name, False); | ||
| 787 | X11_XChangeProperty(display, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, | ||
| 788 | PropModeReplace, (unsigned char *)&wintype, 1); | ||
| 789 | if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, true)) { | ||
| 790 | _NET_WM_BYPASS_COMPOSITOR = X11_XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); | ||
| 791 | X11_XChangeProperty(display, w, _NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, | ||
| 792 | PropModeReplace, | ||
| 793 | (unsigned char *)&compositor, 1); | ||
| 794 | } | ||
| 795 | |||
| 796 | { | ||
| 797 | Atom protocols[4]; | ||
| 798 | int proto_count = 0; | ||
| 799 | |||
| 800 | protocols[proto_count++] = data->atoms.WM_DELETE_WINDOW; // Allow window to be deleted by the WM | ||
| 801 | protocols[proto_count++] = data->atoms.WM_TAKE_FOCUS; // Since we will want to set input focus explicitly | ||
| 802 | |||
| 803 | // Default to using ping if there is no hint | ||
| 804 | if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NET_WM_PING, true)) { | ||
| 805 | protocols[proto_count++] = data->atoms._NET_WM_PING; // Respond so WM knows we're alive | ||
| 806 | } | ||
| 807 | |||
| 808 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 809 | if (use_resize_sync) { | ||
| 810 | protocols[proto_count++] = data->atoms._NET_WM_SYNC_REQUEST; /* Respond after completing resize */ | ||
| 811 | } | ||
| 812 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 813 | |||
| 814 | SDL_assert(proto_count <= sizeof(protocols) / sizeof(protocols[0])); | ||
| 815 | |||
| 816 | X11_XSetWMProtocols(display, w, protocols, proto_count); | ||
| 817 | } | ||
| 818 | |||
| 819 | if (!SetupWindowData(_this, window, w)) { | ||
| 820 | X11_XDestroyWindow(display, w); | ||
| 821 | return false; | ||
| 822 | } | ||
| 823 | windowdata = window->internal; | ||
| 824 | |||
| 825 | // Set the parent if this is a non-popup window. | ||
| 826 | if (!SDL_WINDOW_IS_POPUP(window) && window->parent) { | ||
| 827 | X11_XSetTransientForHint(display, w, window->parent->internal->xwindow); | ||
| 828 | } | ||
| 829 | |||
| 830 | // Set the flag if the borders were forced on when creating a fullscreen window for later removal. | ||
| 831 | windowdata->fullscreen_borders_forced_on = !!(window->pending_flags & SDL_WINDOW_FULLSCREEN) && | ||
| 832 | !!(window->flags & SDL_WINDOW_BORDERLESS); | ||
| 833 | |||
| 834 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 835 | if (use_resize_sync) { | ||
| 836 | X11_InitResizeSync(window); | ||
| 837 | } | ||
| 838 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 839 | |||
| 840 | #if defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2) || defined(SDL_VIDEO_OPENGL_EGL) | ||
| 841 | if ((window->flags & SDL_WINDOW_OPENGL) && | ||
| 842 | ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) || | ||
| 843 | SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) | ||
| 844 | #ifdef SDL_VIDEO_OPENGL_GLX | ||
| 845 | && (!_this->gl_data || X11_GL_UseEGL(_this)) | ||
| 846 | #endif | ||
| 847 | ) { | ||
| 848 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 849 | if (!_this->egl_data) { | ||
| 850 | return false; | ||
| 851 | } | ||
| 852 | |||
| 853 | // Create the GLES window surface | ||
| 854 | windowdata->egl_surface = SDL_EGL_CreateSurface(_this, window, (NativeWindowType)w); | ||
| 855 | |||
| 856 | if (windowdata->egl_surface == EGL_NO_SURFACE) { | ||
| 857 | return SDL_SetError("Could not create GLES window surface"); | ||
| 858 | } | ||
| 859 | #else | ||
| 860 | return SDL_SetError("Could not create GLES window surface (EGL support not configured)"); | ||
| 861 | #endif // SDL_VIDEO_OPENGL_EGL | ||
| 862 | } | ||
| 863 | #endif | ||
| 864 | |||
| 865 | #ifdef SDL_VIDEO_DRIVER_X11_XSHAPE | ||
| 866 | // Tooltips do not receive input | ||
| 867 | if (window->flags & SDL_WINDOW_TOOLTIP) { | ||
| 868 | Region region = X11_XCreateRegion(); | ||
| 869 | X11_XShapeCombineRegion(display, w, ShapeInput, 0, 0, region, ShapeSet); | ||
| 870 | X11_XDestroyRegion(region); | ||
| 871 | } | ||
| 872 | #endif | ||
| 873 | |||
| 874 | SetupWindowInput(_this, window); | ||
| 875 | |||
| 876 | // For _ICC_PROFILE. | ||
| 877 | X11_XSelectInput(display, RootWindow(display, screen), PropertyChangeMask); | ||
| 878 | |||
| 879 | X11_XFlush(display); | ||
| 880 | |||
| 881 | return true; | ||
| 882 | } | ||
| 883 | |||
| 884 | char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow) | ||
| 885 | { | ||
| 886 | SDL_VideoData *data = _this->internal; | ||
| 887 | Display *display = data->display; | ||
| 888 | int status, real_format; | ||
| 889 | Atom real_type; | ||
| 890 | unsigned long items_read, items_left; | ||
| 891 | unsigned char *propdata; | ||
| 892 | char *title = NULL; | ||
| 893 | |||
| 894 | status = X11_XGetWindowProperty(display, xwindow, data->atoms._NET_WM_NAME, | ||
| 895 | 0L, 8192L, False, data->atoms.UTF8_STRING, &real_type, &real_format, | ||
| 896 | &items_read, &items_left, &propdata); | ||
| 897 | if (status == Success && propdata) { | ||
| 898 | title = SDL_strdup(SDL_static_cast(char *, propdata)); | ||
| 899 | X11_XFree(propdata); | ||
| 900 | } else { | ||
| 901 | status = X11_XGetWindowProperty(display, xwindow, XA_WM_NAME, | ||
| 902 | 0L, 8192L, False, XA_STRING, &real_type, &real_format, | ||
| 903 | &items_read, &items_left, &propdata); | ||
| 904 | if (status == Success && propdata) { | ||
| 905 | title = SDL_iconv_string("UTF-8", "", SDL_static_cast(char *, propdata), items_read + 1); | ||
| 906 | SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Failed to convert WM_NAME title expecting UTF8! Title: %s", title); | ||
| 907 | X11_XFree(propdata); | ||
| 908 | } else { | ||
| 909 | SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Could not get any window title response from Xorg, returning empty string!"); | ||
| 910 | title = SDL_strdup(""); | ||
| 911 | } | ||
| 912 | } | ||
| 913 | return title; | ||
| 914 | } | ||
| 915 | |||
| 916 | void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 917 | { | ||
| 918 | SDL_WindowData *data = window->internal; | ||
| 919 | Window xwindow = data->xwindow; | ||
| 920 | Display *display = data->videodata->display; | ||
| 921 | char *title = window->title ? window->title : ""; | ||
| 922 | |||
| 923 | SDL_X11_SetWindowTitle(display, xwindow, title); | ||
| 924 | } | ||
| 925 | |||
| 926 | static bool caught_x11_error = false; | ||
| 927 | static int X11_CatchAnyError(Display *d, XErrorEvent *e) | ||
| 928 | { | ||
| 929 | /* this may happen during tumultuous times when we are polling anyhow, | ||
| 930 | so just note we had an error and return control. */ | ||
| 931 | caught_x11_error = true; | ||
| 932 | return 0; | ||
| 933 | } | ||
| 934 | |||
| 935 | /* Wait a brief time, or not, to see if the window manager decided to move/resize the window. | ||
| 936 | * Send MOVED and RESIZED window events */ | ||
| 937 | static bool X11_SyncWindowTimeout(SDL_VideoDevice *_this, SDL_Window *window, Uint64 param_timeout) | ||
| 938 | { | ||
| 939 | SDL_WindowData *data = window->internal; | ||
| 940 | Display *display = data->videodata->display; | ||
| 941 | int (*prev_handler)(Display *, XErrorEvent *); | ||
| 942 | Uint64 timeout = 0; | ||
| 943 | bool force_exit = false; | ||
| 944 | bool result = true; | ||
| 945 | |||
| 946 | X11_XSync(display, False); | ||
| 947 | prev_handler = X11_XSetErrorHandler(X11_CatchAnyError); | ||
| 948 | |||
| 949 | if (param_timeout) { | ||
| 950 | timeout = SDL_GetTicksNS() + param_timeout; | ||
| 951 | } | ||
| 952 | |||
| 953 | while (true) { | ||
| 954 | X11_XSync(display, False); | ||
| 955 | X11_PumpEvents(_this); | ||
| 956 | |||
| 957 | if ((data->pending_operation & X11_PENDING_OP_MOVE) && (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top)) { | ||
| 958 | data->pending_operation &= ~X11_PENDING_OP_MOVE; | ||
| 959 | } | ||
| 960 | if ((data->pending_operation & X11_PENDING_OP_RESIZE) && (window->w == data->expected.w && window->h == data->expected.h)) { | ||
| 961 | data->pending_operation &= ~X11_PENDING_OP_RESIZE; | ||
| 962 | } | ||
| 963 | |||
| 964 | if (data->pending_operation == X11_PENDING_OP_NONE) { | ||
| 965 | if (force_exit || | ||
| 966 | (window->x == data->expected.x + data->border_left && window->y == data->expected.y + data->border_top && | ||
| 967 | window->w == data->expected.w && window->h == data->expected.h)) { | ||
| 968 | // The window is in the expected state and nothing is pending. Done. | ||
| 969 | break; | ||
| 970 | } | ||
| 971 | |||
| 972 | /* No operations are pending, but the window still isn't in the expected state. | ||
| 973 | * Try one more time before exiting. | ||
| 974 | */ | ||
| 975 | force_exit = true; | ||
| 976 | } | ||
| 977 | |||
| 978 | if (SDL_GetTicksNS() >= timeout) { | ||
| 979 | // Timed out without the expected values. Update the requested data so future sync calls won't block. | ||
| 980 | data->expected.x = window->x; | ||
| 981 | data->expected.y = window->y; | ||
| 982 | data->expected.w = window->w; | ||
| 983 | data->expected.h = window->h; | ||
| 984 | |||
| 985 | result = false; | ||
| 986 | break; | ||
| 987 | } | ||
| 988 | |||
| 989 | SDL_Delay(10); | ||
| 990 | } | ||
| 991 | |||
| 992 | data->pending_operation = X11_PENDING_OP_NONE; | ||
| 993 | |||
| 994 | if (!caught_x11_error) { | ||
| 995 | X11_PumpEvents(_this); | ||
| 996 | } else { | ||
| 997 | result = false; | ||
| 998 | } | ||
| 999 | |||
| 1000 | X11_XSetErrorHandler(prev_handler); | ||
| 1001 | caught_x11_error = false; | ||
| 1002 | |||
| 1003 | return result; | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon) | ||
| 1007 | { | ||
| 1008 | SDL_WindowData *data = window->internal; | ||
| 1009 | Display *display = data->videodata->display; | ||
| 1010 | Atom _NET_WM_ICON = data->videodata->atoms._NET_WM_ICON; | ||
| 1011 | int (*prevHandler)(Display *, XErrorEvent *) = NULL; | ||
| 1012 | bool result = true; | ||
| 1013 | |||
| 1014 | if (icon) { | ||
| 1015 | int x, y; | ||
| 1016 | int propsize; | ||
| 1017 | long *propdata; | ||
| 1018 | Uint32 *src; | ||
| 1019 | long *dst; | ||
| 1020 | |||
| 1021 | // Set the _NET_WM_ICON property | ||
| 1022 | SDL_assert(icon->format == SDL_PIXELFORMAT_ARGB8888); | ||
| 1023 | propsize = 2 + (icon->w * icon->h); | ||
| 1024 | propdata = SDL_malloc(propsize * sizeof(long)); | ||
| 1025 | |||
| 1026 | if (!propdata) { | ||
| 1027 | return false; | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | X11_XSync(display, False); | ||
| 1031 | prevHandler = X11_XSetErrorHandler(X11_CatchAnyError); | ||
| 1032 | |||
| 1033 | propdata[0] = icon->w; | ||
| 1034 | propdata[1] = icon->h; | ||
| 1035 | dst = &propdata[2]; | ||
| 1036 | |||
| 1037 | for (y = 0; y < icon->h; ++y) { | ||
| 1038 | src = (Uint32 *)((Uint8 *)icon->pixels + y * icon->pitch); | ||
| 1039 | for (x = 0; x < icon->w; ++x) { | ||
| 1040 | *dst++ = *src++; | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | X11_XChangeProperty(display, data->xwindow, _NET_WM_ICON, XA_CARDINAL, | ||
| 1045 | 32, PropModeReplace, (unsigned char *)propdata, | ||
| 1046 | propsize); | ||
| 1047 | SDL_free(propdata); | ||
| 1048 | |||
| 1049 | if (caught_x11_error) { | ||
| 1050 | result = SDL_SetError("An error occurred while trying to set the window's icon"); | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | X11_XFlush(display); | ||
| 1055 | |||
| 1056 | if (prevHandler) { | ||
| 1057 | X11_XSetErrorHandler(prevHandler); | ||
| 1058 | caught_x11_error = false; | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | return result; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position) | ||
| 1065 | { | ||
| 1066 | SDL_WindowData *data = window->internal; | ||
| 1067 | Display *display = data->videodata->display; | ||
| 1068 | const int rel_x = use_current_position ? window->x : window->pending.x; | ||
| 1069 | const int rel_y = use_current_position ? window->y : window->pending.y; | ||
| 1070 | |||
| 1071 | SDL_RelativeToGlobalForWindow(window, | ||
| 1072 | rel_x - data->border_left, rel_y - data->border_top, | ||
| 1073 | &data->expected.x, &data->expected.y); | ||
| 1074 | |||
| 1075 | // Attempt to move the window | ||
| 1076 | if (window->flags & SDL_WINDOW_HIDDEN) { | ||
| 1077 | window->internal->pending_position = true; | ||
| 1078 | } else { | ||
| 1079 | data->pending_operation |= X11_PENDING_OP_MOVE; | ||
| 1080 | X11_XMoveWindow(display, data->xwindow, data->expected.x, data->expected.y); | ||
| 1081 | } | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1085 | { | ||
| 1086 | // Sync any pending fullscreen or maximize events. | ||
| 1087 | if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE)) { | ||
| 1088 | X11_FlushPendingEvents(_this, window); | ||
| 1089 | } | ||
| 1090 | |||
| 1091 | // Set the position as pending if the window is maximized with a restore pending. | ||
| 1092 | if (window->flags & SDL_WINDOW_MAXIMIZED) { | ||
| 1093 | if (window->internal->pending_operation & X11_PENDING_OP_RESTORE) { | ||
| 1094 | window->internal->pending_position = true; | ||
| 1095 | } | ||
| 1096 | return true; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1100 | if (SDL_WINDOW_IS_POPUP(window)) { | ||
| 1101 | X11_ConstrainPopup(window, true); | ||
| 1102 | } | ||
| 1103 | X11_UpdateWindowPosition(window, false); | ||
| 1104 | } else { | ||
| 1105 | SDL_UpdateFullscreenMode(window, SDL_FULLSCREEN_OP_UPDATE, true); | ||
| 1106 | } | ||
| 1107 | return true; | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | void X11_SetWindowMinMax(SDL_Window *window, bool use_current) | ||
| 1111 | { | ||
| 1112 | SDL_WindowData *data = window->internal; | ||
| 1113 | Display *display = data->videodata->display; | ||
| 1114 | XSizeHints *sizehints = X11_XAllocSizeHints(); | ||
| 1115 | long hint_flags = 0; | ||
| 1116 | |||
| 1117 | X11_XGetWMNormalHints(display, data->xwindow, sizehints, &hint_flags); | ||
| 1118 | sizehints->flags &= ~(PMinSize | PMaxSize | PAspect); | ||
| 1119 | |||
| 1120 | if (data->window->flags & SDL_WINDOW_RESIZABLE) { | ||
| 1121 | if (data->window->min_w || data->window->min_h) { | ||
| 1122 | sizehints->flags |= PMinSize; | ||
| 1123 | sizehints->min_width = data->window->min_w; | ||
| 1124 | sizehints->min_height = data->window->min_h; | ||
| 1125 | } | ||
| 1126 | if (data->window->max_w || data->window->max_h) { | ||
| 1127 | sizehints->flags |= PMaxSize; | ||
| 1128 | sizehints->max_width = data->window->max_w; | ||
| 1129 | sizehints->max_height = data->window->max_h; | ||
| 1130 | } | ||
| 1131 | if (data->window->min_aspect > 0.0f || data->window->max_aspect > 0.0f) { | ||
| 1132 | sizehints->flags |= PAspect; | ||
| 1133 | SDL_CalculateFraction(data->window->min_aspect, &sizehints->min_aspect.x, &sizehints->min_aspect.y); | ||
| 1134 | SDL_CalculateFraction(data->window->max_aspect, &sizehints->max_aspect.x, &sizehints->max_aspect.y); | ||
| 1135 | } | ||
| 1136 | } else { | ||
| 1137 | // Set the min/max to the same values to make the window non-resizable | ||
| 1138 | sizehints->flags |= PMinSize | PMaxSize; | ||
| 1139 | sizehints->min_width = sizehints->max_width = use_current ? data->window->floating.w : window->windowed.w; | ||
| 1140 | sizehints->min_height = sizehints->max_height = use_current ? data->window->floating.h : window->windowed.h; | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | X11_XSetWMNormalHints(display, data->xwindow, sizehints); | ||
| 1144 | X11_XFree(sizehints); | ||
| 1145 | } | ||
| 1146 | |||
| 1147 | void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1148 | { | ||
| 1149 | if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { | ||
| 1150 | X11_SyncWindow(_this, window); | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1154 | X11_SetWindowMinMax(window, true); | ||
| 1155 | } | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1159 | { | ||
| 1160 | if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { | ||
| 1161 | X11_SyncWindow(_this, window); | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1165 | X11_SetWindowMinMax(window, true); | ||
| 1166 | } | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1170 | { | ||
| 1171 | if (window->internal->pending_operation & X11_PENDING_OP_FULLSCREEN) { | ||
| 1172 | X11_SyncWindow(_this, window); | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1176 | X11_SetWindowMinMax(window, true); | ||
| 1177 | } | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1181 | { | ||
| 1182 | SDL_WindowData *data = window->internal; | ||
| 1183 | Display *display = data->videodata->display; | ||
| 1184 | |||
| 1185 | /* Wait for pending maximize and fullscreen operations to complete, as these windows | ||
| 1186 | * don't get size changes. | ||
| 1187 | */ | ||
| 1188 | if (data->pending_operation & (X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_FULLSCREEN)) { | ||
| 1189 | X11_FlushPendingEvents(_this, window); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | // Set the size as pending if the window is being restored. | ||
| 1193 | if (window->flags & (SDL_WINDOW_MAXIMIZED | SDL_WINDOW_FULLSCREEN)) { | ||
| 1194 | // New size will be set when the window is restored. | ||
| 1195 | if (data->pending_operation & X11_PENDING_OP_RESTORE) { | ||
| 1196 | data->pending_size = true; | ||
| 1197 | } else { | ||
| 1198 | // Can't resize the window. | ||
| 1199 | window->last_size_pending = false; | ||
| 1200 | } | ||
| 1201 | return; | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | if (!(window->flags & SDL_WINDOW_RESIZABLE)) { | ||
| 1205 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1206 | /* Apparently, if the X11 Window is set to a 'non-resizable' window, you cannot resize it using the X11_XResizeWindow, thus | ||
| 1207 | * we must set the size hints to adjust the window size. | ||
| 1208 | */ | ||
| 1209 | XSizeHints *sizehints = X11_XAllocSizeHints(); | ||
| 1210 | long userhints; | ||
| 1211 | int dest_x, dest_y; | ||
| 1212 | |||
| 1213 | X11_XGetWMNormalHints(display, data->xwindow, sizehints, &userhints); | ||
| 1214 | |||
| 1215 | sizehints->min_width = sizehints->max_width = window->pending.w; | ||
| 1216 | sizehints->min_height = sizehints->max_height = window->pending.h; | ||
| 1217 | sizehints->flags |= PMinSize | PMaxSize; | ||
| 1218 | |||
| 1219 | X11_XSetWMNormalHints(display, data->xwindow, sizehints); | ||
| 1220 | |||
| 1221 | /* From Pierre-Loup: | ||
| 1222 | WMs each have their little quirks with that. When you change the | ||
| 1223 | size hints, they get a ConfigureNotify event with the | ||
| 1224 | WM_NORMAL_SIZE_HINTS Atom. They all save the hints then, but they | ||
| 1225 | don't all resize the window right away to enforce the new hints. | ||
| 1226 | |||
| 1227 | Some of them resize only after: | ||
| 1228 | - A user-initiated move or resize | ||
| 1229 | - A code-initiated move or resize | ||
| 1230 | - Hiding & showing window (Unmap & map) | ||
| 1231 | |||
| 1232 | The following move & resize seems to help a lot of WMs that didn't | ||
| 1233 | properly update after the hints were changed. We don't do a | ||
| 1234 | hide/show, because there are supposedly subtle problems with doing so | ||
| 1235 | and transitioning from windowed to fullscreen in Unity. | ||
| 1236 | */ | ||
| 1237 | X11_XResizeWindow(display, data->xwindow, window->pending.w, window->pending.h); | ||
| 1238 | const int x = window->last_position_pending ? window->pending.x : window->x; | ||
| 1239 | const int y = window->last_position_pending ? window->pending.y : window->y; | ||
| 1240 | SDL_RelativeToGlobalForWindow(window, | ||
| 1241 | x - data->border_left, | ||
| 1242 | y - data->border_top, | ||
| 1243 | &dest_x, &dest_y); | ||
| 1244 | X11_XMoveWindow(display, data->xwindow, dest_x, dest_y); | ||
| 1245 | X11_XRaiseWindow(display, data->xwindow); | ||
| 1246 | |||
| 1247 | X11_XFree(sizehints); | ||
| 1248 | } | ||
| 1249 | } else { | ||
| 1250 | data->expected.w = window->pending.w; | ||
| 1251 | data->expected.h = window->pending.h; | ||
| 1252 | data->pending_operation |= X11_PENDING_OP_RESIZE; | ||
| 1253 | X11_XResizeWindow(display, data->xwindow, data->expected.w, data->expected.h); | ||
| 1254 | } | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right) | ||
| 1258 | { | ||
| 1259 | SDL_WindowData *data = window->internal; | ||
| 1260 | |||
| 1261 | *left = data->border_left; | ||
| 1262 | *right = data->border_right; | ||
| 1263 | *top = data->border_top; | ||
| 1264 | *bottom = data->border_bottom; | ||
| 1265 | |||
| 1266 | return true; | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity) | ||
| 1270 | { | ||
| 1271 | SDL_WindowData *data = window->internal; | ||
| 1272 | Display *display = data->videodata->display; | ||
| 1273 | Atom _NET_WM_WINDOW_OPACITY = data->videodata->atoms._NET_WM_WINDOW_OPACITY; | ||
| 1274 | |||
| 1275 | if (opacity == 1.0f) { | ||
| 1276 | X11_XDeleteProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY); | ||
| 1277 | } else { | ||
| 1278 | const Uint32 FullyOpaque = 0xFFFFFFFF; | ||
| 1279 | const long alpha = (long)((double)opacity * (double)FullyOpaque); | ||
| 1280 | X11_XChangeProperty(display, data->xwindow, _NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, | ||
| 1281 | PropModeReplace, (unsigned char *)&alpha, 1); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | return true; | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent) | ||
| 1288 | { | ||
| 1289 | SDL_WindowData *data = window->internal; | ||
| 1290 | SDL_WindowData *parent_data = parent ? parent->internal : NULL; | ||
| 1291 | SDL_VideoData *video_data = _this->internal; | ||
| 1292 | Display *display = video_data->display; | ||
| 1293 | |||
| 1294 | if (parent_data) { | ||
| 1295 | X11_XSetTransientForHint(display, data->xwindow, parent_data->xwindow); | ||
| 1296 | } else { | ||
| 1297 | X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | return true; | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal) | ||
| 1304 | { | ||
| 1305 | SDL_WindowData *data = window->internal; | ||
| 1306 | SDL_VideoData *video_data = _this->internal; | ||
| 1307 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1308 | Display *display = video_data->display; | ||
| 1309 | Uint32 flags = window->flags; | ||
| 1310 | Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; | ||
| 1311 | Atom _NET_WM_STATE_MODAL = data->videodata->atoms._NET_WM_STATE_MODAL; | ||
| 1312 | |||
| 1313 | if (modal) { | ||
| 1314 | flags |= SDL_WINDOW_MODAL; | ||
| 1315 | } else { | ||
| 1316 | flags &= ~SDL_WINDOW_MODAL; | ||
| 1317 | X11_XDeleteProperty(display, data->xwindow, video_data->atoms.WM_TRANSIENT_FOR); | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1321 | XEvent e; | ||
| 1322 | |||
| 1323 | SDL_zero(e); | ||
| 1324 | e.xany.type = ClientMessage; | ||
| 1325 | e.xclient.message_type = _NET_WM_STATE; | ||
| 1326 | e.xclient.format = 32; | ||
| 1327 | e.xclient.window = data->xwindow; | ||
| 1328 | e.xclient.data.l[0] = modal ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; | ||
| 1329 | e.xclient.data.l[1] = _NET_WM_STATE_MODAL; | ||
| 1330 | e.xclient.data.l[3] = 0l; | ||
| 1331 | |||
| 1332 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1333 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1334 | } else { | ||
| 1335 | X11_SetNetWMState(_this, data->xwindow, flags); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | X11_XFlush(display); | ||
| 1339 | |||
| 1340 | return true; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered) | ||
| 1344 | { | ||
| 1345 | const bool focused = (window->flags & SDL_WINDOW_INPUT_FOCUS) ? true : false; | ||
| 1346 | const bool visible = (!(window->flags & SDL_WINDOW_HIDDEN)) ? true : false; | ||
| 1347 | SDL_WindowData *data = window->internal; | ||
| 1348 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1349 | Display *display = data->videodata->display; | ||
| 1350 | XEvent event; | ||
| 1351 | |||
| 1352 | if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) { | ||
| 1353 | X11_SyncWindow(_this, window); | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode. | ||
| 1357 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1358 | SetWindowBordered(display, displaydata->screen, data->xwindow, bordered); | ||
| 1359 | X11_XFlush(display); | ||
| 1360 | |||
| 1361 | if (visible) { | ||
| 1362 | XWindowAttributes attr; | ||
| 1363 | do { | ||
| 1364 | X11_XSync(display, False); | ||
| 1365 | X11_XGetWindowAttributes(display, data->xwindow, &attr); | ||
| 1366 | } while (attr.map_state != IsViewable); | ||
| 1367 | |||
| 1368 | if (focused) { | ||
| 1369 | X11_XSetInputFocus(display, data->xwindow, RevertToParent, CurrentTime); | ||
| 1370 | } | ||
| 1371 | } | ||
| 1372 | |||
| 1373 | // make sure these don't make it to the real event queue if they fired here. | ||
| 1374 | X11_XSync(display, False); | ||
| 1375 | X11_XCheckIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); | ||
| 1376 | X11_XCheckIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); | ||
| 1377 | |||
| 1378 | // Turning the borders off doesn't send an extent event, so they must be cleared here. | ||
| 1379 | X11_GetBorderValues(data); | ||
| 1380 | |||
| 1381 | // Make sure the window manager didn't resize our window for the difference. | ||
| 1382 | X11_XResizeWindow(display, data->xwindow, window->floating.w, window->floating.h); | ||
| 1383 | X11_XSync(display, False); | ||
| 1384 | } else { | ||
| 1385 | // If fullscreen, set a flag to toggle the borders when returning to windowed mode. | ||
| 1386 | data->toggle_borders = true; | ||
| 1387 | data->fullscreen_borders_forced_on = false; | ||
| 1388 | } | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable) | ||
| 1392 | { | ||
| 1393 | SDL_WindowData *data = window->internal; | ||
| 1394 | |||
| 1395 | if (data->pending_operation & X11_PENDING_OP_FULLSCREEN) { | ||
| 1396 | X11_SyncWindow(_this, window); | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | // If the window is fullscreen, the resize capability will be set/cleared when it is returned to windowed mode. | ||
| 1400 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1401 | X11_SetWindowMinMax(window, true); | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top) | ||
| 1406 | { | ||
| 1407 | SDL_WindowData *data = window->internal; | ||
| 1408 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1409 | Display *display = data->videodata->display; | ||
| 1410 | Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; | ||
| 1411 | Atom _NET_WM_STATE_ABOVE = data->videodata->atoms._NET_WM_STATE_ABOVE; | ||
| 1412 | |||
| 1413 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1414 | XEvent e; | ||
| 1415 | |||
| 1416 | SDL_zero(e); | ||
| 1417 | e.xany.type = ClientMessage; | ||
| 1418 | e.xclient.message_type = _NET_WM_STATE; | ||
| 1419 | e.xclient.format = 32; | ||
| 1420 | e.xclient.window = data->xwindow; | ||
| 1421 | e.xclient.data.l[0] = | ||
| 1422 | on_top ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; | ||
| 1423 | e.xclient.data.l[1] = _NET_WM_STATE_ABOVE; | ||
| 1424 | e.xclient.data.l[3] = 0l; | ||
| 1425 | |||
| 1426 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1427 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1428 | } else { | ||
| 1429 | X11_SetNetWMState(_this, data->xwindow, window->flags); | ||
| 1430 | } | ||
| 1431 | X11_XFlush(display); | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1435 | { | ||
| 1436 | SDL_WindowData *data = window->internal; | ||
| 1437 | Display *display = data->videodata->display; | ||
| 1438 | bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, true); | ||
| 1439 | bool position_is_absolute = false; | ||
| 1440 | bool set_position = false; | ||
| 1441 | XEvent event; | ||
| 1442 | |||
| 1443 | if (SDL_WINDOW_IS_POPUP(window)) { | ||
| 1444 | // Update the position in case the parent moved while we were hidden | ||
| 1445 | X11_ConstrainPopup(window, true); | ||
| 1446 | data->pending_position = true; | ||
| 1447 | |||
| 1448 | // Coordinates after X11_ConstrainPopup() are already in the global space. | ||
| 1449 | position_is_absolute = true; | ||
| 1450 | set_position = true; | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | /* Whether XMapRaised focuses the window is based on the window type and it is | ||
| 1454 | * wm specific. There isn't much we can do here */ | ||
| 1455 | (void)bActivate; | ||
| 1456 | |||
| 1457 | if (!X11_IsWindowMapped(_this, window)) { | ||
| 1458 | X11_XMapRaised(display, data->xwindow); | ||
| 1459 | /* Blocking wait for "MapNotify" event. | ||
| 1460 | * We use X11_XIfEvent because pXWindowEvent takes a mask rather than a type, | ||
| 1461 | * and XCheckTypedWindowEvent doesn't block */ | ||
| 1462 | if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) { | ||
| 1463 | X11_XIfEvent(display, &event, &isMapNotify, (XPointer)&data->xwindow); | ||
| 1464 | } | ||
| 1465 | X11_XFlush(display); | ||
| 1466 | set_position = data->pending_position || | ||
| 1467 | (!(window->flags & SDL_WINDOW_BORDERLESS) && !window->undefined_x && !window->undefined_y); | ||
| 1468 | } | ||
| 1469 | |||
| 1470 | if (!data->videodata->net_wm) { | ||
| 1471 | // no WM means no FocusIn event, which confuses us. Force it. | ||
| 1472 | X11_XSync(display, False); | ||
| 1473 | X11_XSetInputFocus(display, data->xwindow, RevertToNone, CurrentTime); | ||
| 1474 | X11_XFlush(display); | ||
| 1475 | } | ||
| 1476 | |||
| 1477 | // Popup menus grab the keyboard | ||
| 1478 | if (window->flags & SDL_WINDOW_POPUP_MENU) { | ||
| 1479 | X11_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus()); | ||
| 1480 | } | ||
| 1481 | |||
| 1482 | // Get some valid border values, if we haven't received them yet | ||
| 1483 | if (data->border_left == 0 && data->border_right == 0 && data->border_top == 0 && data->border_bottom == 0) { | ||
| 1484 | X11_GetBorderValues(data); | ||
| 1485 | } | ||
| 1486 | |||
| 1487 | if (set_position) { | ||
| 1488 | // Apply the window position, accounting for offsets due to the borders appearing. | ||
| 1489 | const int tx = data->pending_position ? window->pending.x : window->x; | ||
| 1490 | const int ty = data->pending_position ? window->pending.y : window->y; | ||
| 1491 | int x, y; | ||
| 1492 | if (position_is_absolute) { | ||
| 1493 | x = tx; | ||
| 1494 | y = ty; | ||
| 1495 | } else { | ||
| 1496 | SDL_RelativeToGlobalForWindow(window, | ||
| 1497 | tx - data->border_left, ty - data->border_top, | ||
| 1498 | &x, &y); | ||
| 1499 | } | ||
| 1500 | data->pending_position = false; | ||
| 1501 | X11_XMoveWindow(display, data->xwindow, x, y); | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | /* Some window managers can send garbage coordinates while mapping the window, so don't emit size and position | ||
| 1505 | * events during the initial configure events. | ||
| 1506 | */ | ||
| 1507 | data->size_move_event_flags = X11_SIZE_MOVE_EVENTS_DISABLE; | ||
| 1508 | X11_XSync(display, False); | ||
| 1509 | X11_PumpEvents(_this); | ||
| 1510 | data->size_move_event_flags = 0; | ||
| 1511 | |||
| 1512 | // If a configure event was received (type is non-zero), send the final window size and coordinates. | ||
| 1513 | if (data->last_xconfigure.type) { | ||
| 1514 | int x, y; | ||
| 1515 | SDL_GlobalToRelativeForWindow(data->window, data->last_xconfigure.x, data->last_xconfigure.y, &x, &y); | ||
| 1516 | SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_RESIZED, data->last_xconfigure.width, data->last_xconfigure.height); | ||
| 1517 | SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_MOVED, x, y); | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1522 | { | ||
| 1523 | SDL_WindowData *data = window->internal; | ||
| 1524 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1525 | int screen = (displaydata ? displaydata->screen : 0); | ||
| 1526 | Display *display = data->videodata->display; | ||
| 1527 | XEvent event; | ||
| 1528 | |||
| 1529 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1530 | X11_XWithdrawWindow(display, data->xwindow, screen); | ||
| 1531 | // Blocking wait for "UnmapNotify" event | ||
| 1532 | if (!(window->flags & SDL_WINDOW_EXTERNAL) && X11_IsDisplayOk(display)) { | ||
| 1533 | X11_XIfEvent(display, &event, &isUnmapNotify, (XPointer)&data->xwindow); | ||
| 1534 | } | ||
| 1535 | X11_XFlush(display); | ||
| 1536 | } | ||
| 1537 | |||
| 1538 | // Transfer keyboard focus back to the parent | ||
| 1539 | if (window->flags & SDL_WINDOW_POPUP_MENU) { | ||
| 1540 | SDL_Window *new_focus = window->parent; | ||
| 1541 | bool set_focus = window == SDL_GetKeyboardFocus(); | ||
| 1542 | |||
| 1543 | // Find the highest level window, up to the toplevel parent, that isn't being hidden or destroyed. | ||
| 1544 | while (SDL_WINDOW_IS_POPUP(new_focus) && (new_focus->is_hiding || new_focus->is_destroying)) { | ||
| 1545 | new_focus = new_focus->parent; | ||
| 1546 | |||
| 1547 | // If some window in the chain currently had focus, set it to the new lowest-level window. | ||
| 1548 | if (!set_focus) { | ||
| 1549 | set_focus = new_focus == SDL_GetKeyboardFocus(); | ||
| 1550 | } | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | X11_SetKeyboardFocus(new_focus, set_focus); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | X11_XSync(display, False); | ||
| 1557 | X11_PumpEvents(_this); | ||
| 1558 | } | ||
| 1559 | |||
| 1560 | static bool X11_SetWindowActive(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1561 | { | ||
| 1562 | CHECK_WINDOW_DATA(window); | ||
| 1563 | |||
| 1564 | SDL_WindowData *data = window->internal; | ||
| 1565 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1566 | Display *display = data->videodata->display; | ||
| 1567 | Atom _NET_ACTIVE_WINDOW = data->videodata->atoms._NET_ACTIVE_WINDOW; | ||
| 1568 | |||
| 1569 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1570 | XEvent e; | ||
| 1571 | |||
| 1572 | // printf("SDL Window %p: sending _NET_ACTIVE_WINDOW with timestamp %lu\n", window, data->user_time); | ||
| 1573 | |||
| 1574 | SDL_zero(e); | ||
| 1575 | e.xany.type = ClientMessage; | ||
| 1576 | e.xclient.message_type = _NET_ACTIVE_WINDOW; | ||
| 1577 | e.xclient.format = 32; | ||
| 1578 | e.xclient.window = data->xwindow; | ||
| 1579 | e.xclient.data.l[0] = 1; // source indication. 1 = application | ||
| 1580 | e.xclient.data.l[1] = data->user_time; | ||
| 1581 | e.xclient.data.l[2] = 0; | ||
| 1582 | |||
| 1583 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1584 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1585 | |||
| 1586 | X11_XFlush(display); | ||
| 1587 | } | ||
| 1588 | return true; | ||
| 1589 | } | ||
| 1590 | |||
| 1591 | void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1592 | { | ||
| 1593 | SDL_WindowData *data = window->internal; | ||
| 1594 | Display *display = data->videodata->display; | ||
| 1595 | bool bActivate = SDL_GetHintBoolean(SDL_HINT_WINDOW_ACTIVATE_WHEN_RAISED, true); | ||
| 1596 | |||
| 1597 | X11_XRaiseWindow(display, data->xwindow); | ||
| 1598 | if (bActivate) { | ||
| 1599 | X11_SetWindowActive(_this, window); | ||
| 1600 | } | ||
| 1601 | X11_XFlush(display); | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | static bool X11_SetWindowMaximized(SDL_VideoDevice *_this, SDL_Window *window, bool maximized) | ||
| 1605 | { | ||
| 1606 | CHECK_WINDOW_DATA(window); | ||
| 1607 | |||
| 1608 | SDL_WindowData *data = window->internal; | ||
| 1609 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1610 | Display *display = data->videodata->display; | ||
| 1611 | Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; | ||
| 1612 | Atom _NET_WM_STATE_MAXIMIZED_VERT = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; | ||
| 1613 | Atom _NET_WM_STATE_MAXIMIZED_HORZ = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 1614 | |||
| 1615 | if (window->flags & SDL_WINDOW_FULLSCREEN) { | ||
| 1616 | /* Fullscreen windows are maximized on some window managers, | ||
| 1617 | and this is functional behavior, so don't remove that state | ||
| 1618 | now, we'll take care of it when we leave fullscreen mode. | ||
| 1619 | */ | ||
| 1620 | return true; | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1624 | XEvent e; | ||
| 1625 | |||
| 1626 | SDL_zero(e); | ||
| 1627 | e.xany.type = ClientMessage; | ||
| 1628 | e.xclient.message_type = _NET_WM_STATE; | ||
| 1629 | e.xclient.format = 32; | ||
| 1630 | e.xclient.window = data->xwindow; | ||
| 1631 | e.xclient.data.l[0] = | ||
| 1632 | maximized ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; | ||
| 1633 | e.xclient.data.l[1] = _NET_WM_STATE_MAXIMIZED_VERT; | ||
| 1634 | e.xclient.data.l[2] = _NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 1635 | e.xclient.data.l[3] = 0l; | ||
| 1636 | |||
| 1637 | if (maximized) { | ||
| 1638 | SDL_DisplayID displayID = SDL_GetDisplayForWindow(window); | ||
| 1639 | SDL_Rect bounds; | ||
| 1640 | |||
| 1641 | SDL_zero(bounds); | ||
| 1642 | SDL_GetDisplayUsableBounds(displayID, &bounds); | ||
| 1643 | |||
| 1644 | data->expected.x = bounds.x + data->border_left; | ||
| 1645 | data->expected.y = bounds.y + data->border_top; | ||
| 1646 | data->expected.w = bounds.w - (data->border_left + data->border_right); | ||
| 1647 | data->expected.h = bounds.h - (data->border_top + data->border_bottom); | ||
| 1648 | } else { | ||
| 1649 | data->expected.x = window->floating.x; | ||
| 1650 | data->expected.y = window->floating.y; | ||
| 1651 | data->expected.w = window->floating.w; | ||
| 1652 | data->expected.h = window->floating.h; | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1656 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1657 | } else { | ||
| 1658 | X11_SetNetWMState(_this, data->xwindow, window->flags); | ||
| 1659 | } | ||
| 1660 | X11_XFlush(display); | ||
| 1661 | |||
| 1662 | return true; | ||
| 1663 | } | ||
| 1664 | |||
| 1665 | void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1666 | { | ||
| 1667 | if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MINIMIZE)) { | ||
| 1668 | SDL_SyncWindow(window); | ||
| 1669 | } | ||
| 1670 | |||
| 1671 | if (window->flags & SDL_WINDOW_FULLSCREEN) { | ||
| 1672 | // If fullscreen, just toggle the restored state. | ||
| 1673 | window->internal->window_was_maximized = true; | ||
| 1674 | return; | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | if (!(window->flags & SDL_WINDOW_MINIMIZED)) { | ||
| 1678 | window->internal->pending_operation |= X11_PENDING_OP_MAXIMIZE; | ||
| 1679 | X11_SetWindowMaximized(_this, window, true); | ||
| 1680 | } | ||
| 1681 | } | ||
| 1682 | |||
| 1683 | void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1684 | { | ||
| 1685 | SDL_WindowData *data = window->internal; | ||
| 1686 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 1687 | Display *display = data->videodata->display; | ||
| 1688 | |||
| 1689 | if (data->pending_operation & SDL_WINDOW_FULLSCREEN) { | ||
| 1690 | SDL_SyncWindow(window); | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | data->pending_operation |= X11_PENDING_OP_MINIMIZE; | ||
| 1694 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1695 | data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); | ||
| 1696 | } | ||
| 1697 | X11_XIconifyWindow(display, data->xwindow, displaydata->screen); | ||
| 1698 | X11_XFlush(display); | ||
| 1699 | } | ||
| 1700 | |||
| 1701 | void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 1702 | { | ||
| 1703 | if (window->internal->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MINIMIZE)) { | ||
| 1704 | SDL_SyncWindow(window); | ||
| 1705 | } | ||
| 1706 | |||
| 1707 | if ((window->flags & SDL_WINDOW_FULLSCREEN) && !(window->flags & SDL_WINDOW_MINIMIZED)) { | ||
| 1708 | // If fullscreen and not minimized, just toggle the restored state. | ||
| 1709 | window->internal->window_was_maximized = false; | ||
| 1710 | return; | ||
| 1711 | } | ||
| 1712 | |||
| 1713 | if (window->flags & (SDL_WINDOW_MINIMIZED | SDL_WINDOW_MAXIMIZED) || | ||
| 1714 | (window->internal->pending_operation & X11_PENDING_OP_MINIMIZE)) { | ||
| 1715 | window->internal->pending_operation |= X11_PENDING_OP_RESTORE; | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | // If the window was minimized while maximized, restore as maximized. | ||
| 1719 | const bool maximize = !!(window->flags & SDL_WINDOW_MINIMIZED) && window->internal->window_was_maximized; | ||
| 1720 | X11_SetWindowMaximized(_this, window, maximize); | ||
| 1721 | X11_ShowWindow(_this, window); | ||
| 1722 | X11_SetWindowActive(_this, window); | ||
| 1723 | } | ||
| 1724 | |||
| 1725 | // This asks the Window Manager to handle fullscreen for us. This is the modern way. | ||
| 1726 | static SDL_FullscreenResult X11_SetWindowFullscreenViaWM(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen) | ||
| 1727 | { | ||
| 1728 | CHECK_WINDOW_DATA(window); | ||
| 1729 | CHECK_DISPLAY_DATA(_display); | ||
| 1730 | |||
| 1731 | SDL_WindowData *data = window->internal; | ||
| 1732 | SDL_DisplayData *displaydata = _display->internal; | ||
| 1733 | Display *display = data->videodata->display; | ||
| 1734 | Atom _NET_WM_STATE = data->videodata->atoms._NET_WM_STATE; | ||
| 1735 | Atom _NET_WM_STATE_FULLSCREEN = data->videodata->atoms._NET_WM_STATE_FULLSCREEN; | ||
| 1736 | |||
| 1737 | if (X11_IsWindowMapped(_this, window)) { | ||
| 1738 | XEvent e; | ||
| 1739 | |||
| 1740 | // Flush any pending fullscreen events. | ||
| 1741 | if (data->pending_operation & (X11_PENDING_OP_FULLSCREEN | X11_PENDING_OP_MAXIMIZE | X11_PENDING_OP_MOVE)) { | ||
| 1742 | X11_SyncWindow(_this, window); | ||
| 1743 | } | ||
| 1744 | |||
| 1745 | if (!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1746 | if (fullscreen == SDL_FULLSCREEN_OP_UPDATE) { | ||
| 1747 | // Request was out of date; set -1 to signal the video core to undo a mode switch. | ||
| 1748 | return SDL_FULLSCREEN_FAILED; | ||
| 1749 | } else if (fullscreen == SDL_FULLSCREEN_OP_LEAVE) { | ||
| 1750 | // Nothing to do. | ||
| 1751 | return SDL_FULLSCREEN_SUCCEEDED; | ||
| 1752 | } | ||
| 1753 | } | ||
| 1754 | |||
| 1755 | if (fullscreen && !(window->flags & SDL_WINDOW_RESIZABLE)) { | ||
| 1756 | /* Compiz refuses fullscreen toggle if we're not resizable, so update the hints so we | ||
| 1757 | can be resized to the fullscreen resolution (or reset so we're not resizable again) */ | ||
| 1758 | XSizeHints *sizehints = X11_XAllocSizeHints(); | ||
| 1759 | long flags = 0; | ||
| 1760 | X11_XGetWMNormalHints(display, data->xwindow, sizehints, &flags); | ||
| 1761 | // we are going fullscreen so turn the flags off | ||
| 1762 | sizehints->flags &= ~(PMinSize | PMaxSize | PAspect); | ||
| 1763 | X11_XSetWMNormalHints(display, data->xwindow, sizehints); | ||
| 1764 | X11_XFree(sizehints); | ||
| 1765 | } | ||
| 1766 | |||
| 1767 | SDL_zero(e); | ||
| 1768 | e.xany.type = ClientMessage; | ||
| 1769 | e.xclient.message_type = _NET_WM_STATE; | ||
| 1770 | e.xclient.format = 32; | ||
| 1771 | e.xclient.window = data->xwindow; | ||
| 1772 | e.xclient.data.l[0] = | ||
| 1773 | fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; | ||
| 1774 | e.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; | ||
| 1775 | e.xclient.data.l[3] = 0l; | ||
| 1776 | |||
| 1777 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1778 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1779 | |||
| 1780 | if (!!(window->flags & SDL_WINDOW_FULLSCREEN) != fullscreen) { | ||
| 1781 | data->pending_operation |= X11_PENDING_OP_FULLSCREEN; | ||
| 1782 | } | ||
| 1783 | |||
| 1784 | // Set the position so the window will be on the target display | ||
| 1785 | if (fullscreen) { | ||
| 1786 | SDL_DisplayID current = SDL_GetDisplayForWindowPosition(window); | ||
| 1787 | SDL_copyp(&data->requested_fullscreen_mode, &window->current_fullscreen_mode); | ||
| 1788 | if (fullscreen != !!(window->flags & SDL_WINDOW_FULLSCREEN)) { | ||
| 1789 | data->window_was_maximized = !!(window->flags & SDL_WINDOW_MAXIMIZED); | ||
| 1790 | } | ||
| 1791 | data->expected.x = displaydata->x; | ||
| 1792 | data->expected.y = displaydata->y; | ||
| 1793 | data->expected.w = _display->current_mode->w; | ||
| 1794 | data->expected.h = _display->current_mode->h; | ||
| 1795 | |||
| 1796 | // Only move the window if it isn't fullscreen or already on the target display. | ||
| 1797 | if (!(window->flags & SDL_WINDOW_FULLSCREEN) || (!current || current != _display->id)) { | ||
| 1798 | X11_XMoveWindow(display, data->xwindow, displaydata->x, displaydata->y); | ||
| 1799 | data->pending_operation |= X11_PENDING_OP_MOVE; | ||
| 1800 | } | ||
| 1801 | } else { | ||
| 1802 | SDL_zero(data->requested_fullscreen_mode); | ||
| 1803 | |||
| 1804 | /* Fullscreen windows sometimes end up being marked maximized by | ||
| 1805 | * window managers. Force it back to how we expect it to be. | ||
| 1806 | */ | ||
| 1807 | SDL_zero(e); | ||
| 1808 | e.xany.type = ClientMessage; | ||
| 1809 | e.xclient.message_type = _NET_WM_STATE; | ||
| 1810 | e.xclient.format = 32; | ||
| 1811 | e.xclient.window = data->xwindow; | ||
| 1812 | if (data->window_was_maximized) { | ||
| 1813 | e.xclient.data.l[0] = _NET_WM_STATE_ADD; | ||
| 1814 | } else { | ||
| 1815 | e.xclient.data.l[0] = _NET_WM_STATE_REMOVE; | ||
| 1816 | } | ||
| 1817 | e.xclient.data.l[1] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_VERT; | ||
| 1818 | e.xclient.data.l[2] = data->videodata->atoms._NET_WM_STATE_MAXIMIZED_HORZ; | ||
| 1819 | e.xclient.data.l[3] = 0l; | ||
| 1820 | X11_XSendEvent(display, RootWindow(display, displaydata->screen), 0, | ||
| 1821 | SubstructureNotifyMask | SubstructureRedirectMask, &e); | ||
| 1822 | } | ||
| 1823 | } else { | ||
| 1824 | SDL_WindowFlags flags; | ||
| 1825 | |||
| 1826 | flags = window->flags; | ||
| 1827 | if (fullscreen) { | ||
| 1828 | flags |= SDL_WINDOW_FULLSCREEN; | ||
| 1829 | } else { | ||
| 1830 | flags &= ~SDL_WINDOW_FULLSCREEN; | ||
| 1831 | } | ||
| 1832 | X11_SetNetWMState(_this, data->xwindow, flags); | ||
| 1833 | } | ||
| 1834 | |||
| 1835 | if (data->visual->class == DirectColor) { | ||
| 1836 | if (fullscreen) { | ||
| 1837 | X11_XInstallColormap(display, data->colormap); | ||
| 1838 | } else { | ||
| 1839 | X11_XUninstallColormap(display, data->colormap); | ||
| 1840 | } | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | return SDL_FULLSCREEN_PENDING; | ||
| 1844 | } | ||
| 1845 | |||
| 1846 | SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *_display, SDL_FullscreenOp fullscreen) | ||
| 1847 | { | ||
| 1848 | return X11_SetWindowFullscreenViaWM(_this, window, _display, fullscreen); | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | typedef struct | ||
| 1852 | { | ||
| 1853 | unsigned char *data; | ||
| 1854 | int format, count; | ||
| 1855 | Atom type; | ||
| 1856 | } SDL_x11Prop; | ||
| 1857 | |||
| 1858 | /* Reads property | ||
| 1859 | Must call X11_XFree on results | ||
| 1860 | */ | ||
| 1861 | static void X11_ReadProperty(SDL_x11Prop *p, Display *disp, Window w, Atom prop) | ||
| 1862 | { | ||
| 1863 | unsigned char *ret = NULL; | ||
| 1864 | Atom type; | ||
| 1865 | int fmt; | ||
| 1866 | unsigned long count; | ||
| 1867 | unsigned long bytes_left; | ||
| 1868 | int bytes_fetch = 0; | ||
| 1869 | |||
| 1870 | do { | ||
| 1871 | if (ret) { | ||
| 1872 | X11_XFree(ret); | ||
| 1873 | } | ||
| 1874 | X11_XGetWindowProperty(disp, w, prop, 0, bytes_fetch, False, AnyPropertyType, &type, &fmt, &count, &bytes_left, &ret); | ||
| 1875 | bytes_fetch += bytes_left; | ||
| 1876 | } while (bytes_left != 0); | ||
| 1877 | |||
| 1878 | p->data = ret; | ||
| 1879 | p->format = fmt; | ||
| 1880 | p->count = count; | ||
| 1881 | p->type = type; | ||
| 1882 | } | ||
| 1883 | |||
| 1884 | void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size) | ||
| 1885 | { | ||
| 1886 | SDL_WindowData *data = window->internal; | ||
| 1887 | Display *display = data->videodata->display; | ||
| 1888 | XWindowAttributes attributes; | ||
| 1889 | Atom icc_profile_atom; | ||
| 1890 | char icc_atom_string[sizeof("_ICC_PROFILE_") + 12]; | ||
| 1891 | void *ret_icc_profile_data = NULL; | ||
| 1892 | CARD8 *icc_profile_data; | ||
| 1893 | int real_format; | ||
| 1894 | unsigned long real_nitems; | ||
| 1895 | SDL_x11Prop atomProp; | ||
| 1896 | |||
| 1897 | X11_XGetWindowAttributes(display, data->xwindow, &attributes); | ||
| 1898 | if (X11_XScreenNumberOfScreen(attributes.screen) > 0) { | ||
| 1899 | (void)SDL_snprintf(icc_atom_string, sizeof("_ICC_PROFILE_") + 12, "%s%d", "_ICC_PROFILE_", X11_XScreenNumberOfScreen(attributes.screen)); | ||
| 1900 | } else { | ||
| 1901 | SDL_strlcpy(icc_atom_string, "_ICC_PROFILE", sizeof("_ICC_PROFILE")); | ||
| 1902 | } | ||
| 1903 | X11_XGetWindowAttributes(display, RootWindowOfScreen(attributes.screen), &attributes); | ||
| 1904 | |||
| 1905 | icc_profile_atom = X11_XInternAtom(display, icc_atom_string, True); | ||
| 1906 | if (icc_profile_atom == None) { | ||
| 1907 | SDL_SetError("Screen is not calibrated."); | ||
| 1908 | return NULL; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | X11_ReadProperty(&atomProp, display, RootWindowOfScreen(attributes.screen), icc_profile_atom); | ||
| 1912 | real_format = atomProp.format; | ||
| 1913 | real_nitems = atomProp.count; | ||
| 1914 | icc_profile_data = atomProp.data; | ||
| 1915 | if (real_format == None) { | ||
| 1916 | SDL_SetError("Screen is not calibrated."); | ||
| 1917 | return NULL; | ||
| 1918 | } | ||
| 1919 | |||
| 1920 | ret_icc_profile_data = SDL_malloc(real_nitems); | ||
| 1921 | if (!ret_icc_profile_data) { | ||
| 1922 | return NULL; | ||
| 1923 | } | ||
| 1924 | |||
| 1925 | SDL_memcpy(ret_icc_profile_data, icc_profile_data, real_nitems); | ||
| 1926 | *size = real_nitems; | ||
| 1927 | X11_XFree(icc_profile_data); | ||
| 1928 | |||
| 1929 | return ret_icc_profile_data; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) | ||
| 1933 | { | ||
| 1934 | SDL_WindowData *data = window->internal; | ||
| 1935 | Display *display; | ||
| 1936 | |||
| 1937 | if (!data) { | ||
| 1938 | return SDL_SetError("Invalid window data"); | ||
| 1939 | } | ||
| 1940 | data->mouse_grabbed = false; | ||
| 1941 | |||
| 1942 | display = data->videodata->display; | ||
| 1943 | |||
| 1944 | if (grabbed) { | ||
| 1945 | /* If the window is unmapped, XGrab calls return GrabNotViewable, | ||
| 1946 | so when we get a MapNotify later, we'll try to update the grab as | ||
| 1947 | appropriate. */ | ||
| 1948 | if (window->flags & SDL_WINDOW_HIDDEN) { | ||
| 1949 | return true; | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | /* If XInput2 is enabled, it will grab the pointer on button presses, | ||
| 1953 | * which results in XGrabPointer returning AlreadyGrabbed. If buttons | ||
| 1954 | * are currently pressed, clear any existing grabs before attempting | ||
| 1955 | * the confinement grab. | ||
| 1956 | */ | ||
| 1957 | if (data->xinput2_mouse_enabled && SDL_GetMouseState(NULL, NULL)) { | ||
| 1958 | X11_XUngrabPointer(display, CurrentTime); | ||
| 1959 | } | ||
| 1960 | |||
| 1961 | // Try to grab the mouse | ||
| 1962 | if (!data->videodata->broken_pointer_grab) { | ||
| 1963 | const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask; | ||
| 1964 | int attempts; | ||
| 1965 | int result = 0; | ||
| 1966 | |||
| 1967 | // Try for up to 5000ms (5s) to grab. If it still fails, stop trying. | ||
| 1968 | for (attempts = 0; attempts < 100; attempts++) { | ||
| 1969 | result = X11_XGrabPointer(display, data->xwindow, False, mask, GrabModeAsync, | ||
| 1970 | GrabModeAsync, data->xwindow, None, CurrentTime); | ||
| 1971 | if (result == GrabSuccess) { | ||
| 1972 | data->mouse_grabbed = true; | ||
| 1973 | break; | ||
| 1974 | } | ||
| 1975 | SDL_Delay(50); | ||
| 1976 | } | ||
| 1977 | |||
| 1978 | if (result != GrabSuccess) { | ||
| 1979 | data->videodata->broken_pointer_grab = true; // don't try again. | ||
| 1980 | } | ||
| 1981 | } | ||
| 1982 | |||
| 1983 | X11_Xinput2GrabTouch(_this, window); | ||
| 1984 | |||
| 1985 | // Raise the window if we grab the mouse | ||
| 1986 | X11_XRaiseWindow(display, data->xwindow); | ||
| 1987 | } else { | ||
| 1988 | X11_XUngrabPointer(display, CurrentTime); | ||
| 1989 | |||
| 1990 | X11_Xinput2UngrabTouch(_this, window); | ||
| 1991 | } | ||
| 1992 | X11_XSync(display, False); | ||
| 1993 | |||
| 1994 | if (!data->videodata->broken_pointer_grab) { | ||
| 1995 | return true; | ||
| 1996 | } else { | ||
| 1997 | return SDL_SetError("The X server refused to let us grab the mouse. You might experience input bugs."); | ||
| 1998 | } | ||
| 1999 | } | ||
| 2000 | |||
| 2001 | bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed) | ||
| 2002 | { | ||
| 2003 | SDL_WindowData *data = window->internal; | ||
| 2004 | Display *display; | ||
| 2005 | |||
| 2006 | if (!data) { | ||
| 2007 | return SDL_SetError("Invalid window data"); | ||
| 2008 | } | ||
| 2009 | |||
| 2010 | display = data->videodata->display; | ||
| 2011 | |||
| 2012 | if (grabbed) { | ||
| 2013 | /* If the window is unmapped, XGrab calls return GrabNotViewable, | ||
| 2014 | so when we get a MapNotify later, we'll try to update the grab as | ||
| 2015 | appropriate. */ | ||
| 2016 | if (window->flags & SDL_WINDOW_HIDDEN) { | ||
| 2017 | return true; | ||
| 2018 | } | ||
| 2019 | |||
| 2020 | X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync, | ||
| 2021 | GrabModeAsync, CurrentTime); | ||
| 2022 | } else { | ||
| 2023 | X11_XUngrabKeyboard(display, CurrentTime); | ||
| 2024 | } | ||
| 2025 | X11_XSync(display, False); | ||
| 2026 | |||
| 2027 | return true; | ||
| 2028 | } | ||
| 2029 | |||
| 2030 | void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 2031 | { | ||
| 2032 | SDL_WindowData *data = window->internal; | ||
| 2033 | |||
| 2034 | if (data) { | ||
| 2035 | SDL_VideoData *videodata = data->videodata; | ||
| 2036 | Display *display = videodata->display; | ||
| 2037 | int numwindows = videodata->numwindows; | ||
| 2038 | SDL_WindowData **windowlist = videodata->windowlist; | ||
| 2039 | int i; | ||
| 2040 | |||
| 2041 | if (windowlist) { | ||
| 2042 | for (i = 0; i < numwindows; ++i) { | ||
| 2043 | if (windowlist[i] && (windowlist[i]->window == window)) { | ||
| 2044 | windowlist[i] = windowlist[numwindows - 1]; | ||
| 2045 | windowlist[numwindows - 1] = NULL; | ||
| 2046 | videodata->numwindows--; | ||
| 2047 | break; | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | } | ||
| 2051 | #ifdef X_HAVE_UTF8_STRING | ||
| 2052 | if (data->ic) { | ||
| 2053 | X11_XDestroyIC(data->ic); | ||
| 2054 | SDL_free(data->preedit_text); | ||
| 2055 | SDL_free(data->preedit_feedback); | ||
| 2056 | } | ||
| 2057 | #endif | ||
| 2058 | |||
| 2059 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 2060 | X11_TermResizeSync(window); | ||
| 2061 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 2062 | |||
| 2063 | if (!(window->flags & SDL_WINDOW_EXTERNAL)) { | ||
| 2064 | X11_XDestroyWindow(display, data->xwindow); | ||
| 2065 | X11_XFlush(display); | ||
| 2066 | } | ||
| 2067 | SDL_free(data); | ||
| 2068 | |||
| 2069 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 2070 | // If the pointer barriers are active for this, deactivate it. | ||
| 2071 | if (videodata->active_cursor_confined_window == window) { | ||
| 2072 | X11_DestroyPointerBarrier(_this, window); | ||
| 2073 | } | ||
| 2074 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 2075 | } | ||
| 2076 | window->internal = NULL; | ||
| 2077 | } | ||
| 2078 | |||
| 2079 | bool X11_SetWindowHitTest(SDL_Window *window, bool enabled) | ||
| 2080 | { | ||
| 2081 | return true; // just succeed, the real work is done elsewhere. | ||
| 2082 | } | ||
| 2083 | |||
| 2084 | void X11_AcceptDragAndDrop(SDL_Window *window, bool accept) | ||
| 2085 | { | ||
| 2086 | SDL_WindowData *data = window->internal; | ||
| 2087 | Display *display = data->videodata->display; | ||
| 2088 | Atom XdndAware = data->videodata->atoms.XdndAware; | ||
| 2089 | |||
| 2090 | if (accept) { | ||
| 2091 | Atom xdnd_version = 5; | ||
| 2092 | X11_XChangeProperty(display, data->xwindow, XdndAware, XA_ATOM, 32, | ||
| 2093 | PropModeReplace, (unsigned char *)&xdnd_version, 1); | ||
| 2094 | } else { | ||
| 2095 | X11_XDeleteProperty(display, data->xwindow, XdndAware); | ||
| 2096 | } | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation) | ||
| 2100 | { | ||
| 2101 | SDL_WindowData *data = window->internal; | ||
| 2102 | Display *display = data->videodata->display; | ||
| 2103 | XWMHints *wmhints; | ||
| 2104 | |||
| 2105 | wmhints = X11_XGetWMHints(display, data->xwindow); | ||
| 2106 | if (!wmhints) { | ||
| 2107 | return SDL_SetError("Couldn't get WM hints"); | ||
| 2108 | } | ||
| 2109 | |||
| 2110 | wmhints->flags &= ~XUrgencyHint; | ||
| 2111 | data->flashing_window = false; | ||
| 2112 | data->flash_cancel_time = 0; | ||
| 2113 | |||
| 2114 | switch (operation) { | ||
| 2115 | case SDL_FLASH_CANCEL: | ||
| 2116 | // Taken care of above | ||
| 2117 | break; | ||
| 2118 | case SDL_FLASH_BRIEFLY: | ||
| 2119 | if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) { | ||
| 2120 | wmhints->flags |= XUrgencyHint; | ||
| 2121 | data->flashing_window = true; | ||
| 2122 | // On Ubuntu 21.04 this causes a dialog to pop up, so leave it up for a full second so users can see it | ||
| 2123 | data->flash_cancel_time = SDL_GetTicks() + 1000; | ||
| 2124 | } | ||
| 2125 | break; | ||
| 2126 | case SDL_FLASH_UNTIL_FOCUSED: | ||
| 2127 | if (!(window->flags & SDL_WINDOW_INPUT_FOCUS)) { | ||
| 2128 | wmhints->flags |= XUrgencyHint; | ||
| 2129 | data->flashing_window = true; | ||
| 2130 | } | ||
| 2131 | break; | ||
| 2132 | default: | ||
| 2133 | break; | ||
| 2134 | } | ||
| 2135 | |||
| 2136 | X11_XSetWMHints(display, data->xwindow, wmhints); | ||
| 2137 | X11_XFree(wmhints); | ||
| 2138 | return true; | ||
| 2139 | } | ||
| 2140 | |||
| 2141 | bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title) | ||
| 2142 | { | ||
| 2143 | Atom _NET_WM_NAME = X11_XInternAtom(display, "_NET_WM_NAME", False); | ||
| 2144 | XTextProperty titleprop; | ||
| 2145 | int conv = X11_XmbTextListToTextProperty(display, &title, 1, XTextStyle, &titleprop); | ||
| 2146 | Status status; | ||
| 2147 | |||
| 2148 | if (X11_XSupportsLocale() != True) { | ||
| 2149 | return SDL_SetError("Current locale not supported by X server, cannot continue."); | ||
| 2150 | } | ||
| 2151 | |||
| 2152 | if (conv == 0) { | ||
| 2153 | X11_XSetTextProperty(display, xwindow, &titleprop, XA_WM_NAME); | ||
| 2154 | X11_XFree(titleprop.value); | ||
| 2155 | // we know this can't be a locale error as we checked X locale validity | ||
| 2156 | } else if (conv < 0) { | ||
| 2157 | return SDL_OutOfMemory(); | ||
| 2158 | } else { // conv > 0 | ||
| 2159 | SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "%d characters were not convertible to the current locale!", conv); | ||
| 2160 | return true; | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | #ifdef X_HAVE_UTF8_STRING | ||
| 2164 | status = X11_Xutf8TextListToTextProperty(display, &title, 1, XUTF8StringStyle, &titleprop); | ||
| 2165 | if (status == Success) { | ||
| 2166 | X11_XSetTextProperty(display, xwindow, &titleprop, _NET_WM_NAME); | ||
| 2167 | X11_XFree(titleprop.value); | ||
| 2168 | } else { | ||
| 2169 | return SDL_SetError("Failed to convert title to UTF8! Bad encoding, or bad Xorg encoding? Window title: «%s»", title); | ||
| 2170 | } | ||
| 2171 | #endif | ||
| 2172 | |||
| 2173 | X11_XFlush(display); | ||
| 2174 | return true; | ||
| 2175 | } | ||
| 2176 | |||
| 2177 | void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y) | ||
| 2178 | { | ||
| 2179 | SDL_WindowData *data = window->internal; | ||
| 2180 | SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(window); | ||
| 2181 | Display *display = data->videodata->display; | ||
| 2182 | Window root = RootWindow(display, displaydata->screen); | ||
| 2183 | XClientMessageEvent e; | ||
| 2184 | Window childReturn; | ||
| 2185 | int wx, wy; | ||
| 2186 | |||
| 2187 | SDL_zero(e); | ||
| 2188 | X11_XTranslateCoordinates(display, data->xwindow, root, x, y, &wx, &wy, &childReturn); | ||
| 2189 | |||
| 2190 | e.type = ClientMessage; | ||
| 2191 | e.window = data->xwindow; | ||
| 2192 | e.message_type = X11_XInternAtom(display, "_GTK_SHOW_WINDOW_MENU", 0); | ||
| 2193 | e.data.l[0] = 0; // GTK device ID (unused) | ||
| 2194 | e.data.l[1] = wx; // X coordinate relative to root | ||
| 2195 | e.data.l[2] = wy; // Y coordinate relative to root | ||
| 2196 | e.format = 32; | ||
| 2197 | |||
| 2198 | X11_XSendEvent(display, root, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&e); | ||
| 2199 | X11_XFlush(display); | ||
| 2200 | } | ||
| 2201 | |||
| 2202 | bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 2203 | { | ||
| 2204 | const Uint64 current_time = SDL_GetTicksNS(); | ||
| 2205 | Uint64 timeout = 0; | ||
| 2206 | |||
| 2207 | // Allow time for any pending mode switches to complete. | ||
| 2208 | for (int i = 0; i < _this->num_displays; ++i) { | ||
| 2209 | if (_this->displays[i]->internal->mode_switch_deadline_ns && | ||
| 2210 | current_time < _this->displays[i]->internal->mode_switch_deadline_ns) { | ||
| 2211 | timeout = SDL_max(_this->displays[i]->internal->mode_switch_deadline_ns - current_time, timeout); | ||
| 2212 | } | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | /* 100ms is fine for most cases, but, for some reason, maximizing | ||
| 2216 | * a window can take a very long time. | ||
| 2217 | */ | ||
| 2218 | timeout += window->internal->pending_operation & X11_PENDING_OP_MAXIMIZE ? SDL_MS_TO_NS(1000) : SDL_MS_TO_NS(100); | ||
| 2219 | |||
| 2220 | return X11_SyncWindowTimeout(_this, window, timeout); | ||
| 2221 | } | ||
| 2222 | |||
| 2223 | bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable) | ||
| 2224 | { | ||
| 2225 | SDL_WindowData *data = window->internal; | ||
| 2226 | Display *display = data->videodata->display; | ||
| 2227 | XWMHints *wmhints; | ||
| 2228 | |||
| 2229 | wmhints = X11_XGetWMHints(display, data->xwindow); | ||
| 2230 | if (!wmhints) { | ||
| 2231 | return SDL_SetError("Couldn't get WM hints"); | ||
| 2232 | } | ||
| 2233 | |||
| 2234 | wmhints->input = focusable ? True : False; | ||
| 2235 | wmhints->flags |= InputHint; | ||
| 2236 | |||
| 2237 | X11_XSetWMHints(display, data->xwindow, wmhints); | ||
| 2238 | X11_XFree(wmhints); | ||
| 2239 | |||
| 2240 | return true; | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h new file mode 100644 index 0000000..f1a73ab --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11window.h | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11window_h_ | ||
| 24 | #define SDL_x11window_h_ | ||
| 25 | |||
| 26 | /* We need to queue the focus in/out changes because they may occur during | ||
| 27 | video mode changes and we can respond to them by triggering more mode | ||
| 28 | changes. | ||
| 29 | */ | ||
| 30 | #define PENDING_FOCUS_TIME 200 | ||
| 31 | |||
| 32 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 33 | #include <EGL/egl.h> | ||
| 34 | #endif | ||
| 35 | |||
| 36 | typedef enum | ||
| 37 | { | ||
| 38 | PENDING_FOCUS_NONE, | ||
| 39 | PENDING_FOCUS_IN, | ||
| 40 | PENDING_FOCUS_OUT | ||
| 41 | } PendingFocusEnum; | ||
| 42 | |||
| 43 | struct SDL_WindowData | ||
| 44 | { | ||
| 45 | SDL_Window *window; | ||
| 46 | Window xwindow; | ||
| 47 | Visual *visual; | ||
| 48 | Colormap colormap; | ||
| 49 | #ifndef NO_SHARED_MEMORY | ||
| 50 | // MIT shared memory extension information | ||
| 51 | bool use_mitshm; | ||
| 52 | XShmSegmentInfo shminfo; | ||
| 53 | #endif | ||
| 54 | XImage *ximage; | ||
| 55 | GC gc; | ||
| 56 | XIC ic; | ||
| 57 | bool created; | ||
| 58 | int border_left; | ||
| 59 | int border_right; | ||
| 60 | int border_top; | ||
| 61 | int border_bottom; | ||
| 62 | bool xinput2_mouse_enabled; | ||
| 63 | bool xinput2_keyboard_enabled; | ||
| 64 | bool mouse_grabbed; | ||
| 65 | Uint64 last_focus_event_time; | ||
| 66 | PendingFocusEnum pending_focus; | ||
| 67 | Uint64 pending_focus_time; | ||
| 68 | bool pending_move; | ||
| 69 | SDL_Point pending_move_point; | ||
| 70 | XConfigureEvent last_xconfigure; | ||
| 71 | struct SDL_VideoData *videodata; | ||
| 72 | unsigned long user_time; | ||
| 73 | Atom xdnd_req; | ||
| 74 | Window xdnd_source; | ||
| 75 | bool flashing_window; | ||
| 76 | Uint64 flash_cancel_time; | ||
| 77 | SDL_Window *keyboard_focus; | ||
| 78 | #ifdef SDL_VIDEO_OPENGL_EGL | ||
| 79 | EGLSurface egl_surface; | ||
| 80 | #endif | ||
| 81 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 82 | bool pointer_barrier_active; | ||
| 83 | PointerBarrier barrier[4]; | ||
| 84 | SDL_Rect barrier_rect; | ||
| 85 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 86 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 87 | XSyncCounter resize_counter; | ||
| 88 | XSyncValue resize_id; | ||
| 89 | bool resize_in_progress; | ||
| 90 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 91 | |||
| 92 | SDL_Rect expected; | ||
| 93 | SDL_DisplayMode requested_fullscreen_mode; | ||
| 94 | |||
| 95 | enum | ||
| 96 | { | ||
| 97 | X11_PENDING_OP_NONE = 0x00, | ||
| 98 | X11_PENDING_OP_RESTORE = 0x01, | ||
| 99 | X11_PENDING_OP_MINIMIZE = 0x02, | ||
| 100 | X11_PENDING_OP_MAXIMIZE = 0x04, | ||
| 101 | X11_PENDING_OP_FULLSCREEN = 0x08, | ||
| 102 | X11_PENDING_OP_MOVE = 0x10, | ||
| 103 | X11_PENDING_OP_RESIZE = 0x20 | ||
| 104 | } pending_operation; | ||
| 105 | |||
| 106 | enum | ||
| 107 | { | ||
| 108 | X11_SIZE_MOVE_EVENTS_DISABLE = 0x01, // Events are completely disabled. | ||
| 109 | X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS = 0x02, // Events are disabled until a _NET_FRAME_EXTENTS event arrives. | ||
| 110 | } size_move_event_flags; | ||
| 111 | |||
| 112 | bool pending_size; | ||
| 113 | bool pending_position; | ||
| 114 | bool window_was_maximized; | ||
| 115 | bool previous_borders_nonzero; | ||
| 116 | bool toggle_borders; | ||
| 117 | bool fullscreen_borders_forced_on; | ||
| 118 | SDL_HitTestResult hit_test_result; | ||
| 119 | |||
| 120 | XPoint xim_spot; | ||
| 121 | char *preedit_text; | ||
| 122 | XIMFeedback *preedit_feedback; | ||
| 123 | int preedit_length; | ||
| 124 | int preedit_cursor; | ||
| 125 | bool ime_needs_clear_composition; | ||
| 126 | }; | ||
| 127 | |||
| 128 | extern void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags); | ||
| 129 | extern Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow); | ||
| 130 | |||
| 131 | extern bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props); | ||
| 132 | extern char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow); | ||
| 133 | extern void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 134 | extern bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon); | ||
| 135 | extern bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 136 | extern void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 137 | extern void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 138 | extern void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 139 | extern bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right); | ||
| 140 | extern bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity); | ||
| 141 | extern bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent); | ||
| 142 | extern bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal); | ||
| 143 | extern void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 144 | extern void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 145 | extern void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 146 | extern void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 147 | extern void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 148 | extern void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 149 | extern void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 150 | extern void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered); | ||
| 151 | extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable); | ||
| 152 | extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top); | ||
| 153 | extern SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen); | ||
| 154 | extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); | ||
| 155 | extern bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed); | ||
| 156 | extern bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed); | ||
| 157 | extern void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 158 | extern bool X11_SetWindowHitTest(SDL_Window *window, bool enabled); | ||
| 159 | extern void X11_AcceptDragAndDrop(SDL_Window *window, bool accept); | ||
| 160 | extern bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); | ||
| 161 | extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y); | ||
| 162 | extern bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 163 | extern bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable); | ||
| 164 | |||
| 165 | extern bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title); | ||
| 166 | extern void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position); | ||
| 167 | extern void X11_SetWindowMinMax(SDL_Window *window, bool use_current); | ||
| 168 | |||
| 169 | #endif // SDL_x11window_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c new file mode 100644 index 0000000..517ebc8 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.c | |||
| @@ -0,0 +1,214 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XFIXES) | ||
| 25 | |||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11xfixes.h" | ||
| 28 | #include "../../events/SDL_mouse_c.h" | ||
| 29 | #include "../../events/SDL_touch_c.h" | ||
| 30 | |||
| 31 | static bool xfixes_initialized = true; | ||
| 32 | static int xfixes_selection_notify_event = 0; | ||
| 33 | |||
| 34 | static int query_xfixes_version(Display *display, int major, int minor) | ||
| 35 | { | ||
| 36 | // We don't care if this fails, so long as it sets major/minor on it's way out the door. | ||
| 37 | X11_XFixesQueryVersion(display, &major, &minor); | ||
| 38 | return (major * 1000) + minor; | ||
| 39 | } | ||
| 40 | |||
| 41 | static bool xfixes_version_atleast(const int version, const int wantmajor, const int wantminor) | ||
| 42 | { | ||
| 43 | return version >= ((wantmajor * 1000) + wantminor); | ||
| 44 | } | ||
| 45 | |||
| 46 | void X11_InitXfixes(SDL_VideoDevice *_this) | ||
| 47 | { | ||
| 48 | SDL_VideoData *data = _this->internal; | ||
| 49 | |||
| 50 | int version = 0; | ||
| 51 | int event, error; | ||
| 52 | int fixes_opcode; | ||
| 53 | |||
| 54 | Atom XA_CLIPBOARD = data->atoms.CLIPBOARD; | ||
| 55 | |||
| 56 | if (!SDL_X11_HAVE_XFIXES || | ||
| 57 | !X11_XQueryExtension(data->display, "XFIXES", &fixes_opcode, &event, &error)) { | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | |||
| 61 | // Selection tracking is available in all versions of XFixes | ||
| 62 | xfixes_selection_notify_event = event + XFixesSelectionNotify; | ||
| 63 | X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display), | ||
| 64 | XA_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask); | ||
| 65 | X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display), | ||
| 66 | XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask); | ||
| 67 | |||
| 68 | // We need at least 5.0 for barriers. | ||
| 69 | version = query_xfixes_version(data->display, 5, 0); | ||
| 70 | if (!xfixes_version_atleast(version, 5, 0)) { | ||
| 71 | return; // X server does not support the version we want at all. | ||
| 72 | } | ||
| 73 | |||
| 74 | xfixes_initialized = 1; | ||
| 75 | } | ||
| 76 | |||
| 77 | bool X11_XfixesIsInitialized(void) | ||
| 78 | { | ||
| 79 | return xfixes_initialized; | ||
| 80 | } | ||
| 81 | |||
| 82 | int X11_GetXFixesSelectionNotifyEvent(void) | ||
| 83 | { | ||
| 84 | return xfixes_selection_notify_event; | ||
| 85 | } | ||
| 86 | |||
| 87 | bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 88 | { | ||
| 89 | if (SDL_RectEmpty(&window->mouse_rect)) { | ||
| 90 | X11_ConfineCursorWithFlags(_this, window, NULL, 0); | ||
| 91 | } else { | ||
| 92 | if (window->flags & SDL_WINDOW_INPUT_FOCUS) { | ||
| 93 | X11_ConfineCursorWithFlags(_this, window, &window->mouse_rect, 0); | ||
| 94 | } else { | ||
| 95 | // Save the state for when we get focus again | ||
| 96 | SDL_WindowData *wdata = window->internal; | ||
| 97 | |||
| 98 | SDL_memcpy(&wdata->barrier_rect, &window->mouse_rect, sizeof(wdata->barrier_rect)); | ||
| 99 | |||
| 100 | wdata->pointer_barrier_active = true; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | return true; | ||
| 105 | } | ||
| 106 | |||
| 107 | bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags) | ||
| 108 | { | ||
| 109 | /* Yaakuro: For some reason Xfixes when confining inside a rect where the | ||
| 110 | * edges exactly match, a rectangle the cursor 'slips' out of the barrier. | ||
| 111 | * To prevent that the lines for the barriers will span the whole screen. | ||
| 112 | */ | ||
| 113 | SDL_VideoData *data = _this->internal; | ||
| 114 | SDL_WindowData *wdata; | ||
| 115 | |||
| 116 | if (!X11_XfixesIsInitialized()) { | ||
| 117 | return SDL_Unsupported(); | ||
| 118 | } | ||
| 119 | |||
| 120 | // If there is already a set of barriers active, disable them. | ||
| 121 | if (data->active_cursor_confined_window) { | ||
| 122 | X11_DestroyPointerBarrier(_this, data->active_cursor_confined_window); | ||
| 123 | } | ||
| 124 | |||
| 125 | SDL_assert(window != NULL); | ||
| 126 | wdata = window->internal; | ||
| 127 | |||
| 128 | /* If user did not specify an area to confine, destroy the barrier that was/is assigned to | ||
| 129 | * this window it was assigned */ | ||
| 130 | if (rect) { | ||
| 131 | int x1, y1, x2, y2; | ||
| 132 | SDL_Rect bounds; | ||
| 133 | SDL_GetWindowPosition(window, &bounds.x, &bounds.y); | ||
| 134 | SDL_GetWindowSize(window, &bounds.w, &bounds.h); | ||
| 135 | |||
| 136 | /** Negative values are not allowed. Clip values relative to the specified window. */ | ||
| 137 | x1 = bounds.x + SDL_max(rect->x, 0); | ||
| 138 | y1 = bounds.y + SDL_max(rect->y, 0); | ||
| 139 | x2 = SDL_min(bounds.x + rect->x + rect->w, bounds.x + bounds.w); | ||
| 140 | y2 = SDL_min(bounds.y + rect->y + rect->h, bounds.y + bounds.h); | ||
| 141 | |||
| 142 | if ((wdata->barrier_rect.x != rect->x) || | ||
| 143 | (wdata->barrier_rect.y != rect->y) || | ||
| 144 | (wdata->barrier_rect.w != rect->w) || | ||
| 145 | (wdata->barrier_rect.h != rect->h)) { | ||
| 146 | wdata->barrier_rect = *rect; | ||
| 147 | } | ||
| 148 | |||
| 149 | // Use the display bounds to ensure the barriers don't have corner gaps | ||
| 150 | SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &bounds); | ||
| 151 | |||
| 152 | /** Create the left barrier */ | ||
| 153 | wdata->barrier[0] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow, | ||
| 154 | x1, bounds.y, | ||
| 155 | x1, bounds.y + bounds.h, | ||
| 156 | BarrierPositiveX, | ||
| 157 | 0, NULL); | ||
| 158 | /** Create the right barrier */ | ||
| 159 | wdata->barrier[1] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow, | ||
| 160 | x2, bounds.y, | ||
| 161 | x2, bounds.y + bounds.h, | ||
| 162 | BarrierNegativeX, | ||
| 163 | 0, NULL); | ||
| 164 | /** Create the top barrier */ | ||
| 165 | wdata->barrier[2] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow, | ||
| 166 | bounds.x, y1, | ||
| 167 | bounds.x + bounds.w, y1, | ||
| 168 | BarrierPositiveY, | ||
| 169 | 0, NULL); | ||
| 170 | /** Create the bottom barrier */ | ||
| 171 | wdata->barrier[3] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow, | ||
| 172 | bounds.x, y2, | ||
| 173 | bounds.x + bounds.w, y2, | ||
| 174 | BarrierNegativeY, | ||
| 175 | 0, NULL); | ||
| 176 | |||
| 177 | X11_XFlush(data->display); | ||
| 178 | |||
| 179 | // Lets remember current active confined window. | ||
| 180 | data->active_cursor_confined_window = window; | ||
| 181 | |||
| 182 | /* User activated the confinement for this window. We use this later to reactivate | ||
| 183 | * the confinement if it got deactivated by FocusOut or UnmapNotify */ | ||
| 184 | wdata->pointer_barrier_active = true; | ||
| 185 | } else { | ||
| 186 | X11_DestroyPointerBarrier(_this, window); | ||
| 187 | |||
| 188 | // Only set barrier inactive when user specified NULL and not handled by focus out. | ||
| 189 | if (flags != X11_BARRIER_HANDLED_BY_EVENT) { | ||
| 190 | wdata->pointer_barrier_active = false; | ||
| 191 | } | ||
| 192 | } | ||
| 193 | return true; | ||
| 194 | } | ||
| 195 | |||
| 196 | void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 197 | { | ||
| 198 | int i; | ||
| 199 | SDL_VideoData *data = _this->internal; | ||
| 200 | if (window) { | ||
| 201 | SDL_WindowData *wdata = window->internal; | ||
| 202 | |||
| 203 | for (i = 0; i < 4; i++) { | ||
| 204 | if (wdata->barrier[i] > 0) { | ||
| 205 | X11_XFixesDestroyPointerBarrier(data->display, wdata->barrier[i]); | ||
| 206 | wdata->barrier[i] = 0; | ||
| 207 | } | ||
| 208 | } | ||
| 209 | X11_XFlush(data->display); | ||
| 210 | } | ||
| 211 | data->active_cursor_confined_window = NULL; | ||
| 212 | } | ||
| 213 | |||
| 214 | #endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XFIXES | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h new file mode 100644 index 0000000..bd8e437 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xfixes.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #ifndef SDL_x11xfixes_h_ | ||
| 25 | #define SDL_x11xfixes_h_ | ||
| 26 | |||
| 27 | #ifdef SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 28 | |||
| 29 | #define X11_BARRIER_HANDLED_BY_EVENT 1 | ||
| 30 | |||
| 31 | extern void X11_InitXfixes(SDL_VideoDevice *_this); | ||
| 32 | extern bool X11_XfixesIsInitialized(void); | ||
| 33 | extern bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 34 | extern bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags); | ||
| 35 | extern void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 36 | extern int X11_GetXFixesSelectionNotifyEvent(void); | ||
| 37 | #endif // SDL_VIDEO_DRIVER_X11_XFIXES | ||
| 38 | |||
| 39 | #endif // SDL_x11xfixes_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c new file mode 100644 index 0000000..afe4a7c --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.c | |||
| @@ -0,0 +1,829 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 24 | |||
| 25 | #include "SDL_x11pen.h" | ||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11xinput2.h" | ||
| 28 | #include "../../events/SDL_events_c.h" | ||
| 29 | #include "../../events/SDL_mouse_c.h" | ||
| 30 | #include "../../events/SDL_pen_c.h" | ||
| 31 | #include "../../events/SDL_touch_c.h" | ||
| 32 | |||
| 33 | #define MAX_AXIS 16 | ||
| 34 | |||
| 35 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 36 | static bool xinput2_initialized; | ||
| 37 | |||
| 38 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 39 | static bool xinput2_multitouch_supported; | ||
| 40 | #endif | ||
| 41 | |||
| 42 | /* Opcode returned X11_XQueryExtension | ||
| 43 | * It will be used in event processing | ||
| 44 | * to know that the event came from | ||
| 45 | * this extension */ | ||
| 46 | static int xinput2_opcode; | ||
| 47 | |||
| 48 | static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len, | ||
| 49 | double *output_values, int output_values_len) | ||
| 50 | { | ||
| 51 | int i = 0, z = 0; | ||
| 52 | int top = mask_len * 8; | ||
| 53 | if (top > MAX_AXIS) { | ||
| 54 | top = MAX_AXIS; | ||
| 55 | } | ||
| 56 | |||
| 57 | SDL_memset(output_values, 0, output_values_len * sizeof(double)); | ||
| 58 | for (; i < top && z < output_values_len; i++) { | ||
| 59 | if (XIMaskIsSet(mask, i)) { | ||
| 60 | const int value = (int)*input_values; | ||
| 61 | output_values[z] = value; | ||
| 62 | input_values++; | ||
| 63 | } | ||
| 64 | z++; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | static int query_xinput2_version(Display *display, int major, int minor) | ||
| 69 | { | ||
| 70 | // We don't care if this fails, so long as it sets major/minor on it's way out the door. | ||
| 71 | X11_XIQueryVersion(display, &major, &minor); | ||
| 72 | return (major * 1000) + minor; | ||
| 73 | } | ||
| 74 | |||
| 75 | static bool xinput2_version_atleast(const int version, const int wantmajor, const int wantminor) | ||
| 76 | { | ||
| 77 | return version >= ((wantmajor * 1000) + wantminor); | ||
| 78 | } | ||
| 79 | |||
| 80 | static SDL_WindowData *xinput2_get_sdlwindowdata(SDL_VideoData *videodata, Window window) | ||
| 81 | { | ||
| 82 | int i; | ||
| 83 | for (i = 0; i < videodata->numwindows; i++) { | ||
| 84 | SDL_WindowData *d = videodata->windowlist[i]; | ||
| 85 | if (d->xwindow == window) { | ||
| 86 | return d; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | return NULL; | ||
| 90 | } | ||
| 91 | |||
| 92 | static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window) | ||
| 93 | { | ||
| 94 | const SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, window); | ||
| 95 | return windowdata ? windowdata->window : NULL; | ||
| 96 | } | ||
| 97 | |||
| 98 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 99 | static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y) | ||
| 100 | { | ||
| 101 | if (window) { | ||
| 102 | if (window->w == 1) { | ||
| 103 | *out_x = 0.5f; | ||
| 104 | } else { | ||
| 105 | *out_x = (float)in_x / (window->w - 1); | ||
| 106 | } | ||
| 107 | if (window->h == 1) { | ||
| 108 | *out_y = 0.5f; | ||
| 109 | } else { | ||
| 110 | *out_y = (float)in_y / (window->h - 1); | ||
| 111 | } | ||
| 112 | } else { | ||
| 113 | // couldn't find the window... | ||
| 114 | *out_x = (float)in_x; | ||
| 115 | *out_y = (float)in_y; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 119 | |||
| 120 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 121 | |||
| 122 | bool X11_InitXinput2(SDL_VideoDevice *_this) | ||
| 123 | { | ||
| 124 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 125 | SDL_VideoData *data = _this->internal; | ||
| 126 | |||
| 127 | int version = 0; | ||
| 128 | XIEventMask eventmask; | ||
| 129 | unsigned char mask[4] = { 0, 0, 0, 0 }; | ||
| 130 | int event, err; | ||
| 131 | |||
| 132 | /* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */ | ||
| 133 | if (!SDL_GetHintBoolean("SDL_VIDEO_X11_XINPUT2", true)) { | ||
| 134 | return false; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* | ||
| 138 | * Initialize XInput 2 | ||
| 139 | * According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better | ||
| 140 | * to inform Xserver what version of Xinput we support.The server will store the version we support. | ||
| 141 | * "As XI2 progresses it becomes important that you use this call as the server may treat the client | ||
| 142 | * differently depending on the supported version". | ||
| 143 | * | ||
| 144 | * FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault | ||
| 145 | */ | ||
| 146 | if (!SDL_X11_HAVE_XINPUT2 || | ||
| 147 | !X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) { | ||
| 148 | return false; // X server does not have XInput at all | ||
| 149 | } | ||
| 150 | |||
| 151 | // We need at least 2.2 for Multitouch, 2.0 otherwise. | ||
| 152 | version = query_xinput2_version(data->display, 2, 2); | ||
| 153 | if (!xinput2_version_atleast(version, 2, 0)) { | ||
| 154 | return false; // X server does not support the version we want at all. | ||
| 155 | } | ||
| 156 | |||
| 157 | xinput2_initialized = true; | ||
| 158 | |||
| 159 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2 | ||
| 160 | xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2); | ||
| 161 | #endif | ||
| 162 | |||
| 163 | // Enable raw motion events for this display | ||
| 164 | SDL_zero(eventmask); | ||
| 165 | SDL_zeroa(mask); | ||
| 166 | eventmask.deviceid = XIAllMasterDevices; | ||
| 167 | eventmask.mask_len = sizeof(mask); | ||
| 168 | eventmask.mask = mask; | ||
| 169 | |||
| 170 | XISetMask(mask, XI_RawMotion); | ||
| 171 | XISetMask(mask, XI_RawButtonPress); | ||
| 172 | XISetMask(mask, XI_RawButtonRelease); | ||
| 173 | |||
| 174 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 175 | // Enable raw touch events if supported | ||
| 176 | if (X11_Xinput2IsMultitouchSupported()) { | ||
| 177 | XISetMask(mask, XI_RawTouchBegin); | ||
| 178 | XISetMask(mask, XI_RawTouchUpdate); | ||
| 179 | XISetMask(mask, XI_RawTouchEnd); | ||
| 180 | } | ||
| 181 | #endif | ||
| 182 | |||
| 183 | X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1); | ||
| 184 | |||
| 185 | SDL_zero(eventmask); | ||
| 186 | SDL_zeroa(mask); | ||
| 187 | eventmask.deviceid = XIAllDevices; | ||
| 188 | eventmask.mask_len = sizeof(mask); | ||
| 189 | eventmask.mask = mask; | ||
| 190 | |||
| 191 | XISetMask(mask, XI_HierarchyChanged); | ||
| 192 | X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1); | ||
| 193 | |||
| 194 | X11_Xinput2UpdateDevices(_this, true); | ||
| 195 | |||
| 196 | return true; | ||
| 197 | #else | ||
| 198 | return false; | ||
| 199 | #endif | ||
| 200 | } | ||
| 201 | |||
| 202 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 203 | // xi2 device went away? take it out of the list. | ||
| 204 | static void xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id) | ||
| 205 | { | ||
| 206 | SDL_XInput2DeviceInfo *prev = NULL; | ||
| 207 | SDL_XInput2DeviceInfo *devinfo; | ||
| 208 | |||
| 209 | for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) { | ||
| 210 | if (devinfo->device_id == device_id) { | ||
| 211 | SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL)); | ||
| 212 | if (!prev) { | ||
| 213 | videodata->mouse_device_info = devinfo->next; | ||
| 214 | } else { | ||
| 215 | prev->next = devinfo->next; | ||
| 216 | } | ||
| 217 | SDL_free(devinfo); | ||
| 218 | return; | ||
| 219 | } | ||
| 220 | prev = devinfo; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, const int device_id) | ||
| 225 | { | ||
| 226 | // cache device info as we see new devices. | ||
| 227 | SDL_XInput2DeviceInfo *prev = NULL; | ||
| 228 | SDL_XInput2DeviceInfo *devinfo; | ||
| 229 | XIDeviceInfo *xidevinfo; | ||
| 230 | int axis = 0; | ||
| 231 | int i; | ||
| 232 | |||
| 233 | for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) { | ||
| 234 | if (devinfo->device_id == device_id) { | ||
| 235 | SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL)); | ||
| 236 | if (prev) { // move this to the front of the list, assuming we'll get more from this one. | ||
| 237 | prev->next = devinfo->next; | ||
| 238 | devinfo->next = videodata->mouse_device_info; | ||
| 239 | videodata->mouse_device_info = devinfo; | ||
| 240 | } | ||
| 241 | return devinfo; | ||
| 242 | } | ||
| 243 | prev = devinfo; | ||
| 244 | } | ||
| 245 | |||
| 246 | // don't know about this device yet, query and cache it. | ||
| 247 | devinfo = (SDL_XInput2DeviceInfo *)SDL_calloc(1, sizeof(SDL_XInput2DeviceInfo)); | ||
| 248 | if (!devinfo) { | ||
| 249 | return NULL; | ||
| 250 | } | ||
| 251 | |||
| 252 | xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i); | ||
| 253 | if (!xidevinfo) { | ||
| 254 | SDL_free(devinfo); | ||
| 255 | return NULL; | ||
| 256 | } | ||
| 257 | |||
| 258 | devinfo->device_id = device_id; | ||
| 259 | |||
| 260 | /* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given | ||
| 261 | !!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes! | ||
| 262 | !!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */ | ||
| 263 | for (i = 0; i < xidevinfo->num_classes; i++) { | ||
| 264 | const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i]; | ||
| 265 | if (v->type == XIValuatorClass) { | ||
| 266 | devinfo->relative[axis] = (v->mode == XIModeRelative); | ||
| 267 | devinfo->minval[axis] = v->min; | ||
| 268 | devinfo->maxval[axis] = v->max; | ||
| 269 | if (++axis >= 2) { | ||
| 270 | break; | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | |||
| 275 | X11_XIFreeDeviceInfo(xidevinfo); | ||
| 276 | |||
| 277 | devinfo->next = videodata->mouse_device_info; | ||
| 278 | videodata->mouse_device_info = devinfo; | ||
| 279 | |||
| 280 | return devinfo; | ||
| 281 | } | ||
| 282 | #endif | ||
| 283 | |||
| 284 | void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie) | ||
| 285 | { | ||
| 286 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 287 | SDL_VideoData *videodata = _this->internal; | ||
| 288 | |||
| 289 | if (cookie->extension != xinput2_opcode) { | ||
| 290 | return; | ||
| 291 | } | ||
| 292 | |||
| 293 | switch (cookie->evtype) { | ||
| 294 | case XI_HierarchyChanged: | ||
| 295 | { | ||
| 296 | const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data; | ||
| 297 | int i; | ||
| 298 | for (i = 0; i < hierev->num_info; i++) { | ||
| 299 | // pen stuff... | ||
| 300 | if ((hierev->info[i].flags & (XISlaveRemoved | XIDeviceDisabled)) != 0) { | ||
| 301 | X11_RemovePenByDeviceID(hierev->info[i].deviceid); // it's okay if this thing isn't actually a pen, it'll handle it. | ||
| 302 | } else if ((hierev->info[i].flags & (XISlaveAdded | XIDeviceEnabled)) != 0) { | ||
| 303 | X11_MaybeAddPenByDeviceID(_this, hierev->info[i].deviceid); // this will do more checks to make sure this is valid. | ||
| 304 | } | ||
| 305 | |||
| 306 | // not pen stuff... | ||
| 307 | if (hierev->info[i].flags & XISlaveRemoved) { | ||
| 308 | xinput2_remove_device_info(videodata, hierev->info[i].deviceid); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | videodata->xinput_hierarchy_changed = true; | ||
| 312 | } break; | ||
| 313 | |||
| 314 | // !!! FIXME: the pen code used to rescan all devices here, but we can do this device-by-device with XI_HierarchyChanged. When do these events fire and why? | ||
| 315 | //case XI_PropertyEvent: | ||
| 316 | //case XI_DeviceChanged: | ||
| 317 | |||
| 318 | case XI_RawMotion: | ||
| 319 | { | ||
| 320 | const XIRawEvent *rawev = (const XIRawEvent *)cookie->data; | ||
| 321 | const bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL; | ||
| 322 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 323 | SDL_XInput2DeviceInfo *devinfo; | ||
| 324 | double coords[2]; | ||
| 325 | double processed_coords[2]; | ||
| 326 | int i; | ||
| 327 | Uint64 timestamp = X11_GetEventTimestamp(rawev->time); | ||
| 328 | |||
| 329 | videodata->global_mouse_changed = true; | ||
| 330 | if (is_pen) { | ||
| 331 | break; // Pens check for XI_Motion instead | ||
| 332 | } | ||
| 333 | |||
| 334 | devinfo = xinput2_get_device_info(videodata, rawev->deviceid); | ||
| 335 | if (!devinfo) { | ||
| 336 | break; // oh well. | ||
| 337 | } | ||
| 338 | |||
| 339 | parse_valuators(rawev->raw_values, rawev->valuators.mask, | ||
| 340 | rawev->valuators.mask_len, coords, 2); | ||
| 341 | |||
| 342 | for (i = 0; i < 2; i++) { | ||
| 343 | if (devinfo->relative[i]) { | ||
| 344 | processed_coords[i] = coords[i]; | ||
| 345 | } else { | ||
| 346 | processed_coords[i] = devinfo->prev_coords[i] - coords[i]; // convert absolute to relative | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | // Relative mouse motion is delivered to the window with keyboard focus | ||
| 351 | if (mouse->relative_mode && SDL_GetKeyboardFocus()) { | ||
| 352 | SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]); | ||
| 353 | } | ||
| 354 | |||
| 355 | devinfo->prev_coords[0] = coords[0]; | ||
| 356 | devinfo->prev_coords[1] = coords[1]; | ||
| 357 | } break; | ||
| 358 | |||
| 359 | case XI_KeyPress: | ||
| 360 | case XI_KeyRelease: | ||
| 361 | { | ||
| 362 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 363 | SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event); | ||
| 364 | XEvent xevent; | ||
| 365 | |||
| 366 | if (xev->deviceid != xev->sourceid) { | ||
| 367 | // Discard events from "Master" devices to avoid duplicates. | ||
| 368 | break; | ||
| 369 | } | ||
| 370 | |||
| 371 | if (cookie->evtype == XI_KeyPress) { | ||
| 372 | xevent.type = KeyPress; | ||
| 373 | } else { | ||
| 374 | xevent.type = KeyRelease; | ||
| 375 | } | ||
| 376 | xevent.xkey.serial = xev->serial; | ||
| 377 | xevent.xkey.send_event = xev->send_event; | ||
| 378 | xevent.xkey.display = xev->display; | ||
| 379 | xevent.xkey.window = xev->event; | ||
| 380 | xevent.xkey.root = xev->root; | ||
| 381 | xevent.xkey.subwindow = xev->child; | ||
| 382 | xevent.xkey.time = xev->time; | ||
| 383 | xevent.xkey.x = (int)xev->event_x; | ||
| 384 | xevent.xkey.y = (int)xev->event_y; | ||
| 385 | xevent.xkey.x_root = (int)xev->root_x; | ||
| 386 | xevent.xkey.y_root = (int)xev->root_y; | ||
| 387 | xevent.xkey.state = xev->mods.effective; | ||
| 388 | xevent.xkey.keycode = xev->detail; | ||
| 389 | xevent.xkey.same_screen = 1; | ||
| 390 | |||
| 391 | X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent); | ||
| 392 | } break; | ||
| 393 | |||
| 394 | case XI_RawButtonPress: | ||
| 395 | case XI_RawButtonRelease: | ||
| 396 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 397 | case XI_RawTouchBegin: | ||
| 398 | case XI_RawTouchUpdate: | ||
| 399 | case XI_RawTouchEnd: | ||
| 400 | #endif | ||
| 401 | { | ||
| 402 | videodata->global_mouse_changed = true; | ||
| 403 | } break; | ||
| 404 | |||
| 405 | case XI_ButtonPress: | ||
| 406 | case XI_ButtonRelease: | ||
| 407 | { | ||
| 408 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 409 | X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid); | ||
| 410 | const int button = xev->detail; | ||
| 411 | const bool down = (cookie->evtype == XI_ButtonPress); | ||
| 412 | |||
| 413 | if (pen) { | ||
| 414 | // Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway. | ||
| 415 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 416 | if (button == 1) { // button 1 is the pen tip | ||
| 417 | SDL_SendPenTouch(0, pen->pen, window, pen->is_eraser, down); | ||
| 418 | } else { | ||
| 419 | SDL_SendPenButton(0, pen->pen, window, button - 1, down); | ||
| 420 | } | ||
| 421 | } else { | ||
| 422 | // Otherwise assume a regular mouse | ||
| 423 | SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event); | ||
| 424 | |||
| 425 | if (xev->deviceid != xev->sourceid) { | ||
| 426 | // Discard events from "Master" devices to avoid duplicates. | ||
| 427 | break; | ||
| 428 | } | ||
| 429 | |||
| 430 | if (down) { | ||
| 431 | X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button, | ||
| 432 | (float)xev->event_x, (float)xev->event_y, xev->time); | ||
| 433 | } else { | ||
| 434 | X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time); | ||
| 435 | } | ||
| 436 | } | ||
| 437 | } break; | ||
| 438 | |||
| 439 | /* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish | ||
| 440 | real mouse motions from synthetic ones, for multitouch and pen support. */ | ||
| 441 | case XI_Motion: | ||
| 442 | { | ||
| 443 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 444 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 445 | bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0); | ||
| 446 | #else | ||
| 447 | bool pointer_emulated = false; | ||
| 448 | #endif | ||
| 449 | |||
| 450 | videodata->global_mouse_changed = true; | ||
| 451 | |||
| 452 | X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid); | ||
| 453 | if (pen) { | ||
| 454 | if (xev->deviceid != xev->sourceid) { | ||
| 455 | // Discard events from "Master" devices to avoid duplicates. | ||
| 456 | break; | ||
| 457 | } | ||
| 458 | |||
| 459 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 460 | SDL_SendPenMotion(0, pen->pen, window, (float) xev->event_x, (float) xev->event_y); | ||
| 461 | |||
| 462 | float axes[SDL_PEN_AXIS_COUNT]; | ||
| 463 | X11_PenAxesFromValuators(pen, xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len, axes); | ||
| 464 | |||
| 465 | for (int i = 0; i < SDL_arraysize(axes); i++) { | ||
| 466 | if (pen->valuator_for_axis[i] != SDL_X11_PEN_AXIS_VALUATOR_MISSING) { | ||
| 467 | SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | } else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) { | ||
| 471 | // Use the master device for non-relative motion, as the slave devices can seemingly lag behind. | ||
| 472 | SDL_Mouse *mouse = SDL_GetMouse(); | ||
| 473 | if (!mouse->relative_mode) { | ||
| 474 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 475 | if (window) { | ||
| 476 | X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false); | ||
| 477 | SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } break; | ||
| 482 | |||
| 483 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 484 | case XI_TouchBegin: | ||
| 485 | { | ||
| 486 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 487 | float x, y; | ||
| 488 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 489 | xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); | ||
| 490 | SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_DOWN, x, y, 1.0); | ||
| 491 | } break; | ||
| 492 | |||
| 493 | case XI_TouchEnd: | ||
| 494 | { | ||
| 495 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 496 | float x, y; | ||
| 497 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 498 | xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); | ||
| 499 | SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_UP, x, y, 1.0); | ||
| 500 | } break; | ||
| 501 | |||
| 502 | case XI_TouchUpdate: | ||
| 503 | { | ||
| 504 | const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data; | ||
| 505 | float x, y; | ||
| 506 | SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event); | ||
| 507 | xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y); | ||
| 508 | SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0); | ||
| 509 | } break; | ||
| 510 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 511 | } | ||
| 512 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 513 | } | ||
| 514 | |||
| 515 | void X11_InitXinput2Multitouch(SDL_VideoDevice *_this) | ||
| 516 | { | ||
| 517 | } | ||
| 518 | |||
| 519 | void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 520 | { | ||
| 521 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 522 | SDL_VideoData *data = NULL; | ||
| 523 | XIEventMask eventmask; | ||
| 524 | unsigned char mask[4] = { 0, 0, 0, 0 }; | ||
| 525 | SDL_WindowData *window_data = NULL; | ||
| 526 | |||
| 527 | if (!X11_Xinput2IsMultitouchSupported()) { | ||
| 528 | return; | ||
| 529 | } | ||
| 530 | |||
| 531 | data = _this->internal; | ||
| 532 | window_data = window->internal; | ||
| 533 | |||
| 534 | eventmask.deviceid = XIAllMasterDevices; | ||
| 535 | eventmask.mask_len = sizeof(mask); | ||
| 536 | eventmask.mask = mask; | ||
| 537 | |||
| 538 | XISetMask(mask, XI_TouchBegin); | ||
| 539 | XISetMask(mask, XI_TouchUpdate); | ||
| 540 | XISetMask(mask, XI_TouchEnd); | ||
| 541 | XISetMask(mask, XI_Motion); | ||
| 542 | |||
| 543 | X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1); | ||
| 544 | #endif | ||
| 545 | } | ||
| 546 | |||
| 547 | bool X11_Xinput2IsInitialized(void) | ||
| 548 | { | ||
| 549 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 550 | return xinput2_initialized; | ||
| 551 | #else | ||
| 552 | return false; | ||
| 553 | #endif | ||
| 554 | } | ||
| 555 | |||
| 556 | bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 557 | { | ||
| 558 | SDL_WindowData *windowdata = window->internal; | ||
| 559 | |||
| 560 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 561 | const SDL_VideoData *data = _this->internal; | ||
| 562 | |||
| 563 | if (X11_Xinput2IsInitialized()) { | ||
| 564 | XIEventMask eventmask; | ||
| 565 | unsigned char mask[4] = { 0, 0, 0, 0 }; | ||
| 566 | |||
| 567 | eventmask.mask_len = sizeof(mask); | ||
| 568 | eventmask.mask = mask; | ||
| 569 | eventmask.deviceid = XIAllDevices; | ||
| 570 | |||
| 571 | // This is not enabled by default because these events are only delivered to the window with mouse focus, not keyboard focus | ||
| 572 | #ifdef USE_XINPUT2_KEYBOARD | ||
| 573 | XISetMask(mask, XI_KeyPress); | ||
| 574 | XISetMask(mask, XI_KeyRelease); | ||
| 575 | windowdata->xinput2_keyboard_enabled = true; | ||
| 576 | #endif | ||
| 577 | |||
| 578 | XISetMask(mask, XI_ButtonPress); | ||
| 579 | XISetMask(mask, XI_ButtonRelease); | ||
| 580 | XISetMask(mask, XI_Motion); | ||
| 581 | windowdata->xinput2_mouse_enabled = true; | ||
| 582 | |||
| 583 | XISetMask(mask, XI_Enter); | ||
| 584 | XISetMask(mask, XI_Leave); | ||
| 585 | |||
| 586 | // Hotplugging: | ||
| 587 | XISetMask(mask, XI_DeviceChanged); | ||
| 588 | XISetMask(mask, XI_HierarchyChanged); | ||
| 589 | XISetMask(mask, XI_PropertyEvent); // E.g., when swapping tablet pens | ||
| 590 | |||
| 591 | if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) != Success) { | ||
| 592 | SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling"); | ||
| 593 | windowdata->xinput2_keyboard_enabled = false; | ||
| 594 | windowdata->xinput2_mouse_enabled = false; | ||
| 595 | } | ||
| 596 | } | ||
| 597 | #endif | ||
| 598 | |||
| 599 | if (windowdata->xinput2_keyboard_enabled || windowdata->xinput2_mouse_enabled) { | ||
| 600 | return true; | ||
| 601 | } | ||
| 602 | return false; | ||
| 603 | } | ||
| 604 | |||
| 605 | bool X11_Xinput2IsMultitouchSupported(void) | ||
| 606 | { | ||
| 607 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 608 | return xinput2_initialized && xinput2_multitouch_supported; | ||
| 609 | #else | ||
| 610 | return true; | ||
| 611 | #endif | ||
| 612 | } | ||
| 613 | |||
| 614 | void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 615 | { | ||
| 616 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 617 | SDL_WindowData *data = window->internal; | ||
| 618 | Display *display = data->videodata->display; | ||
| 619 | |||
| 620 | unsigned char mask[4] = { 0, 0, 0, 0 }; | ||
| 621 | XIGrabModifiers mods; | ||
| 622 | XIEventMask eventmask; | ||
| 623 | |||
| 624 | if (!X11_Xinput2IsMultitouchSupported()) { | ||
| 625 | return; | ||
| 626 | } | ||
| 627 | |||
| 628 | mods.modifiers = XIAnyModifier; | ||
| 629 | mods.status = 0; | ||
| 630 | |||
| 631 | eventmask.deviceid = XIAllDevices; | ||
| 632 | eventmask.mask_len = sizeof(mask); | ||
| 633 | eventmask.mask = mask; | ||
| 634 | |||
| 635 | XISetMask(eventmask.mask, XI_TouchBegin); | ||
| 636 | XISetMask(eventmask.mask, XI_TouchUpdate); | ||
| 637 | XISetMask(eventmask.mask, XI_TouchEnd); | ||
| 638 | XISetMask(eventmask.mask, XI_Motion); | ||
| 639 | |||
| 640 | X11_XIGrabTouchBegin(display, XIAllDevices, data->xwindow, True, &eventmask, 1, &mods); | ||
| 641 | #endif | ||
| 642 | } | ||
| 643 | |||
| 644 | void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window) | ||
| 645 | { | ||
| 646 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 647 | SDL_WindowData *data = window->internal; | ||
| 648 | Display *display = data->videodata->display; | ||
| 649 | |||
| 650 | XIGrabModifiers mods; | ||
| 651 | |||
| 652 | if (!X11_Xinput2IsMultitouchSupported()) { | ||
| 653 | return; | ||
| 654 | } | ||
| 655 | |||
| 656 | mods.modifiers = XIAnyModifier; | ||
| 657 | mods.status = 0; | ||
| 658 | |||
| 659 | X11_XIUngrabTouchBegin(display, XIAllDevices, data->xwindow, 1, &mods); | ||
| 660 | #endif | ||
| 661 | } | ||
| 662 | |||
| 663 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 664 | |||
| 665 | static void AddDeviceID(Uint32 deviceID, Uint32 **list, int *count) | ||
| 666 | { | ||
| 667 | int new_count = (*count + 1); | ||
| 668 | Uint32 *new_list = (Uint32 *)SDL_realloc(*list, new_count * sizeof(*new_list)); | ||
| 669 | if (!new_list) { | ||
| 670 | // Oh well, we'll drop this one | ||
| 671 | return; | ||
| 672 | } | ||
| 673 | new_list[new_count - 1] = deviceID; | ||
| 674 | |||
| 675 | *count = new_count; | ||
| 676 | *list = new_list; | ||
| 677 | } | ||
| 678 | |||
| 679 | static bool HasDeviceID(Uint32 deviceID, const Uint32 *list, int count) | ||
| 680 | { | ||
| 681 | for (int i = 0; i < count; ++i) { | ||
| 682 | if (deviceID == list[i]) { | ||
| 683 | return true; | ||
| 684 | } | ||
| 685 | } | ||
| 686 | return false; | ||
| 687 | } | ||
| 688 | |||
| 689 | static void AddDeviceID64(Uint64 deviceID, Uint64 **list, int *count) | ||
| 690 | { | ||
| 691 | int new_count = (*count + 1); | ||
| 692 | Uint64 *new_list = (Uint64 *)SDL_realloc(*list, new_count * sizeof(*new_list)); | ||
| 693 | if (!new_list) { | ||
| 694 | // Oh well, we'll drop this one | ||
| 695 | return; | ||
| 696 | } | ||
| 697 | new_list[new_count - 1] = deviceID; | ||
| 698 | |||
| 699 | *count = new_count; | ||
| 700 | *list = new_list; | ||
| 701 | } | ||
| 702 | |||
| 703 | static bool HasDeviceID64(Uint64 deviceID, const Uint64 *list, int count) | ||
| 704 | { | ||
| 705 | for (int i = 0; i < count; ++i) { | ||
| 706 | if (deviceID == list[i]) { | ||
| 707 | return true; | ||
| 708 | } | ||
| 709 | } | ||
| 710 | return false; | ||
| 711 | } | ||
| 712 | |||
| 713 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 714 | |||
| 715 | void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check) | ||
| 716 | { | ||
| 717 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 718 | SDL_VideoData *data = _this->internal; | ||
| 719 | XIDeviceInfo *info; | ||
| 720 | int ndevices; | ||
| 721 | int old_keyboard_count = 0; | ||
| 722 | SDL_KeyboardID *old_keyboards = NULL; | ||
| 723 | int new_keyboard_count = 0; | ||
| 724 | SDL_KeyboardID *new_keyboards = NULL; | ||
| 725 | int old_mouse_count = 0; | ||
| 726 | SDL_MouseID *old_mice = NULL; | ||
| 727 | int new_mouse_count = 0; | ||
| 728 | SDL_MouseID *new_mice = NULL; | ||
| 729 | int old_touch_count = 0; | ||
| 730 | Uint64 *old_touch_devices = NULL; | ||
| 731 | int new_touch_count = 0; | ||
| 732 | Uint64 *new_touch_devices = NULL; | ||
| 733 | bool send_event = !initial_check; | ||
| 734 | |||
| 735 | SDL_assert(X11_Xinput2IsInitialized()); | ||
| 736 | |||
| 737 | info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices); | ||
| 738 | |||
| 739 | old_keyboards = SDL_GetKeyboards(&old_keyboard_count); | ||
| 740 | old_mice = SDL_GetMice(&old_mouse_count); | ||
| 741 | old_touch_devices = SDL_GetTouchDevices(&old_touch_count); | ||
| 742 | |||
| 743 | for (int i = 0; i < ndevices; i++) { | ||
| 744 | XIDeviceInfo *dev = &info[i]; | ||
| 745 | |||
| 746 | switch (dev->use) { | ||
| 747 | case XIMasterKeyboard: | ||
| 748 | case XISlaveKeyboard: | ||
| 749 | { | ||
| 750 | SDL_KeyboardID keyboardID = (SDL_KeyboardID)dev->deviceid; | ||
| 751 | AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count); | ||
| 752 | if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) { | ||
| 753 | SDL_AddKeyboard(keyboardID, dev->name, send_event); | ||
| 754 | } | ||
| 755 | } | ||
| 756 | break; | ||
| 757 | case XIMasterPointer: | ||
| 758 | data->xinput_master_pointer_device = dev->deviceid; | ||
| 759 | SDL_FALLTHROUGH; | ||
| 760 | case XISlavePointer: | ||
| 761 | { | ||
| 762 | SDL_MouseID mouseID = (SDL_MouseID)dev->deviceid; | ||
| 763 | AddDeviceID(mouseID, &new_mice, &new_mouse_count); | ||
| 764 | if (!HasDeviceID(mouseID, old_mice, old_mouse_count)) { | ||
| 765 | SDL_AddMouse(mouseID, dev->name, send_event); | ||
| 766 | } | ||
| 767 | } | ||
| 768 | break; | ||
| 769 | default: | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | |||
| 773 | #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 774 | for (int j = 0; j < dev->num_classes; j++) { | ||
| 775 | Uint64 touchID; | ||
| 776 | SDL_TouchDeviceType touchType; | ||
| 777 | XIAnyClassInfo *class = dev->classes[j]; | ||
| 778 | XITouchClassInfo *t = (XITouchClassInfo *)class; | ||
| 779 | |||
| 780 | // Only touch devices | ||
| 781 | if (class->type != XITouchClass) { | ||
| 782 | continue; | ||
| 783 | } | ||
| 784 | |||
| 785 | touchID = (Uint64)t->sourceid; | ||
| 786 | AddDeviceID64(touchID, &new_touch_devices, &new_touch_count); | ||
| 787 | if (!HasDeviceID64(touchID, old_touch_devices, old_touch_count)) { | ||
| 788 | if (t->mode == XIDependentTouch) { | ||
| 789 | touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE; | ||
| 790 | } else { // XIDirectTouch | ||
| 791 | touchType = SDL_TOUCH_DEVICE_DIRECT; | ||
| 792 | } | ||
| 793 | SDL_AddTouch(touchID, touchType, dev->name); | ||
| 794 | } | ||
| 795 | } | ||
| 796 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH | ||
| 797 | } | ||
| 798 | |||
| 799 | for (int i = old_keyboard_count; i--;) { | ||
| 800 | if (!HasDeviceID(old_keyboards[i], new_keyboards, new_keyboard_count)) { | ||
| 801 | SDL_RemoveKeyboard(old_keyboards[i], send_event); | ||
| 802 | } | ||
| 803 | } | ||
| 804 | |||
| 805 | for (int i = old_mouse_count; i--;) { | ||
| 806 | if (!HasDeviceID(old_mice[i], new_mice, new_mouse_count)) { | ||
| 807 | SDL_RemoveMouse(old_mice[i], send_event); | ||
| 808 | } | ||
| 809 | } | ||
| 810 | |||
| 811 | for (int i = old_touch_count; i--;) { | ||
| 812 | if (!HasDeviceID64(old_touch_devices[i], new_touch_devices, new_touch_count)) { | ||
| 813 | SDL_DelTouch(old_touch_devices[i]); | ||
| 814 | } | ||
| 815 | } | ||
| 816 | |||
| 817 | SDL_free(old_keyboards); | ||
| 818 | SDL_free(new_keyboards); | ||
| 819 | SDL_free(old_mice); | ||
| 820 | SDL_free(new_mice); | ||
| 821 | SDL_free(old_touch_devices); | ||
| 822 | SDL_free(new_touch_devices); | ||
| 823 | |||
| 824 | X11_XIFreeDeviceInfo(info); | ||
| 825 | |||
| 826 | #endif // SDL_VIDEO_DRIVER_X11_XINPUT2 | ||
| 827 | } | ||
| 828 | |||
| 829 | #endif // SDL_VIDEO_DRIVER_X11 | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h new file mode 100644 index 0000000..c96c020 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xinput2.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | #ifndef SDL_x11xinput2_h_ | ||
| 24 | #define SDL_x11xinput2_h_ | ||
| 25 | |||
| 26 | #ifndef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS | ||
| 27 | /* Define XGenericEventCookie as forward declaration when | ||
| 28 | *xinput2 is not available in order to compile */ | ||
| 29 | struct XGenericEventCookie; | ||
| 30 | typedef struct XGenericEventCookie XGenericEventCookie; | ||
| 31 | #endif | ||
| 32 | |||
| 33 | extern bool X11_InitXinput2(SDL_VideoDevice *_this); | ||
| 34 | extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this); | ||
| 35 | extern void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie); | ||
| 36 | extern bool X11_Xinput2IsInitialized(void); | ||
| 37 | extern bool X11_Xinput2IsMultitouchSupported(void); | ||
| 38 | extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 39 | extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 40 | extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 41 | extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window); | ||
| 42 | extern void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check); | ||
| 43 | |||
| 44 | #endif // SDL_x11xinput2_h_ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c new file mode 100644 index 0000000..5310d67 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.c | |||
| @@ -0,0 +1,148 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XSYNC) | ||
| 25 | |||
| 26 | #include "SDL_x11video.h" | ||
| 27 | #include "SDL_x11xsync.h" | ||
| 28 | |||
| 29 | static int xsync_initialized = 0; | ||
| 30 | |||
| 31 | static int query_xsync_version(Display *display, int major, int minor) | ||
| 32 | { | ||
| 33 | /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */ | ||
| 34 | X11_XSyncInitialize(display, &major, &minor); | ||
| 35 | return (major * 1000) + minor; | ||
| 36 | } | ||
| 37 | |||
| 38 | static bool xsync_version_atleast(const int version, const int wantmajor, const int wantminor) | ||
| 39 | { | ||
| 40 | return version >= ((wantmajor * 1000) + wantminor); | ||
| 41 | } | ||
| 42 | |||
| 43 | void X11_InitXsync(SDL_VideoDevice *_this) | ||
| 44 | { | ||
| 45 | SDL_VideoData *data = _this->internal; | ||
| 46 | |||
| 47 | int version = 0; | ||
| 48 | int event, error; | ||
| 49 | int sync_opcode; | ||
| 50 | |||
| 51 | if (!SDL_X11_HAVE_XSYNC || | ||
| 52 | !X11_XQueryExtension(data->display, "SYNC", &sync_opcode, &event, &error)) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* We need at least 5.0 for barriers. */ | ||
| 57 | version = query_xsync_version(data->display, 5, 0); | ||
| 58 | if (!xsync_version_atleast(version, 3, 0)) { | ||
| 59 | return; /* X server does not support the version we want at all. */ | ||
| 60 | } | ||
| 61 | |||
| 62 | xsync_initialized = 1; | ||
| 63 | } | ||
| 64 | |||
| 65 | int X11_XsyncIsInitialized(void) | ||
| 66 | { | ||
| 67 | return xsync_initialized; | ||
| 68 | } | ||
| 69 | |||
| 70 | int X11_InitResizeSync(SDL_Window *window) | ||
| 71 | { | ||
| 72 | SDL_assert(window != NULL); | ||
| 73 | SDL_WindowData *data = window->internal; | ||
| 74 | Display *display = data->videodata->display; | ||
| 75 | Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER; | ||
| 76 | XSyncCounter counter; | ||
| 77 | CARD32 counter_id; | ||
| 78 | |||
| 79 | if (!X11_XsyncIsInitialized()){ | ||
| 80 | return SDL_Unsupported(); | ||
| 81 | } | ||
| 82 | |||
| 83 | counter = X11_XSyncCreateCounter(display, (XSyncValue){0, 0}); | ||
| 84 | data->resize_counter = counter; | ||
| 85 | data->resize_id.lo = 0; | ||
| 86 | data->resize_id.hi = 0; | ||
| 87 | data->resize_in_progress = false; | ||
| 88 | |||
| 89 | if (counter == None){ | ||
| 90 | return SDL_Unsupported(); | ||
| 91 | } | ||
| 92 | |||
| 93 | counter_id = counter; | ||
| 94 | X11_XChangeProperty(display, data->xwindow, counter_prop, XA_CARDINAL, 32, | ||
| 95 | PropModeReplace, (unsigned char *)&counter_id, 1); | ||
| 96 | |||
| 97 | return 0; | ||
| 98 | } | ||
| 99 | |||
| 100 | void X11_TermResizeSync(SDL_Window *window) | ||
| 101 | { | ||
| 102 | SDL_WindowData *data = window->internal; | ||
| 103 | Display *display = data->videodata->display; | ||
| 104 | Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER; | ||
| 105 | XSyncCounter counter = data->resize_counter; | ||
| 106 | |||
| 107 | X11_XDeleteProperty(display, data->xwindow, counter_prop); | ||
| 108 | if (counter != None) { | ||
| 109 | X11_XSyncDestroyCounter(display, counter); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event) | ||
| 114 | { | ||
| 115 | SDL_WindowData *data = window->internal; | ||
| 116 | |||
| 117 | data->resize_id.lo = event->data.l[2]; | ||
| 118 | data->resize_id.hi = event->data.l[3]; | ||
| 119 | data->resize_in_progress = false; | ||
| 120 | } | ||
| 121 | |||
| 122 | void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event) | ||
| 123 | { | ||
| 124 | SDL_WindowData *data = window->internal; | ||
| 125 | |||
| 126 | if (data->resize_id.lo || data->resize_id.hi) { | ||
| 127 | data->resize_in_progress = true; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | void X11_HandlePresent(SDL_Window *window) | ||
| 132 | { | ||
| 133 | SDL_WindowData *data = window->internal; | ||
| 134 | Display *display = data->videodata->display; | ||
| 135 | XSyncCounter counter = data->resize_counter; | ||
| 136 | |||
| 137 | if ((counter == None) || (!data->resize_in_progress)) { | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | |||
| 141 | X11_XSyncSetCounter(display, counter, data->resize_id); | ||
| 142 | |||
| 143 | data->resize_id.lo = 0; | ||
| 144 | data->resize_id.hi = 0; | ||
| 145 | data->resize_in_progress = false; | ||
| 146 | } | ||
| 147 | |||
| 148 | #endif /* SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h new file mode 100644 index 0000000..bc747c1 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/SDL_x11xsync.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | Simple DirectMedia Layer | ||
| 3 | Copyright (C) 1997-2025 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 | |||
| 22 | #include "SDL_internal.h" | ||
| 23 | |||
| 24 | #ifndef SDL_x11xsync_h_ | ||
| 25 | #define SDL_x11xsync_h_ | ||
| 26 | |||
| 27 | #ifdef SDL_VIDEO_DRIVER_X11_XSYNC | ||
| 28 | |||
| 29 | extern void X11_InitXsync(SDL_VideoDevice *_this); | ||
| 30 | extern int X11_XsyncIsInitialized(void); | ||
| 31 | int X11_InitResizeSync(SDL_Window *window); | ||
| 32 | void X11_TermResizeSync(SDL_Window *window); | ||
| 33 | void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event); | ||
| 34 | void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event); | ||
| 35 | void X11_HandlePresent(SDL_Window *window); | ||
| 36 | |||
| 37 | #endif /* SDL_VIDEO_DRIVER_X11_XSYNC */ | ||
| 38 | |||
| 39 | #endif /* SDL_x11xsync_h_ */ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/edid-parse.c b/contrib/SDL-3.2.8/src/video/x11/edid-parse.c new file mode 100644 index 0000000..eca187b --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/edid-parse.c | |||
| @@ -0,0 +1,753 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2007 Red Hat, Inc. | ||
| 3 | * | ||
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 5 | * copy of this software and associated documentation files (the "Software"), | ||
| 6 | * to deal in the Software without restriction, including without limitation | ||
| 7 | * on the rights to use, copy, modify, merge, publish, distribute, sub | ||
| 8 | * license, and/or sell copies of the Software, and to permit persons to whom | ||
| 9 | * the Software is furnished to do so, subject to the following conditions: | ||
| 10 | * | ||
| 11 | * The above copyright notice and this permission notice (including the next | ||
| 12 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 13 | * Software. | ||
| 14 | * | ||
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
| 18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* Author: Soren Sandmann <sandmann@redhat.com> */ | ||
| 24 | #include "SDL_internal.h" | ||
| 25 | |||
| 26 | #include "edid.h" | ||
| 27 | #include <stdlib.h> | ||
| 28 | #include <string.h> | ||
| 29 | #include <math.h> | ||
| 30 | #include <stdio.h> | ||
| 31 | |||
| 32 | #define TRUE 1 | ||
| 33 | #define FALSE 0 | ||
| 34 | |||
| 35 | static int | ||
| 36 | get_bit (int in, int bit) | ||
| 37 | { | ||
| 38 | return (in & (1 << bit)) >> bit; | ||
| 39 | } | ||
| 40 | |||
| 41 | static int | ||
| 42 | get_bits (int in, int begin, int end) | ||
| 43 | { | ||
| 44 | int mask = (1 << (end - begin + 1)) - 1; | ||
| 45 | |||
| 46 | return (in >> begin) & mask; | ||
| 47 | } | ||
| 48 | |||
| 49 | static int | ||
| 50 | decode_header (const uchar *edid) | ||
| 51 | { | ||
| 52 | if (SDL_memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0) | ||
| 53 | return TRUE; | ||
| 54 | return FALSE; | ||
| 55 | } | ||
| 56 | |||
| 57 | static int | ||
| 58 | decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info) | ||
| 59 | { | ||
| 60 | int is_model_year; | ||
| 61 | |||
| 62 | /* Manufacturer Code */ | ||
| 63 | info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6); | ||
| 64 | info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3; | ||
| 65 | info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7); | ||
| 66 | info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4); | ||
| 67 | info->manufacturer_code[3] = '\0'; | ||
| 68 | |||
| 69 | info->manufacturer_code[0] += 'A' - 1; | ||
| 70 | info->manufacturer_code[1] += 'A' - 1; | ||
| 71 | info->manufacturer_code[2] += 'A' - 1; | ||
| 72 | |||
| 73 | /* Product Code */ | ||
| 74 | info->product_code = edid[0x0b] << 8 | edid[0x0a]; | ||
| 75 | |||
| 76 | /* Serial Number */ | ||
| 77 | info->serial_number = | ||
| 78 | edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | (Uint32)edid[0x0f] << 24; | ||
| 79 | |||
| 80 | /* Week and Year */ | ||
| 81 | is_model_year = FALSE; | ||
| 82 | switch (edid[0x10]) | ||
| 83 | { | ||
| 84 | case 0x00: | ||
| 85 | info->production_week = -1; | ||
| 86 | break; | ||
| 87 | |||
| 88 | case 0xff: | ||
| 89 | info->production_week = -1; | ||
| 90 | is_model_year = TRUE; | ||
| 91 | break; | ||
| 92 | |||
| 93 | default: | ||
| 94 | info->production_week = edid[0x10]; | ||
| 95 | break; | ||
| 96 | } | ||
| 97 | |||
| 98 | if (is_model_year) | ||
| 99 | { | ||
| 100 | info->production_year = -1; | ||
| 101 | info->model_year = 1990 + edid[0x11]; | ||
| 102 | } | ||
| 103 | else | ||
| 104 | { | ||
| 105 | info->production_year = 1990 + edid[0x11]; | ||
| 106 | info->model_year = -1; | ||
| 107 | } | ||
| 108 | |||
| 109 | return TRUE; | ||
| 110 | } | ||
| 111 | |||
| 112 | static int | ||
| 113 | decode_edid_version (const uchar *edid, MonitorInfo *info) | ||
| 114 | { | ||
| 115 | info->major_version = edid[0x12]; | ||
| 116 | info->minor_version = edid[0x13]; | ||
| 117 | |||
| 118 | return TRUE; | ||
| 119 | } | ||
| 120 | |||
| 121 | static int | ||
| 122 | decode_display_parameters (const uchar *edid, MonitorInfo *info) | ||
| 123 | { | ||
| 124 | /* Digital vs Analog */ | ||
| 125 | info->is_digital = get_bit (edid[0x14], 7); | ||
| 126 | |||
| 127 | if (info->is_digital) | ||
| 128 | { | ||
| 129 | int bits; | ||
| 130 | |||
| 131 | static const int bit_depth[8] = | ||
| 132 | { | ||
| 133 | -1, 6, 8, 10, 12, 14, 16, -1 | ||
| 134 | }; | ||
| 135 | |||
| 136 | static const Interface interfaces[6] = | ||
| 137 | { | ||
| 138 | UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT | ||
| 139 | }; | ||
| 140 | |||
| 141 | bits = get_bits (edid[0x14], 4, 6); | ||
| 142 | info->ad.digital.bits_per_primary = bit_depth[bits]; | ||
| 143 | |||
| 144 | bits = get_bits (edid[0x14], 0, 3); | ||
| 145 | |||
| 146 | if (bits <= 5) | ||
| 147 | info->ad.digital.interface = interfaces[bits]; | ||
| 148 | else | ||
| 149 | info->ad.digital.interface = UNDEFINED; | ||
| 150 | } | ||
| 151 | else | ||
| 152 | { | ||
| 153 | int bits = get_bits (edid[0x14], 5, 6); | ||
| 154 | |||
| 155 | static const double levels[][3] = | ||
| 156 | { | ||
| 157 | { 0.7, 0.3, 1.0 }, | ||
| 158 | { 0.714, 0.286, 1.0 }, | ||
| 159 | { 1.0, 0.4, 1.4 }, | ||
| 160 | { 0.7, 0.0, 0.7 }, | ||
| 161 | }; | ||
| 162 | |||
| 163 | info->ad.analog.video_signal_level = levels[bits][0]; | ||
| 164 | info->ad.analog.sync_signal_level = levels[bits][1]; | ||
| 165 | info->ad.analog.total_signal_level = levels[bits][2]; | ||
| 166 | |||
| 167 | info->ad.analog.blank_to_black = get_bit (edid[0x14], 4); | ||
| 168 | |||
| 169 | info->ad.analog.separate_hv_sync = get_bit (edid[0x14], 3); | ||
| 170 | info->ad.analog.composite_sync_on_h = get_bit (edid[0x14], 2); | ||
| 171 | info->ad.analog.composite_sync_on_green = get_bit (edid[0x14], 1); | ||
| 172 | |||
| 173 | info->ad.analog.serration_on_vsync = get_bit (edid[0x14], 0); | ||
| 174 | } | ||
| 175 | |||
| 176 | /* Screen Size / Aspect Ratio */ | ||
| 177 | if (edid[0x15] == 0 && edid[0x16] == 0) | ||
| 178 | { | ||
| 179 | info->width_mm = -1; | ||
| 180 | info->height_mm = -1; | ||
| 181 | info->aspect_ratio = -1.0; | ||
| 182 | } | ||
| 183 | else if (edid[0x16] == 0) | ||
| 184 | { | ||
| 185 | info->width_mm = -1; | ||
| 186 | info->height_mm = -1; | ||
| 187 | info->aspect_ratio = 100.0 / (edid[0x15] + 99); | ||
| 188 | } | ||
| 189 | else if (edid[0x15] == 0) | ||
| 190 | { | ||
| 191 | info->width_mm = -1; | ||
| 192 | info->height_mm = -1; | ||
| 193 | info->aspect_ratio = 100.0 / (edid[0x16] + 99); | ||
| 194 | info->aspect_ratio = 1/info->aspect_ratio; /* portrait */ | ||
| 195 | } | ||
| 196 | else | ||
| 197 | { | ||
| 198 | info->width_mm = 10 * edid[0x15]; | ||
| 199 | info->height_mm = 10 * edid[0x16]; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* Gamma */ | ||
| 203 | if (edid[0x17] == 0xFF) | ||
| 204 | info->gamma = -1.0; | ||
| 205 | else | ||
| 206 | info->gamma = (edid[0x17] + 100.0) / 100.0; | ||
| 207 | |||
| 208 | /* Features */ | ||
| 209 | info->standby = get_bit (edid[0x18], 7); | ||
| 210 | info->suspend = get_bit (edid[0x18], 6); | ||
| 211 | info->active_off = get_bit (edid[0x18], 5); | ||
| 212 | |||
| 213 | if (info->is_digital) | ||
| 214 | { | ||
| 215 | info->ad.digital.rgb444 = TRUE; | ||
| 216 | if (get_bit (edid[0x18], 3)) | ||
| 217 | info->ad.digital.ycrcb444 = 1; | ||
| 218 | if (get_bit (edid[0x18], 4)) | ||
| 219 | info->ad.digital.ycrcb422 = 1; | ||
| 220 | } | ||
| 221 | else | ||
| 222 | { | ||
| 223 | int bits = get_bits (edid[0x18], 3, 4); | ||
| 224 | ColorType color_type[4] = | ||
| 225 | { | ||
| 226 | MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR | ||
| 227 | }; | ||
| 228 | |||
| 229 | info->ad.analog.color_type = color_type[bits]; | ||
| 230 | } | ||
| 231 | |||
| 232 | info->srgb_is_standard = get_bit (edid[0x18], 2); | ||
| 233 | |||
| 234 | /* In 1.3 this is called "has preferred timing" */ | ||
| 235 | info->preferred_timing_includes_native = get_bit (edid[0x18], 1); | ||
| 236 | |||
| 237 | /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */ | ||
| 238 | info->continuous_frequency = get_bit (edid[0x18], 0); | ||
| 239 | return TRUE; | ||
| 240 | } | ||
| 241 | |||
| 242 | static double | ||
| 243 | decode_fraction (int high, int low) | ||
| 244 | { | ||
| 245 | double result = 0.0; | ||
| 246 | int i; | ||
| 247 | |||
| 248 | high = (high << 2) | low; | ||
| 249 | |||
| 250 | for (i = 0; i < 10; ++i) | ||
| 251 | result += get_bit (high, i) * SDL_pow (2, i - 10); | ||
| 252 | |||
| 253 | return result; | ||
| 254 | } | ||
| 255 | |||
| 256 | static int | ||
| 257 | decode_color_characteristics (const uchar *edid, MonitorInfo *info) | ||
| 258 | { | ||
| 259 | info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7)); | ||
| 260 | info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4)); | ||
| 261 | info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3)); | ||
| 262 | info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1)); | ||
| 263 | info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7)); | ||
| 264 | info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5)); | ||
| 265 | info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3)); | ||
| 266 | info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1)); | ||
| 267 | |||
| 268 | return TRUE; | ||
| 269 | } | ||
| 270 | |||
| 271 | static int | ||
| 272 | decode_established_timings (const uchar *edid, MonitorInfo *info) | ||
| 273 | { | ||
| 274 | static const Timing established[][8] = | ||
| 275 | { | ||
| 276 | { | ||
| 277 | { 800, 600, 60 }, | ||
| 278 | { 800, 600, 56 }, | ||
| 279 | { 640, 480, 75 }, | ||
| 280 | { 640, 480, 72 }, | ||
| 281 | { 640, 480, 67 }, | ||
| 282 | { 640, 480, 60 }, | ||
| 283 | { 720, 400, 88 }, | ||
| 284 | { 720, 400, 70 } | ||
| 285 | }, | ||
| 286 | { | ||
| 287 | { 1280, 1024, 75 }, | ||
| 288 | { 1024, 768, 75 }, | ||
| 289 | { 1024, 768, 70 }, | ||
| 290 | { 1024, 768, 60 }, | ||
| 291 | { 1024, 768, 87 }, | ||
| 292 | { 832, 624, 75 }, | ||
| 293 | { 800, 600, 75 }, | ||
| 294 | { 800, 600, 72 } | ||
| 295 | }, | ||
| 296 | { | ||
| 297 | { 0, 0, 0 }, | ||
| 298 | { 0, 0, 0 }, | ||
| 299 | { 0, 0, 0 }, | ||
| 300 | { 0, 0, 0 }, | ||
| 301 | { 0, 0, 0 }, | ||
| 302 | { 0, 0, 0 }, | ||
| 303 | { 0, 0, 0 }, | ||
| 304 | { 1152, 870, 75 } | ||
| 305 | }, | ||
| 306 | }; | ||
| 307 | |||
| 308 | int i, j, idx; | ||
| 309 | |||
| 310 | idx = 0; | ||
| 311 | for (i = 0; i < 3; ++i) | ||
| 312 | { | ||
| 313 | for (j = 0; j < 8; ++j) | ||
| 314 | { | ||
| 315 | int byte = edid[0x23 + i]; | ||
| 316 | |||
| 317 | if (get_bit (byte, j) && established[i][j].frequency != 0) | ||
| 318 | info->established[idx++] = established[i][j]; | ||
| 319 | } | ||
| 320 | } | ||
| 321 | return TRUE; | ||
| 322 | } | ||
| 323 | |||
| 324 | static int | ||
| 325 | decode_standard_timings (const uchar *edid, MonitorInfo *info) | ||
| 326 | { | ||
| 327 | int i; | ||
| 328 | |||
| 329 | for (i = 0; i < 8; i++) | ||
| 330 | { | ||
| 331 | int first = edid[0x26 + 2 * i]; | ||
| 332 | int second = edid[0x27 + 2 * i]; | ||
| 333 | |||
| 334 | if (first != 0x01 && second != 0x01) | ||
| 335 | { | ||
| 336 | int w = 8 * (first + 31); | ||
| 337 | int h = 0; | ||
| 338 | |||
| 339 | switch (get_bits (second, 6, 7)) | ||
| 340 | { | ||
| 341 | case 0x00: h = (w / 16) * 10; break; | ||
| 342 | case 0x01: h = (w / 4) * 3; break; | ||
| 343 | case 0x02: h = (w / 5) * 4; break; | ||
| 344 | case 0x03: h = (w / 16) * 9; break; | ||
| 345 | } | ||
| 346 | |||
| 347 | info->standard[i].width = w; | ||
| 348 | info->standard[i].height = h; | ||
| 349 | info->standard[i].frequency = get_bits (second, 0, 5) + 60; | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | return TRUE; | ||
| 354 | } | ||
| 355 | |||
| 356 | static void | ||
| 357 | decode_lf_string (const uchar *s, int n_chars, char *result) | ||
| 358 | { | ||
| 359 | int i; | ||
| 360 | for (i = 0; i < n_chars; ++i) | ||
| 361 | { | ||
| 362 | if (s[i] == 0x0a) | ||
| 363 | { | ||
| 364 | *result++ = '\0'; | ||
| 365 | break; | ||
| 366 | } | ||
| 367 | else if (s[i] == 0x00) | ||
| 368 | { | ||
| 369 | /* Convert embedded 0's to spaces */ | ||
| 370 | *result++ = ' '; | ||
| 371 | } | ||
| 372 | else | ||
| 373 | { | ||
| 374 | *result++ = s[i]; | ||
| 375 | } | ||
| 376 | } | ||
| 377 | } | ||
| 378 | |||
| 379 | static void | ||
| 380 | decode_display_descriptor (const uchar *desc, | ||
| 381 | MonitorInfo *info) | ||
| 382 | { | ||
| 383 | switch (desc[0x03]) | ||
| 384 | { | ||
| 385 | case 0xFC: | ||
| 386 | decode_lf_string (desc + 5, 13, info->dsc_product_name); | ||
| 387 | break; | ||
| 388 | case 0xFF: | ||
| 389 | decode_lf_string (desc + 5, 13, info->dsc_serial_number); | ||
| 390 | break; | ||
| 391 | case 0xFE: | ||
| 392 | decode_lf_string (desc + 5, 13, info->dsc_string); | ||
| 393 | break; | ||
| 394 | case 0xFD: | ||
| 395 | /* Range Limits */ | ||
| 396 | break; | ||
| 397 | case 0xFB: | ||
| 398 | /* Color Point */ | ||
| 399 | break; | ||
| 400 | case 0xFA: | ||
| 401 | /* Timing Identifications */ | ||
| 402 | break; | ||
| 403 | case 0xF9: | ||
| 404 | /* Color Management */ | ||
| 405 | break; | ||
| 406 | case 0xF8: | ||
| 407 | /* Timing Codes */ | ||
| 408 | break; | ||
| 409 | case 0xF7: | ||
| 410 | /* Established Timings */ | ||
| 411 | break; | ||
| 412 | case 0x10: | ||
| 413 | break; | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | static void | ||
| 418 | decode_detailed_timing (const uchar *timing, | ||
| 419 | DetailedTiming *detailed) | ||
| 420 | { | ||
| 421 | int bits; | ||
| 422 | StereoType stereo[] = | ||
| 423 | { | ||
| 424 | NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT, | ||
| 425 | TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, | ||
| 426 | FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE | ||
| 427 | }; | ||
| 428 | |||
| 429 | detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000; | ||
| 430 | detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4); | ||
| 431 | detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8); | ||
| 432 | detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4); | ||
| 433 | detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8); | ||
| 434 | detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8; | ||
| 435 | detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8; | ||
| 436 | detailed->v_front_porch = | ||
| 437 | get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4; | ||
| 438 | detailed->v_sync = | ||
| 439 | get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4; | ||
| 440 | detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8; | ||
| 441 | detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8; | ||
| 442 | detailed->right_border = timing[0x0f]; | ||
| 443 | detailed->top_border = timing[0x10]; | ||
| 444 | |||
| 445 | detailed->interlaced = get_bit (timing[0x11], 7); | ||
| 446 | |||
| 447 | /* Stereo */ | ||
| 448 | bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0); | ||
| 449 | detailed->stereo = stereo[bits]; | ||
| 450 | |||
| 451 | /* Sync */ | ||
| 452 | bits = timing[0x11]; | ||
| 453 | |||
| 454 | detailed->digital_sync = get_bit (bits, 4); | ||
| 455 | if (detailed->digital_sync) | ||
| 456 | { | ||
| 457 | detailed->ad.digital.composite = !get_bit (bits, 3); | ||
| 458 | |||
| 459 | if (detailed->ad.digital.composite) | ||
| 460 | { | ||
| 461 | detailed->ad.digital.serrations = get_bit (bits, 2); | ||
| 462 | detailed->ad.digital.negative_vsync = FALSE; | ||
| 463 | } | ||
| 464 | else | ||
| 465 | { | ||
| 466 | detailed->ad.digital.serrations = FALSE; | ||
| 467 | detailed->ad.digital.negative_vsync = !get_bit (bits, 2); | ||
| 468 | } | ||
| 469 | |||
| 470 | detailed->ad.digital.negative_hsync = !get_bit (bits, 0); | ||
| 471 | } | ||
| 472 | else | ||
| 473 | { | ||
| 474 | detailed->ad.analog.bipolar = get_bit (bits, 3); | ||
| 475 | detailed->ad.analog.serrations = get_bit (bits, 2); | ||
| 476 | detailed->ad.analog.sync_on_green = !get_bit (bits, 1); | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | static int | ||
| 481 | decode_descriptors (const uchar *edid, MonitorInfo *info) | ||
| 482 | { | ||
| 483 | int i; | ||
| 484 | int timing_idx; | ||
| 485 | |||
| 486 | timing_idx = 0; | ||
| 487 | |||
| 488 | for (i = 0; i < 4; ++i) | ||
| 489 | { | ||
| 490 | int index = 0x36 + i * 18; | ||
| 491 | |||
| 492 | if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) | ||
| 493 | { | ||
| 494 | decode_display_descriptor (edid + index, info); | ||
| 495 | } | ||
| 496 | else | ||
| 497 | { | ||
| 498 | decode_detailed_timing ( | ||
| 499 | edid + index, &(info->detailed_timings[timing_idx++])); | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | info->n_detailed_timings = timing_idx; | ||
| 504 | |||
| 505 | return TRUE; | ||
| 506 | } | ||
| 507 | |||
| 508 | static void | ||
| 509 | decode_check_sum (const uchar *edid, | ||
| 510 | MonitorInfo *info) | ||
| 511 | { | ||
| 512 | int i; | ||
| 513 | uchar check = 0; | ||
| 514 | |||
| 515 | for (i = 0; i < 128; ++i) | ||
| 516 | check += edid[i]; | ||
| 517 | |||
| 518 | info->checksum = check; | ||
| 519 | } | ||
| 520 | |||
| 521 | MonitorInfo * | ||
| 522 | decode_edid (const uchar *edid) | ||
| 523 | { | ||
| 524 | MonitorInfo *info = SDL_calloc (1, sizeof (MonitorInfo)); | ||
| 525 | |||
| 526 | decode_check_sum (edid, info); | ||
| 527 | |||
| 528 | if (!decode_header (edid) || | ||
| 529 | !decode_vendor_and_product_identification (edid, info) || | ||
| 530 | !decode_edid_version (edid, info) || | ||
| 531 | !decode_display_parameters (edid, info) || | ||
| 532 | !decode_color_characteristics (edid, info) || | ||
| 533 | !decode_established_timings (edid, info) || | ||
| 534 | !decode_standard_timings (edid, info) || | ||
| 535 | !decode_descriptors (edid, info)) { | ||
| 536 | SDL_free(info); | ||
| 537 | return NULL; | ||
| 538 | } | ||
| 539 | |||
| 540 | return info; | ||
| 541 | } | ||
| 542 | |||
| 543 | static const char * | ||
| 544 | yesno (int v) | ||
| 545 | { | ||
| 546 | return v? "yes" : "no"; | ||
| 547 | } | ||
| 548 | |||
| 549 | void | ||
| 550 | dump_monitor_info (MonitorInfo *info) | ||
| 551 | { | ||
| 552 | int i; | ||
| 553 | |||
| 554 | printf ("Checksum: %d (%s)\n", | ||
| 555 | info->checksum, info->checksum? "incorrect" : "correct"); | ||
| 556 | printf ("Manufacturer Code: %s\n", info->manufacturer_code); | ||
| 557 | printf ("Product Code: 0x%x\n", info->product_code); | ||
| 558 | printf ("Serial Number: %u\n", info->serial_number); | ||
| 559 | |||
| 560 | if (info->production_week != -1) | ||
| 561 | printf ("Production Week: %d\n", info->production_week); | ||
| 562 | else | ||
| 563 | printf ("Production Week: unspecified\n"); | ||
| 564 | |||
| 565 | if (info->production_year != -1) | ||
| 566 | printf ("Production Year: %d\n", info->production_year); | ||
| 567 | else | ||
| 568 | printf ("Production Year: unspecified\n"); | ||
| 569 | |||
| 570 | if (info->model_year != -1) | ||
| 571 | printf ("Model Year: %d\n", info->model_year); | ||
| 572 | else | ||
| 573 | printf ("Model Year: unspecified\n"); | ||
| 574 | |||
| 575 | printf ("EDID revision: %d.%d\n", info->major_version, info->minor_version); | ||
| 576 | |||
| 577 | printf ("Display is %s\n", info->is_digital? "digital" : "analog"); | ||
| 578 | if (info->is_digital) | ||
| 579 | { | ||
| 580 | const char *interface; | ||
| 581 | if (info->ad.digital.bits_per_primary != -1) | ||
| 582 | printf ("Bits Per Primary: %d\n", info->ad.digital.bits_per_primary); | ||
| 583 | else | ||
| 584 | printf ("Bits Per Primary: undefined\n"); | ||
| 585 | |||
| 586 | switch (info->ad.digital.interface) | ||
| 587 | { | ||
| 588 | case DVI: interface = "DVI"; break; | ||
| 589 | case HDMI_A: interface = "HDMI-a"; break; | ||
| 590 | case HDMI_B: interface = "HDMI-b"; break; | ||
| 591 | case MDDI: interface = "MDDI"; break; | ||
| 592 | case DISPLAY_PORT: interface = "DisplayPort"; break; | ||
| 593 | case UNDEFINED: interface = "undefined"; break; | ||
| 594 | default: interface = "unknown"; break; | ||
| 595 | } | ||
| 596 | printf ("Interface: %s\n", interface); | ||
| 597 | |||
| 598 | printf ("RGB 4:4:4: %s\n", yesno (info->ad.digital.rgb444)); | ||
| 599 | printf ("YCrCb 4:4:4: %s\n", yesno (info->ad.digital.ycrcb444)); | ||
| 600 | printf ("YCrCb 4:2:2: %s\n", yesno (info->ad.digital.ycrcb422)); | ||
| 601 | } | ||
| 602 | else | ||
| 603 | { | ||
| 604 | const char *s; | ||
| 605 | printf ("Video Signal Level: %f\n", info->ad.analog.video_signal_level); | ||
| 606 | printf ("Sync Signal Level: %f\n", info->ad.analog.sync_signal_level); | ||
| 607 | printf ("Total Signal Level: %f\n", info->ad.analog.total_signal_level); | ||
| 608 | |||
| 609 | printf ("Blank to Black: %s\n", | ||
| 610 | yesno (info->ad.analog.blank_to_black)); | ||
| 611 | printf ("Separate HV Sync: %s\n", | ||
| 612 | yesno (info->ad.analog.separate_hv_sync)); | ||
| 613 | printf ("Composite Sync on H: %s\n", | ||
| 614 | yesno (info->ad.analog.composite_sync_on_h)); | ||
| 615 | printf ("Serration on VSync: %s\n", | ||
| 616 | yesno (info->ad.analog.serration_on_vsync)); | ||
| 617 | |||
| 618 | switch (info->ad.analog.color_type) | ||
| 619 | { | ||
| 620 | case UNDEFINED_COLOR: s = "undefined"; break; | ||
| 621 | case MONOCHROME: s = "monochrome"; break; | ||
| 622 | case RGB: s = "rgb"; break; | ||
| 623 | case OTHER_COLOR: s = "other color"; break; | ||
| 624 | default: s = "unknown"; break; | ||
| 625 | } | ||
| 626 | |||
| 627 | printf ("Color: %s\n", s); | ||
| 628 | } | ||
| 629 | |||
| 630 | if (info->width_mm == -1) | ||
| 631 | printf ("Width: undefined\n"); | ||
| 632 | else | ||
| 633 | printf ("Width: %d mm\n", info->width_mm); | ||
| 634 | |||
| 635 | if (info->height_mm == -1) | ||
| 636 | printf ("Height: undefined\n"); | ||
| 637 | else | ||
| 638 | printf ("Height: %d mm\n", info->height_mm); | ||
| 639 | |||
| 640 | if (info->aspect_ratio > 0) | ||
| 641 | printf ("Aspect Ratio: %f\n", info->aspect_ratio); | ||
| 642 | else | ||
| 643 | printf ("Aspect Ratio: undefined\n"); | ||
| 644 | |||
| 645 | if (info->gamma >= 0) | ||
| 646 | printf ("Gamma: %f\n", info->gamma); | ||
| 647 | else | ||
| 648 | printf ("Gamma: undefined\n"); | ||
| 649 | |||
| 650 | printf ("Standby: %s\n", yesno (info->standby)); | ||
| 651 | printf ("Suspend: %s\n", yesno (info->suspend)); | ||
| 652 | printf ("Active Off: %s\n", yesno (info->active_off)); | ||
| 653 | |||
| 654 | printf ("SRGB is Standard: %s\n", yesno (info->srgb_is_standard)); | ||
| 655 | printf ("Preferred Timing Includes Native: %s\n", | ||
| 656 | yesno (info->preferred_timing_includes_native)); | ||
| 657 | printf ("Continuous Frequency: %s\n", yesno (info->continuous_frequency)); | ||
| 658 | |||
| 659 | printf ("Red X: %f\n", info->red_x); | ||
| 660 | printf ("Red Y: %f\n", info->red_y); | ||
| 661 | printf ("Green X: %f\n", info->green_x); | ||
| 662 | printf ("Green Y: %f\n", info->green_y); | ||
| 663 | printf ("Blue X: %f\n", info->blue_x); | ||
| 664 | printf ("Blue Y: %f\n", info->blue_y); | ||
| 665 | printf ("White X: %f\n", info->white_x); | ||
| 666 | printf ("White Y: %f\n", info->white_y); | ||
| 667 | |||
| 668 | printf ("Established Timings:\n"); | ||
| 669 | |||
| 670 | for (i = 0; i < 24; ++i) | ||
| 671 | { | ||
| 672 | Timing *timing = &(info->established[i]); | ||
| 673 | |||
| 674 | if (timing->frequency == 0) | ||
| 675 | break; | ||
| 676 | |||
| 677 | printf (" %d x %d @ %d Hz\n", | ||
| 678 | timing->width, timing->height, timing->frequency); | ||
| 679 | |||
| 680 | } | ||
| 681 | |||
| 682 | printf ("Standard Timings:\n"); | ||
| 683 | for (i = 0; i < 8; ++i) | ||
| 684 | { | ||
| 685 | Timing *timing = &(info->standard[i]); | ||
| 686 | |||
| 687 | if (timing->frequency == 0) | ||
| 688 | break; | ||
| 689 | |||
| 690 | printf (" %d x %d @ %d Hz\n", | ||
| 691 | timing->width, timing->height, timing->frequency); | ||
| 692 | } | ||
| 693 | |||
| 694 | for (i = 0; i < info->n_detailed_timings; ++i) | ||
| 695 | { | ||
| 696 | DetailedTiming *timing = &(info->detailed_timings[i]); | ||
| 697 | const char *s; | ||
| 698 | |||
| 699 | printf ("Timing%s: \n", | ||
| 700 | (i == 0 && info->preferred_timing_includes_native)? | ||
| 701 | " (Preferred)" : ""); | ||
| 702 | printf (" Pixel Clock: %d\n", timing->pixel_clock); | ||
| 703 | printf (" H Addressable: %d\n", timing->h_addr); | ||
| 704 | printf (" H Blank: %d\n", timing->h_blank); | ||
| 705 | printf (" H Front Porch: %d\n", timing->h_front_porch); | ||
| 706 | printf (" H Sync: %d\n", timing->h_sync); | ||
| 707 | printf (" V Addressable: %d\n", timing->v_addr); | ||
| 708 | printf (" V Blank: %d\n", timing->v_blank); | ||
| 709 | printf (" V Front Porch: %d\n", timing->v_front_porch); | ||
| 710 | printf (" V Sync: %d\n", timing->v_sync); | ||
| 711 | printf (" Width: %d mm\n", timing->width_mm); | ||
| 712 | printf (" Height: %d mm\n", timing->height_mm); | ||
| 713 | printf (" Right Border: %d\n", timing->right_border); | ||
| 714 | printf (" Top Border: %d\n", timing->top_border); | ||
| 715 | switch (timing->stereo) | ||
| 716 | { | ||
| 717 | default: | ||
| 718 | case NO_STEREO: s = "No Stereo"; break; | ||
| 719 | case FIELD_RIGHT: s = "Field Sequential, Right on Sync"; break; | ||
| 720 | case FIELD_LEFT: s = "Field Sequential, Left on Sync"; break; | ||
| 721 | case TWO_WAY_RIGHT_ON_EVEN: s = "Two-way, Right on Even"; break; | ||
| 722 | case TWO_WAY_LEFT_ON_EVEN: s = "Two-way, Left on Even"; break; | ||
| 723 | case FOUR_WAY_INTERLEAVED: s = "Four-way Interleaved"; break; | ||
| 724 | case SIDE_BY_SIDE: s = "Side-by-Side"; break; | ||
| 725 | } | ||
| 726 | printf (" Stereo: %s\n", s); | ||
| 727 | |||
| 728 | if (timing->digital_sync) | ||
| 729 | { | ||
| 730 | printf (" Digital Sync:\n"); | ||
| 731 | printf (" composite: %s\n", yesno (timing->ad.digital.composite)); | ||
| 732 | printf (" serrations: %s\n", yesno (timing->ad.digital.serrations)); | ||
| 733 | printf (" negative vsync: %s\n", | ||
| 734 | yesno (timing->ad.digital.negative_vsync)); | ||
| 735 | printf (" negative hsync: %s\n", | ||
| 736 | yesno (timing->ad.digital.negative_hsync)); | ||
| 737 | } | ||
| 738 | else | ||
| 739 | { | ||
| 740 | printf (" Analog Sync:\n"); | ||
| 741 | printf (" bipolar: %s\n", yesno (timing->ad.analog.bipolar)); | ||
| 742 | printf (" serrations: %s\n", yesno (timing->ad.analog.serrations)); | ||
| 743 | printf (" sync on green: %s\n", yesno ( | ||
| 744 | timing->ad.analog.sync_on_green)); | ||
| 745 | } | ||
| 746 | } | ||
| 747 | |||
| 748 | printf ("Detailed Product information:\n"); | ||
| 749 | printf (" Product Name: %s\n", info->dsc_product_name); | ||
| 750 | printf (" Serial Number: %s\n", info->dsc_serial_number); | ||
| 751 | printf (" Unspecified String: %s\n", info->dsc_string); | ||
| 752 | } | ||
| 753 | |||
diff --git a/contrib/SDL-3.2.8/src/video/x11/edid.h b/contrib/SDL-3.2.8/src/video/x11/edid.h new file mode 100644 index 0000000..4581291 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/edid.h | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2007 Red Hat, Inc. | ||
| 3 | * | ||
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
| 5 | * copy of this software and associated documentation files (the "Software"), | ||
| 6 | * to deal in the Software without restriction, including without limitation | ||
| 7 | * on the rights to use, copy, modify, merge, publish, distribute, sub | ||
| 8 | * license, and/or sell copies of the Software, and to permit persons to whom | ||
| 9 | * the Software is furnished to do so, subject to the following conditions: | ||
| 10 | * | ||
| 11 | * The above copyright notice and this permission notice (including the next | ||
| 12 | * paragraph) shall be included in all copies or substantial portions of the | ||
| 13 | * Software. | ||
| 14 | * | ||
| 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
| 18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
| 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
| 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* Author: Soren Sandmann <sandmann@redhat.com> */ | ||
| 24 | |||
| 25 | typedef unsigned char uchar; | ||
| 26 | typedef struct MonitorInfo MonitorInfo; | ||
| 27 | typedef struct Timing Timing; | ||
| 28 | typedef struct DetailedTiming DetailedTiming; | ||
| 29 | |||
| 30 | typedef enum | ||
| 31 | { | ||
| 32 | UNDEFINED, | ||
| 33 | DVI, | ||
| 34 | HDMI_A, | ||
| 35 | HDMI_B, | ||
| 36 | MDDI, | ||
| 37 | DISPLAY_PORT | ||
| 38 | } Interface; | ||
| 39 | |||
| 40 | typedef enum | ||
| 41 | { | ||
| 42 | UNDEFINED_COLOR, | ||
| 43 | MONOCHROME, | ||
| 44 | RGB, | ||
| 45 | OTHER_COLOR | ||
| 46 | } ColorType; | ||
| 47 | |||
| 48 | typedef enum | ||
| 49 | { | ||
| 50 | NO_STEREO, | ||
| 51 | FIELD_RIGHT, | ||
| 52 | FIELD_LEFT, | ||
| 53 | TWO_WAY_RIGHT_ON_EVEN, | ||
| 54 | TWO_WAY_LEFT_ON_EVEN, | ||
| 55 | FOUR_WAY_INTERLEAVED, | ||
| 56 | SIDE_BY_SIDE | ||
| 57 | } StereoType; | ||
| 58 | |||
| 59 | struct Timing | ||
| 60 | { | ||
| 61 | int width; | ||
| 62 | int height; | ||
| 63 | int frequency; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct DetailedTiming | ||
| 67 | { | ||
| 68 | int pixel_clock; | ||
| 69 | int h_addr; | ||
| 70 | int h_blank; | ||
| 71 | int h_sync; | ||
| 72 | int h_front_porch; | ||
| 73 | int v_addr; | ||
| 74 | int v_blank; | ||
| 75 | int v_sync; | ||
| 76 | int v_front_porch; | ||
| 77 | int width_mm; | ||
| 78 | int height_mm; | ||
| 79 | int right_border; | ||
| 80 | int top_border; | ||
| 81 | int interlaced; | ||
| 82 | StereoType stereo; | ||
| 83 | |||
| 84 | int digital_sync; | ||
| 85 | union | ||
| 86 | { | ||
| 87 | struct | ||
| 88 | { | ||
| 89 | int bipolar; | ||
| 90 | int serrations; | ||
| 91 | int sync_on_green; | ||
| 92 | } analog; | ||
| 93 | |||
| 94 | struct | ||
| 95 | { | ||
| 96 | int composite; | ||
| 97 | int serrations; | ||
| 98 | int negative_vsync; | ||
| 99 | int negative_hsync; | ||
| 100 | } digital; | ||
| 101 | } ad; | ||
| 102 | }; | ||
| 103 | |||
| 104 | struct MonitorInfo | ||
| 105 | { | ||
| 106 | int checksum; | ||
| 107 | char manufacturer_code[4]; | ||
| 108 | int product_code; | ||
| 109 | unsigned int serial_number; | ||
| 110 | |||
| 111 | int production_week; // -1 if not specified | ||
| 112 | int production_year; // -1 if not specified | ||
| 113 | int model_year; // -1 if not specified | ||
| 114 | |||
| 115 | int major_version; | ||
| 116 | int minor_version; | ||
| 117 | |||
| 118 | int is_digital; | ||
| 119 | |||
| 120 | union | ||
| 121 | { | ||
| 122 | struct | ||
| 123 | { | ||
| 124 | int bits_per_primary; | ||
| 125 | Interface interface; | ||
| 126 | int rgb444; | ||
| 127 | int ycrcb444; | ||
| 128 | int ycrcb422; | ||
| 129 | } digital; | ||
| 130 | |||
| 131 | struct | ||
| 132 | { | ||
| 133 | double video_signal_level; | ||
| 134 | double sync_signal_level; | ||
| 135 | double total_signal_level; | ||
| 136 | |||
| 137 | int blank_to_black; | ||
| 138 | |||
| 139 | int separate_hv_sync; | ||
| 140 | int composite_sync_on_h; | ||
| 141 | int composite_sync_on_green; | ||
| 142 | int serration_on_vsync; | ||
| 143 | ColorType color_type; | ||
| 144 | } analog; | ||
| 145 | } ad; | ||
| 146 | |||
| 147 | int width_mm; // -1 if not specified | ||
| 148 | int height_mm; // -1 if not specified | ||
| 149 | double aspect_ratio; // -1.0 if not specififed | ||
| 150 | |||
| 151 | double gamma; // -1.0 if not specified | ||
| 152 | |||
| 153 | int standby; | ||
| 154 | int suspend; | ||
| 155 | int active_off; | ||
| 156 | |||
| 157 | int srgb_is_standard; | ||
| 158 | int preferred_timing_includes_native; | ||
| 159 | int continuous_frequency; | ||
| 160 | |||
| 161 | double red_x; | ||
| 162 | double red_y; | ||
| 163 | double green_x; | ||
| 164 | double green_y; | ||
| 165 | double blue_x; | ||
| 166 | double blue_y; | ||
| 167 | double white_x; | ||
| 168 | double white_y; | ||
| 169 | |||
| 170 | Timing established[24]; // Terminated by 0x0x0 | ||
| 171 | Timing standard[8]; | ||
| 172 | |||
| 173 | int n_detailed_timings; | ||
| 174 | DetailedTiming detailed_timings[4]; /* If monitor has a preferred | ||
| 175 | * mode, it is the first one | ||
| 176 | * (whether it has, is | ||
| 177 | * determined by the | ||
| 178 | * preferred_timing_includes | ||
| 179 | * bit. | ||
| 180 | */ | ||
| 181 | |||
| 182 | // Optional product description | ||
| 183 | char dsc_serial_number[14]; | ||
| 184 | char dsc_product_name[14]; | ||
| 185 | char dsc_string[14]; // Unspecified ASCII data | ||
| 186 | }; | ||
| 187 | |||
| 188 | MonitorInfo *decode_edid(const uchar *data); | ||
| 189 | void dump_monitor_info(MonitorInfo *info); | ||
| 190 | char *make_display_name(const char *output_name, | ||
| 191 | const MonitorInfo *info); | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c new file mode 100644 index 0000000..8fb1cd3 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.c | |||
| @@ -0,0 +1,859 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2001, 2007 Red Hat, Inc. | ||
| 3 | * Copyright 2024 Igalia S.L. | ||
| 4 | * | ||
| 5 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
| 6 | * documentation for any purpose is hereby granted without fee, provided that | ||
| 7 | * the above copyright notice appear in all copies and that both that | ||
| 8 | * copyright notice and this permission notice appear in supporting | ||
| 9 | * documentation, and that the name of Red Hat not be used in advertising or | ||
| 10 | * publicity pertaining to distribution of the software without specific, | ||
| 11 | * written prior permission. Red Hat makes no representations about the | ||
| 12 | * suitability of this software for any purpose. It is provided "as is" | ||
| 13 | * without express or implied warranty. | ||
| 14 | * | ||
| 15 | * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL | ||
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT | ||
| 17 | * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
| 19 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
| 20 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 21 | * | ||
| 22 | * Author: Owen Taylor, Red Hat, Inc. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include "SDL_internal.h" | ||
| 26 | |||
| 27 | #ifdef SDL_VIDEO_DRIVER_X11 | ||
| 28 | |||
| 29 | #include "SDL_x11video.h" | ||
| 30 | |||
| 31 | #include <limits.h> | ||
| 32 | #include <stdio.h> | ||
| 33 | #include <stdlib.h> | ||
| 34 | #include <string.h> | ||
| 35 | |||
| 36 | #include "xsettings-client.h" | ||
| 37 | |||
| 38 | struct _XSettingsClient | ||
| 39 | { | ||
| 40 | Display *display; | ||
| 41 | int screen; | ||
| 42 | XSettingsNotifyFunc notify; | ||
| 43 | XSettingsWatchFunc watch; | ||
| 44 | void *cb_data; | ||
| 45 | |||
| 46 | XSettingsGrabFunc grab; | ||
| 47 | XSettingsGrabFunc ungrab; | ||
| 48 | |||
| 49 | Window manager_window; | ||
| 50 | Atom manager_atom; | ||
| 51 | Atom selection_atom; | ||
| 52 | Atom xsettings_atom; | ||
| 53 | |||
| 54 | XSettingsList *settings; | ||
| 55 | }; | ||
| 56 | |||
| 57 | static void | ||
| 58 | notify_changes (XSettingsClient *client, | ||
| 59 | XSettingsList *old_list) | ||
| 60 | { | ||
| 61 | XSettingsList *old_iter = old_list; | ||
| 62 | XSettingsList *new_iter = client->settings; | ||
| 63 | |||
| 64 | if (!client->notify) | ||
| 65 | return; | ||
| 66 | |||
| 67 | while (old_iter || new_iter) | ||
| 68 | { | ||
| 69 | int cmp; | ||
| 70 | |||
| 71 | if (old_iter && new_iter) | ||
| 72 | cmp = strcmp (old_iter->setting->name, new_iter->setting->name); | ||
| 73 | else if (old_iter) | ||
| 74 | cmp = -1; | ||
| 75 | else | ||
| 76 | cmp = 1; | ||
| 77 | |||
| 78 | if (cmp < 0) | ||
| 79 | { | ||
| 80 | client->notify (old_iter->setting->name, | ||
| 81 | XSETTINGS_ACTION_DELETED, | ||
| 82 | NULL, | ||
| 83 | client->cb_data); | ||
| 84 | } | ||
| 85 | else if (cmp == 0) | ||
| 86 | { | ||
| 87 | if (!xsettings_setting_equal (old_iter->setting, | ||
| 88 | new_iter->setting)) | ||
| 89 | client->notify (old_iter->setting->name, | ||
| 90 | XSETTINGS_ACTION_CHANGED, | ||
| 91 | new_iter->setting, | ||
| 92 | client->cb_data); | ||
| 93 | } | ||
| 94 | else | ||
| 95 | { | ||
| 96 | client->notify (new_iter->setting->name, | ||
| 97 | XSETTINGS_ACTION_NEW, | ||
| 98 | new_iter->setting, | ||
| 99 | client->cb_data); | ||
| 100 | } | ||
| 101 | |||
| 102 | if (old_iter) | ||
| 103 | old_iter = old_iter->next; | ||
| 104 | if (new_iter) | ||
| 105 | new_iter = new_iter->next; | ||
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | static int | ||
| 110 | ignore_errors (Display *display, XErrorEvent *event) | ||
| 111 | { | ||
| 112 | return True; | ||
| 113 | } | ||
| 114 | |||
| 115 | static char local_byte_order = '\0'; | ||
| 116 | |||
| 117 | #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos) | ||
| 118 | |||
| 119 | static XSettingsResult | ||
| 120 | fetch_card16 (XSettingsBuffer *buffer, | ||
| 121 | CARD16 *result) | ||
| 122 | { | ||
| 123 | CARD16 x; | ||
| 124 | |||
| 125 | if (BYTES_LEFT (buffer) < 2) | ||
| 126 | return XSETTINGS_ACCESS; | ||
| 127 | |||
| 128 | x = *(CARD16 *)buffer->pos; | ||
| 129 | buffer->pos += 2; | ||
| 130 | |||
| 131 | if (buffer->byte_order == local_byte_order) | ||
| 132 | *result = x; | ||
| 133 | else | ||
| 134 | *result = (x << 8) | (x >> 8); | ||
| 135 | |||
| 136 | return XSETTINGS_SUCCESS; | ||
| 137 | } | ||
| 138 | |||
| 139 | static XSettingsResult | ||
| 140 | fetch_ushort (XSettingsBuffer *buffer, | ||
| 141 | unsigned short *result) | ||
| 142 | { | ||
| 143 | CARD16 x; | ||
| 144 | XSettingsResult r; | ||
| 145 | |||
| 146 | r = fetch_card16 (buffer, &x); | ||
| 147 | if (r == XSETTINGS_SUCCESS) | ||
| 148 | *result = x; | ||
| 149 | |||
| 150 | return r; | ||
| 151 | } | ||
| 152 | |||
| 153 | static XSettingsResult | ||
| 154 | fetch_card32 (XSettingsBuffer *buffer, | ||
| 155 | CARD32 *result) | ||
| 156 | { | ||
| 157 | CARD32 x; | ||
| 158 | |||
| 159 | if (BYTES_LEFT (buffer) < 4) | ||
| 160 | return XSETTINGS_ACCESS; | ||
| 161 | |||
| 162 | x = *(CARD32 *)buffer->pos; | ||
| 163 | buffer->pos += 4; | ||
| 164 | |||
| 165 | if (buffer->byte_order == local_byte_order) | ||
| 166 | *result = x; | ||
| 167 | else | ||
| 168 | *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); | ||
| 169 | |||
| 170 | return XSETTINGS_SUCCESS; | ||
| 171 | } | ||
| 172 | |||
| 173 | static XSettingsResult | ||
| 174 | fetch_card8 (XSettingsBuffer *buffer, | ||
| 175 | CARD8 *result) | ||
| 176 | { | ||
| 177 | if (BYTES_LEFT (buffer) < 1) | ||
| 178 | return XSETTINGS_ACCESS; | ||
| 179 | |||
| 180 | *result = *(CARD8 *)buffer->pos; | ||
| 181 | buffer->pos += 1; | ||
| 182 | |||
| 183 | return XSETTINGS_SUCCESS; | ||
| 184 | } | ||
| 185 | |||
| 186 | #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) | ||
| 187 | |||
| 188 | static XSettingsList * | ||
| 189 | parse_settings (unsigned char *data, | ||
| 190 | size_t len) | ||
| 191 | { | ||
| 192 | XSettingsBuffer buffer; | ||
| 193 | XSettingsResult result = XSETTINGS_SUCCESS; | ||
| 194 | XSettingsList *settings = NULL; | ||
| 195 | CARD32 serial; | ||
| 196 | CARD32 n_entries; | ||
| 197 | CARD32 i; | ||
| 198 | XSettingsSetting *setting = NULL; | ||
| 199 | char buffer_byte_order = '\0'; | ||
| 200 | |||
| 201 | local_byte_order = xsettings_byte_order (); | ||
| 202 | |||
| 203 | buffer.pos = buffer.data = data; | ||
| 204 | buffer.len = len; | ||
| 205 | buffer.byte_order = '\0'; | ||
| 206 | |||
| 207 | result = fetch_card8 (&buffer, (unsigned char *) &buffer_byte_order); | ||
| 208 | if (buffer_byte_order != MSBFirst && | ||
| 209 | buffer_byte_order != LSBFirst) | ||
| 210 | { | ||
| 211 | fprintf (stderr, "Invalid byte order in XSETTINGS property\n"); | ||
| 212 | result = XSETTINGS_FAILED; | ||
| 213 | goto out; | ||
| 214 | } | ||
| 215 | |||
| 216 | buffer.byte_order = buffer_byte_order; | ||
| 217 | buffer.pos += 3; | ||
| 218 | |||
| 219 | result = fetch_card32 (&buffer, &serial); | ||
| 220 | if (result != XSETTINGS_SUCCESS) | ||
| 221 | goto out; | ||
| 222 | |||
| 223 | result = fetch_card32 (&buffer, &n_entries); | ||
| 224 | if (result != XSETTINGS_SUCCESS) | ||
| 225 | goto out; | ||
| 226 | |||
| 227 | for (i = 0; i < n_entries; i++) | ||
| 228 | { | ||
| 229 | CARD8 type; | ||
| 230 | CARD16 name_len; | ||
| 231 | CARD32 v_int; | ||
| 232 | size_t pad_len; | ||
| 233 | |||
| 234 | result = fetch_card8 (&buffer, &type); | ||
| 235 | if (result != XSETTINGS_SUCCESS) | ||
| 236 | goto out; | ||
| 237 | |||
| 238 | buffer.pos += 1; | ||
| 239 | |||
| 240 | result = fetch_card16 (&buffer, &name_len); | ||
| 241 | if (result != XSETTINGS_SUCCESS) | ||
| 242 | goto out; | ||
| 243 | |||
| 244 | pad_len = XSETTINGS_PAD(name_len, 4); | ||
| 245 | if (BYTES_LEFT (&buffer) < pad_len) | ||
| 246 | { | ||
| 247 | result = XSETTINGS_ACCESS; | ||
| 248 | goto out; | ||
| 249 | } | ||
| 250 | |||
| 251 | setting = malloc (sizeof *setting); | ||
| 252 | if (!setting) | ||
| 253 | { | ||
| 254 | result = XSETTINGS_NO_MEM; | ||
| 255 | goto out; | ||
| 256 | } | ||
| 257 | setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */ | ||
| 258 | |||
| 259 | setting->name = malloc (name_len + 1); | ||
| 260 | if (!setting->name) | ||
| 261 | { | ||
| 262 | result = XSETTINGS_NO_MEM; | ||
| 263 | goto out; | ||
| 264 | } | ||
| 265 | |||
| 266 | memcpy (setting->name, buffer.pos, name_len); | ||
| 267 | setting->name[name_len] = '\0'; | ||
| 268 | buffer.pos += pad_len; | ||
| 269 | |||
| 270 | result = fetch_card32 (&buffer, &v_int); | ||
| 271 | if (result != XSETTINGS_SUCCESS) | ||
| 272 | goto out; | ||
| 273 | setting->last_change_serial = v_int; | ||
| 274 | |||
| 275 | switch (type) | ||
| 276 | { | ||
| 277 | case XSETTINGS_TYPE_INT: | ||
| 278 | result = fetch_card32 (&buffer, &v_int); | ||
| 279 | if (result != XSETTINGS_SUCCESS) | ||
| 280 | goto out; | ||
| 281 | |||
| 282 | setting->data.v_int = (INT32)v_int; | ||
| 283 | break; | ||
| 284 | case XSETTINGS_TYPE_STRING: | ||
| 285 | result = fetch_card32 (&buffer, &v_int); | ||
| 286 | if (result != XSETTINGS_SUCCESS) | ||
| 287 | goto out; | ||
| 288 | |||
| 289 | pad_len = XSETTINGS_PAD (v_int, 4); | ||
| 290 | if (v_int + 1 == 0 || /* Guard against wrap-around */ | ||
| 291 | BYTES_LEFT (&buffer) < pad_len) | ||
| 292 | { | ||
| 293 | result = XSETTINGS_ACCESS; | ||
| 294 | goto out; | ||
| 295 | } | ||
| 296 | |||
| 297 | setting->data.v_string = malloc (v_int + 1); | ||
| 298 | if (!setting->data.v_string) | ||
| 299 | { | ||
| 300 | result = XSETTINGS_NO_MEM; | ||
| 301 | goto out; | ||
| 302 | } | ||
| 303 | |||
| 304 | memcpy (setting->data.v_string, buffer.pos, v_int); | ||
| 305 | setting->data.v_string[v_int] = '\0'; | ||
| 306 | buffer.pos += pad_len; | ||
| 307 | |||
| 308 | break; | ||
| 309 | case XSETTINGS_TYPE_COLOR: | ||
| 310 | result = fetch_ushort (&buffer, &setting->data.v_color.red); | ||
| 311 | if (result != XSETTINGS_SUCCESS) | ||
| 312 | goto out; | ||
| 313 | result = fetch_ushort (&buffer, &setting->data.v_color.green); | ||
| 314 | if (result != XSETTINGS_SUCCESS) | ||
| 315 | goto out; | ||
| 316 | result = fetch_ushort (&buffer, &setting->data.v_color.blue); | ||
| 317 | if (result != XSETTINGS_SUCCESS) | ||
| 318 | goto out; | ||
| 319 | result = fetch_ushort (&buffer, &setting->data.v_color.alpha); | ||
| 320 | if (result != XSETTINGS_SUCCESS) | ||
| 321 | goto out; | ||
| 322 | |||
| 323 | break; | ||
| 324 | default: | ||
| 325 | /* Quietly ignore unknown types */ | ||
| 326 | break; | ||
| 327 | } | ||
| 328 | |||
| 329 | setting->type = type; | ||
| 330 | |||
| 331 | result = xsettings_list_insert (&settings, setting); | ||
| 332 | if (result != XSETTINGS_SUCCESS) | ||
| 333 | goto out; | ||
| 334 | |||
| 335 | setting = NULL; | ||
| 336 | } | ||
| 337 | |||
| 338 | out: | ||
| 339 | |||
| 340 | if (result != XSETTINGS_SUCCESS) | ||
| 341 | { | ||
| 342 | switch (result) | ||
| 343 | { | ||
| 344 | case XSETTINGS_NO_MEM: | ||
| 345 | fprintf(stderr, "Out of memory reading XSETTINGS property\n"); | ||
| 346 | break; | ||
| 347 | case XSETTINGS_ACCESS: | ||
| 348 | fprintf(stderr, "Invalid XSETTINGS property (read off end)\n"); | ||
| 349 | break; | ||
| 350 | case XSETTINGS_DUPLICATE_ENTRY: | ||
| 351 | fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name); | ||
| 352 | SDL_FALLTHROUGH; | ||
| 353 | case XSETTINGS_FAILED: | ||
| 354 | SDL_FALLTHROUGH; | ||
| 355 | case XSETTINGS_SUCCESS: | ||
| 356 | SDL_FALLTHROUGH; | ||
| 357 | case XSETTINGS_NO_ENTRY: | ||
| 358 | break; | ||
| 359 | } | ||
| 360 | |||
| 361 | if (setting) | ||
| 362 | xsettings_setting_free (setting); | ||
| 363 | |||
| 364 | xsettings_list_free (settings); | ||
| 365 | settings = NULL; | ||
| 366 | |||
| 367 | } | ||
| 368 | |||
| 369 | return settings; | ||
| 370 | } | ||
| 371 | |||
| 372 | static void | ||
| 373 | read_settings (XSettingsClient *client) | ||
| 374 | { | ||
| 375 | Atom type; | ||
| 376 | int format; | ||
| 377 | unsigned long n_items; | ||
| 378 | unsigned long bytes_after; | ||
| 379 | unsigned char *data; | ||
| 380 | int result; | ||
| 381 | |||
| 382 | int (*old_handler) (Display *, XErrorEvent *); | ||
| 383 | |||
| 384 | XSettingsList *old_list = client->settings; | ||
| 385 | |||
| 386 | client->settings = NULL; | ||
| 387 | |||
| 388 | if (client->manager_window) | ||
| 389 | { | ||
| 390 | old_handler = X11_XSetErrorHandler (ignore_errors); | ||
| 391 | result = X11_XGetWindowProperty (client->display, client->manager_window, | ||
| 392 | client->xsettings_atom, 0, LONG_MAX, | ||
| 393 | False, client->xsettings_atom, | ||
| 394 | &type, &format, &n_items, &bytes_after, &data); | ||
| 395 | X11_XSetErrorHandler (old_handler); | ||
| 396 | |||
| 397 | if (result == Success && type != None) | ||
| 398 | { | ||
| 399 | if (type != client->xsettings_atom) | ||
| 400 | { | ||
| 401 | fprintf (stderr, "Invalid type for XSETTINGS property"); | ||
| 402 | } | ||
| 403 | else if (format != 8) | ||
| 404 | { | ||
| 405 | fprintf (stderr, "Invalid format for XSETTINGS property %d", format); | ||
| 406 | } | ||
| 407 | else | ||
| 408 | client->settings = parse_settings (data, n_items); | ||
| 409 | |||
| 410 | X11_XFree (data); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | notify_changes (client, old_list); | ||
| 415 | xsettings_list_free (old_list); | ||
| 416 | } | ||
| 417 | |||
| 418 | static void | ||
| 419 | add_events (Display *display, | ||
| 420 | Window window, | ||
| 421 | long mask) | ||
| 422 | { | ||
| 423 | XWindowAttributes attr; | ||
| 424 | |||
| 425 | X11_XGetWindowAttributes (display, window, &attr); | ||
| 426 | X11_XSelectInput (display, window, attr.your_event_mask | mask); | ||
| 427 | } | ||
| 428 | |||
| 429 | static void | ||
| 430 | check_manager_window (XSettingsClient *client) | ||
| 431 | { | ||
| 432 | if (client->manager_window && client->watch) | ||
| 433 | client->watch (client->manager_window, False, 0, client->cb_data); | ||
| 434 | |||
| 435 | if (client->grab) | ||
| 436 | client->grab (client->display); | ||
| 437 | else | ||
| 438 | X11_XGrabServer (client->display); | ||
| 439 | |||
| 440 | client->manager_window = X11_XGetSelectionOwner (client->display, | ||
| 441 | client->selection_atom); | ||
| 442 | if (client->manager_window) | ||
| 443 | X11_XSelectInput (client->display, client->manager_window, | ||
| 444 | PropertyChangeMask | StructureNotifyMask); | ||
| 445 | |||
| 446 | if (client->ungrab) | ||
| 447 | client->ungrab (client->display); | ||
| 448 | else | ||
| 449 | X11_XUngrabServer (client->display); | ||
| 450 | |||
| 451 | X11_XFlush (client->display); | ||
| 452 | |||
| 453 | if (client->manager_window && client->watch) | ||
| 454 | { | ||
| 455 | if (!client->watch (client->manager_window, True, | ||
| 456 | PropertyChangeMask | StructureNotifyMask, | ||
| 457 | client->cb_data)) | ||
| 458 | { | ||
| 459 | /* Inability to watch the window probably means that it was destroyed | ||
| 460 | * after we ungrabbed | ||
| 461 | */ | ||
| 462 | client->manager_window = None; | ||
| 463 | return; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | |||
| 468 | read_settings (client); | ||
| 469 | } | ||
| 470 | |||
| 471 | XSettingsClient * | ||
| 472 | xsettings_client_new (Display *display, | ||
| 473 | int screen, | ||
| 474 | XSettingsNotifyFunc notify, | ||
| 475 | XSettingsWatchFunc watch, | ||
| 476 | void *cb_data) | ||
| 477 | { | ||
| 478 | return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data, | ||
| 479 | NULL, NULL); | ||
| 480 | } | ||
| 481 | |||
| 482 | XSettingsClient * | ||
| 483 | xsettings_client_new_with_grab_funcs (Display *display, | ||
| 484 | int screen, | ||
| 485 | XSettingsNotifyFunc notify, | ||
| 486 | XSettingsWatchFunc watch, | ||
| 487 | void *cb_data, | ||
| 488 | XSettingsGrabFunc grab, | ||
| 489 | XSettingsGrabFunc ungrab) | ||
| 490 | { | ||
| 491 | XSettingsClient *client; | ||
| 492 | char buffer[256]; | ||
| 493 | char *atom_names[3]; | ||
| 494 | Atom atoms[3]; | ||
| 495 | |||
| 496 | client = malloc (sizeof *client); | ||
| 497 | if (!client) | ||
| 498 | return NULL; | ||
| 499 | |||
| 500 | client->display = display; | ||
| 501 | client->screen = screen; | ||
| 502 | client->notify = notify; | ||
| 503 | client->watch = watch; | ||
| 504 | client->cb_data = cb_data; | ||
| 505 | client->grab = grab; | ||
| 506 | client->ungrab = ungrab; | ||
| 507 | |||
| 508 | client->manager_window = None; | ||
| 509 | client->settings = NULL; | ||
| 510 | |||
| 511 | sprintf(buffer, "_XSETTINGS_S%d", screen); | ||
| 512 | atom_names[0] = buffer; | ||
| 513 | atom_names[1] = "_XSETTINGS_SETTINGS"; | ||
| 514 | atom_names[2] = "MANAGER"; | ||
| 515 | |||
| 516 | #ifdef HAVE_XINTERNATOMS | ||
| 517 | XInternAtoms (display, atom_names, 3, False, atoms); | ||
| 518 | #else | ||
| 519 | atoms[0] = X11_XInternAtom (display, atom_names[0], False); | ||
| 520 | atoms[1] = X11_XInternAtom (display, atom_names[1], False); | ||
| 521 | atoms[2] = X11_XInternAtom (display, atom_names[2], False); | ||
| 522 | #endif | ||
| 523 | |||
| 524 | client->selection_atom = atoms[0]; | ||
| 525 | client->xsettings_atom = atoms[1]; | ||
| 526 | client->manager_atom = atoms[2]; | ||
| 527 | |||
| 528 | /* Select on StructureNotify so we get MANAGER events | ||
| 529 | */ | ||
| 530 | add_events (display, RootWindow (display, screen), StructureNotifyMask); | ||
| 531 | |||
| 532 | if (client->watch) | ||
| 533 | client->watch (RootWindow (display, screen), True, StructureNotifyMask, | ||
| 534 | client->cb_data); | ||
| 535 | |||
| 536 | check_manager_window (client); | ||
| 537 | |||
| 538 | return client; | ||
| 539 | } | ||
| 540 | |||
| 541 | |||
| 542 | void | ||
| 543 | xsettings_client_set_grab_func (XSettingsClient *client, | ||
| 544 | XSettingsGrabFunc grab) | ||
| 545 | { | ||
| 546 | client->grab = grab; | ||
| 547 | } | ||
| 548 | |||
| 549 | void | ||
| 550 | xsettings_client_set_ungrab_func (XSettingsClient *client, | ||
| 551 | XSettingsGrabFunc ungrab) | ||
| 552 | { | ||
| 553 | client->ungrab = ungrab; | ||
| 554 | } | ||
| 555 | |||
| 556 | void | ||
| 557 | xsettings_client_destroy (XSettingsClient *client) | ||
| 558 | { | ||
| 559 | if (client->watch) | ||
| 560 | client->watch (RootWindow (client->display, client->screen), | ||
| 561 | False, 0, client->cb_data); | ||
| 562 | if (client->manager_window && client->watch) | ||
| 563 | client->watch (client->manager_window, False, 0, client->cb_data); | ||
| 564 | |||
| 565 | xsettings_list_free (client->settings); | ||
| 566 | free (client); | ||
| 567 | } | ||
| 568 | |||
| 569 | XSettingsResult | ||
| 570 | xsettings_client_get_setting (XSettingsClient *client, | ||
| 571 | const char *name, | ||
| 572 | XSettingsSetting **setting) | ||
| 573 | { | ||
| 574 | XSettingsSetting *search = xsettings_list_lookup (client->settings, name); | ||
| 575 | if (search) | ||
| 576 | { | ||
| 577 | *setting = xsettings_setting_copy (search); | ||
| 578 | return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM; | ||
| 579 | } | ||
| 580 | else | ||
| 581 | return XSETTINGS_NO_ENTRY; | ||
| 582 | } | ||
| 583 | |||
| 584 | Bool | ||
| 585 | xsettings_client_process_event (XSettingsClient *client, | ||
| 586 | const XEvent *xev) | ||
| 587 | { | ||
| 588 | /* The checks here will not unlikely cause us to reread | ||
| 589 | * the properties from the manager window a number of | ||
| 590 | * times when the manager changes from A->B. But manager changes | ||
| 591 | * are going to be pretty rare. | ||
| 592 | */ | ||
| 593 | if (xev->xany.window == RootWindow (client->display, client->screen)) | ||
| 594 | { | ||
| 595 | if (xev->xany.type == ClientMessage && | ||
| 596 | xev->xclient.message_type == client->manager_atom && | ||
| 597 | xev->xclient.data.l[1] == client->selection_atom) | ||
| 598 | { | ||
| 599 | check_manager_window (client); | ||
| 600 | return True; | ||
| 601 | } | ||
| 602 | } | ||
| 603 | else if (xev->xany.window == client->manager_window) | ||
| 604 | { | ||
| 605 | if (xev->xany.type == DestroyNotify) | ||
| 606 | { | ||
| 607 | check_manager_window (client); | ||
| 608 | return False; | ||
| 609 | } | ||
| 610 | else if (xev->xany.type == PropertyNotify) | ||
| 611 | { | ||
| 612 | read_settings (client); | ||
| 613 | return True; | ||
| 614 | } | ||
| 615 | } | ||
| 616 | |||
| 617 | return False; | ||
| 618 | } | ||
| 619 | |||
| 620 | XSettingsSetting * | ||
| 621 | xsettings_setting_copy (XSettingsSetting *setting) | ||
| 622 | { | ||
| 623 | XSettingsSetting *result; | ||
| 624 | size_t str_len; | ||
| 625 | |||
| 626 | result = malloc (sizeof *result); | ||
| 627 | if (!result) | ||
| 628 | return NULL; | ||
| 629 | |||
| 630 | str_len = strlen (setting->name); | ||
| 631 | result->name = malloc (str_len + 1); | ||
| 632 | if (!result->name) | ||
| 633 | goto err; | ||
| 634 | |||
| 635 | memcpy (result->name, setting->name, str_len + 1); | ||
| 636 | |||
| 637 | result->type = setting->type; | ||
| 638 | |||
| 639 | switch (setting->type) | ||
| 640 | { | ||
| 641 | case XSETTINGS_TYPE_INT: | ||
| 642 | result->data.v_int = setting->data.v_int; | ||
| 643 | break; | ||
| 644 | case XSETTINGS_TYPE_COLOR: | ||
| 645 | result->data.v_color = setting->data.v_color; | ||
| 646 | break; | ||
| 647 | case XSETTINGS_TYPE_STRING: | ||
| 648 | str_len = strlen (setting->data.v_string); | ||
| 649 | result->data.v_string = malloc (str_len + 1); | ||
| 650 | if (!result->data.v_string) | ||
| 651 | goto err; | ||
| 652 | |||
| 653 | memcpy (result->data.v_string, setting->data.v_string, str_len + 1); | ||
| 654 | break; | ||
| 655 | } | ||
| 656 | |||
| 657 | result->last_change_serial = setting->last_change_serial; | ||
| 658 | |||
| 659 | return result; | ||
| 660 | |||
| 661 | err: | ||
| 662 | if (result->name) | ||
| 663 | free (result->name); | ||
| 664 | free (result); | ||
| 665 | |||
| 666 | return NULL; | ||
| 667 | } | ||
| 668 | |||
| 669 | XSettingsList * | ||
| 670 | xsettings_list_copy (XSettingsList *list) | ||
| 671 | { | ||
| 672 | XSettingsList *new = NULL; | ||
| 673 | XSettingsList *old_iter = list; | ||
| 674 | XSettingsList *new_iter = NULL; | ||
| 675 | |||
| 676 | while (old_iter) | ||
| 677 | { | ||
| 678 | XSettingsList *new_node; | ||
| 679 | |||
| 680 | new_node = malloc (sizeof *new_node); | ||
| 681 | if (!new_node) | ||
| 682 | goto error; | ||
| 683 | |||
| 684 | new_node->setting = xsettings_setting_copy (old_iter->setting); | ||
| 685 | if (!new_node->setting) | ||
| 686 | { | ||
| 687 | free (new_node); | ||
| 688 | goto error; | ||
| 689 | } | ||
| 690 | |||
| 691 | if (new_iter) | ||
| 692 | new_iter->next = new_node; | ||
| 693 | else | ||
| 694 | { | ||
| 695 | new = new_node; | ||
| 696 | new->next = NULL; | ||
| 697 | } | ||
| 698 | |||
| 699 | |||
| 700 | new_iter = new_node; | ||
| 701 | |||
| 702 | old_iter = old_iter->next; | ||
| 703 | } | ||
| 704 | |||
| 705 | return new; | ||
| 706 | |||
| 707 | error: | ||
| 708 | xsettings_list_free (new); | ||
| 709 | return NULL; | ||
| 710 | } | ||
| 711 | |||
| 712 | int | ||
| 713 | xsettings_setting_equal (XSettingsSetting *setting_a, | ||
| 714 | XSettingsSetting *setting_b) | ||
| 715 | { | ||
| 716 | if (setting_a->type != setting_b->type) | ||
| 717 | return 0; | ||
| 718 | |||
| 719 | if (strcmp (setting_a->name, setting_b->name) != 0) | ||
| 720 | return 0; | ||
| 721 | |||
| 722 | switch (setting_a->type) | ||
| 723 | { | ||
| 724 | case XSETTINGS_TYPE_INT: | ||
| 725 | return setting_a->data.v_int == setting_b->data.v_int; | ||
| 726 | case XSETTINGS_TYPE_COLOR: | ||
| 727 | return (setting_a->data.v_color.red == setting_b->data.v_color.red && | ||
| 728 | setting_a->data.v_color.green == setting_b->data.v_color.green && | ||
| 729 | setting_a->data.v_color.blue == setting_b->data.v_color.blue && | ||
| 730 | setting_a->data.v_color.alpha == setting_b->data.v_color.alpha); | ||
| 731 | case XSETTINGS_TYPE_STRING: | ||
| 732 | return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0; | ||
| 733 | } | ||
| 734 | |||
| 735 | return 0; | ||
| 736 | } | ||
| 737 | |||
| 738 | void | ||
| 739 | xsettings_setting_free (XSettingsSetting *setting) | ||
| 740 | { | ||
| 741 | if (setting->type == XSETTINGS_TYPE_STRING) | ||
| 742 | free (setting->data.v_string); | ||
| 743 | |||
| 744 | if (setting->name) | ||
| 745 | free (setting->name); | ||
| 746 | |||
| 747 | free (setting); | ||
| 748 | } | ||
| 749 | |||
| 750 | void | ||
| 751 | xsettings_list_free (XSettingsList *list) | ||
| 752 | { | ||
| 753 | while (list) | ||
| 754 | { | ||
| 755 | XSettingsList *next = list->next; | ||
| 756 | |||
| 757 | xsettings_setting_free (list->setting); | ||
| 758 | free (list); | ||
| 759 | |||
| 760 | list = next; | ||
| 761 | } | ||
| 762 | } | ||
| 763 | |||
| 764 | XSettingsResult | ||
| 765 | xsettings_list_insert (XSettingsList **list, | ||
| 766 | XSettingsSetting *setting) | ||
| 767 | { | ||
| 768 | XSettingsList *node; | ||
| 769 | XSettingsList *iter; | ||
| 770 | XSettingsList *last = NULL; | ||
| 771 | |||
| 772 | node = malloc (sizeof *node); | ||
| 773 | if (!node) | ||
| 774 | return XSETTINGS_NO_MEM; | ||
| 775 | node->setting = setting; | ||
| 776 | |||
| 777 | iter = *list; | ||
| 778 | while (iter) | ||
| 779 | { | ||
| 780 | int cmp = strcmp (setting->name, iter->setting->name); | ||
| 781 | |||
| 782 | if (cmp < 0) | ||
| 783 | break; | ||
| 784 | else if (cmp == 0) | ||
| 785 | { | ||
| 786 | free (node); | ||
| 787 | return XSETTINGS_DUPLICATE_ENTRY; | ||
| 788 | } | ||
| 789 | |||
| 790 | last = iter; | ||
| 791 | iter = iter->next; | ||
| 792 | } | ||
| 793 | |||
| 794 | if (last) | ||
| 795 | last->next = node; | ||
| 796 | else | ||
| 797 | *list = node; | ||
| 798 | |||
| 799 | node->next = iter; | ||
| 800 | |||
| 801 | return XSETTINGS_SUCCESS; | ||
| 802 | } | ||
| 803 | |||
| 804 | XSettingsResult | ||
| 805 | xsettings_list_delete (XSettingsList **list, | ||
| 806 | const char *name) | ||
| 807 | { | ||
| 808 | XSettingsList *iter; | ||
| 809 | XSettingsList *last = NULL; | ||
| 810 | |||
| 811 | iter = *list; | ||
| 812 | while (iter) | ||
| 813 | { | ||
| 814 | if (strcmp (name, iter->setting->name) == 0) | ||
| 815 | { | ||
| 816 | if (last) | ||
| 817 | last->next = iter->next; | ||
| 818 | else | ||
| 819 | *list = iter->next; | ||
| 820 | |||
| 821 | xsettings_setting_free (iter->setting); | ||
| 822 | free (iter); | ||
| 823 | |||
| 824 | return XSETTINGS_SUCCESS; | ||
| 825 | } | ||
| 826 | |||
| 827 | last = iter; | ||
| 828 | iter = iter->next; | ||
| 829 | } | ||
| 830 | |||
| 831 | return XSETTINGS_FAILED; | ||
| 832 | } | ||
| 833 | |||
| 834 | XSettingsSetting * | ||
| 835 | xsettings_list_lookup (XSettingsList *list, | ||
| 836 | const char *name) | ||
| 837 | { | ||
| 838 | XSettingsList *iter; | ||
| 839 | |||
| 840 | iter = list; | ||
| 841 | while (iter) | ||
| 842 | { | ||
| 843 | if (strcmp (name, iter->setting->name) == 0) | ||
| 844 | return iter->setting; | ||
| 845 | |||
| 846 | iter = iter->next; | ||
| 847 | } | ||
| 848 | |||
| 849 | return NULL; | ||
| 850 | } | ||
| 851 | |||
| 852 | char | ||
| 853 | xsettings_byte_order (void) | ||
| 854 | { | ||
| 855 | CARD32 myint = 0x01020304; | ||
| 856 | return (*(char *)&myint == 1) ? MSBFirst : LSBFirst; | ||
| 857 | } | ||
| 858 | |||
| 859 | #endif /* SDL_VIDEO_DRIVER_X11 */ | ||
diff --git a/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h new file mode 100644 index 0000000..863c6f9 --- /dev/null +++ b/contrib/SDL-3.2.8/src/video/x11/xsettings-client.h | |||
| @@ -0,0 +1,153 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2001, 2007 Red Hat, Inc. | ||
| 3 | * Copyright 2024 Igalia S.L. | ||
| 4 | * | ||
| 5 | * Permission to use, copy, modify, distribute, and sell this software and its | ||
| 6 | * documentation for any purpose is hereby granted without fee, provided that | ||
| 7 | * the above copyright notice appear in all copies and that both that | ||
| 8 | * copyright notice and this permission notice appear in supporting | ||
| 9 | * documentation, and that the name of Red Hat not be used in advertising or | ||
| 10 | * publicity pertaining to distribution of the software without specific, | ||
| 11 | * written prior permission. Red Hat makes no representations about the | ||
| 12 | * suitability of this software for any purpose. It is provided "as is" | ||
| 13 | * without express or implied warranty. | ||
| 14 | * | ||
| 15 | * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL | ||
| 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT | ||
| 17 | * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
| 18 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
| 19 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
| 20 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
| 21 | * | ||
| 22 | * Author: Owen Taylor, Red Hat, Inc. | ||
| 23 | */ | ||
| 24 | #ifndef XSETTINGS_CLIENT_H | ||
| 25 | #define XSETTINGS_CLIENT_H | ||
| 26 | |||
| 27 | #ifdef __cplusplus | ||
| 28 | extern "C" { | ||
| 29 | #endif /* __cplusplus */ | ||
| 30 | |||
| 31 | typedef struct _XSettingsBuffer XSettingsBuffer; | ||
| 32 | typedef struct _XSettingsColor XSettingsColor; | ||
| 33 | typedef struct _XSettingsList XSettingsList; | ||
| 34 | typedef struct _XSettingsSetting XSettingsSetting; | ||
| 35 | |||
| 36 | /* Types of settings possible. Enum values correspond to | ||
| 37 | * protocol values. | ||
| 38 | */ | ||
| 39 | typedef enum | ||
| 40 | { | ||
| 41 | XSETTINGS_TYPE_INT = 0, | ||
| 42 | XSETTINGS_TYPE_STRING = 1, | ||
| 43 | XSETTINGS_TYPE_COLOR = 2 | ||
| 44 | } XSettingsType; | ||
| 45 | |||
| 46 | typedef enum | ||
| 47 | { | ||
| 48 | XSETTINGS_SUCCESS, | ||
| 49 | XSETTINGS_NO_MEM, | ||
| 50 | XSETTINGS_ACCESS, | ||
| 51 | XSETTINGS_FAILED, | ||
| 52 | XSETTINGS_NO_ENTRY, | ||
| 53 | XSETTINGS_DUPLICATE_ENTRY | ||
| 54 | } XSettingsResult; | ||
| 55 | |||
| 56 | struct _XSettingsBuffer | ||
| 57 | { | ||
| 58 | char byte_order; | ||
| 59 | size_t len; | ||
| 60 | unsigned char *data; | ||
| 61 | unsigned char *pos; | ||
| 62 | }; | ||
| 63 | |||
| 64 | struct _XSettingsColor | ||
| 65 | { | ||
| 66 | unsigned short red, green, blue, alpha; | ||
| 67 | }; | ||
| 68 | |||
| 69 | struct _XSettingsList | ||
| 70 | { | ||
| 71 | XSettingsSetting *setting; | ||
| 72 | XSettingsList *next; | ||
| 73 | }; | ||
| 74 | |||
| 75 | struct _XSettingsSetting | ||
| 76 | { | ||
| 77 | char *name; | ||
| 78 | XSettingsType type; | ||
| 79 | |||
| 80 | union { | ||
| 81 | int v_int; | ||
| 82 | char *v_string; | ||
| 83 | XSettingsColor v_color; | ||
| 84 | } data; | ||
| 85 | |||
| 86 | unsigned long last_change_serial; | ||
| 87 | }; | ||
| 88 | |||
| 89 | XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting); | ||
| 90 | void xsettings_setting_free (XSettingsSetting *setting); | ||
| 91 | int xsettings_setting_equal (XSettingsSetting *setting_a, | ||
| 92 | XSettingsSetting *setting_b); | ||
| 93 | |||
| 94 | void xsettings_list_free (XSettingsList *list); | ||
| 95 | XSettingsList *xsettings_list_copy (XSettingsList *list); | ||
| 96 | XSettingsResult xsettings_list_insert (XSettingsList **list, | ||
| 97 | XSettingsSetting *setting); | ||
| 98 | XSettingsSetting *xsettings_list_lookup (XSettingsList *list, | ||
| 99 | const char *name); | ||
| 100 | XSettingsResult xsettings_list_delete (XSettingsList **list, | ||
| 101 | const char *name); | ||
| 102 | |||
| 103 | char xsettings_byte_order (void); | ||
| 104 | |||
| 105 | #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) | ||
| 106 | |||
| 107 | typedef struct _XSettingsClient XSettingsClient; | ||
| 108 | |||
| 109 | typedef enum | ||
| 110 | { | ||
| 111 | XSETTINGS_ACTION_NEW, | ||
| 112 | XSETTINGS_ACTION_CHANGED, | ||
| 113 | XSETTINGS_ACTION_DELETED | ||
| 114 | } XSettingsAction; | ||
| 115 | |||
| 116 | typedef void (*XSettingsNotifyFunc) (const char *name, | ||
| 117 | XSettingsAction action, | ||
| 118 | XSettingsSetting *setting, | ||
| 119 | void *cb_data); | ||
| 120 | typedef Bool (*XSettingsWatchFunc) (Window window, | ||
| 121 | Bool is_start, | ||
| 122 | long mask, | ||
| 123 | void *cb_data); | ||
| 124 | typedef void (*XSettingsGrabFunc) (Display *display); | ||
| 125 | |||
| 126 | XSettingsClient *xsettings_client_new (Display *display, | ||
| 127 | int screen, | ||
| 128 | XSettingsNotifyFunc notify, | ||
| 129 | XSettingsWatchFunc watch, | ||
| 130 | void *cb_data); | ||
| 131 | XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display, | ||
| 132 | int screen, | ||
| 133 | XSettingsNotifyFunc notify, | ||
| 134 | XSettingsWatchFunc watch, | ||
| 135 | void *cb_data, | ||
| 136 | XSettingsGrabFunc grab, | ||
| 137 | XSettingsGrabFunc ungrab); | ||
| 138 | void xsettings_client_set_grab_func (XSettingsClient *client, | ||
| 139 | XSettingsGrabFunc grab); | ||
| 140 | void xsettings_client_set_ungrab_func (XSettingsClient *client, | ||
| 141 | XSettingsGrabFunc ungrab); | ||
| 142 | void xsettings_client_destroy (XSettingsClient *client); | ||
| 143 | Bool xsettings_client_process_event (XSettingsClient *client, | ||
| 144 | const XEvent *xev); | ||
| 145 | XSettingsResult xsettings_client_get_setting (XSettingsClient *client, | ||
| 146 | const char *name, | ||
| 147 | XSettingsSetting **setting); | ||
| 148 | |||
| 149 | #ifdef __cplusplus | ||
| 150 | } | ||
| 151 | #endif /* __cplusplus */ | ||
| 152 | |||
| 153 | #endif /* XSETTINGS_CLIENT_H */ | ||
