chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1,329 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include <limits.h> // For INT_MAX
#include "SDL_x11video.h"
#include "SDL_x11clipboard.h"
#include "../SDL_clipboard_c.h"
#include "../../events/SDL_events_c.h"
static const char *text_mime_types[] = {
"UTF8_STRING",
"text/plain;charset=utf-8",
"text/plain",
"TEXT",
"STRING"
};
// Get any application owned window handle for clipboard association
Window GetWindow(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
/* We create an unmapped window that exists just to manage the clipboard,
since X11 selection data is tied to a specific window and dies with it.
We create the window on demand, so apps that don't use the clipboard
don't have to keep an unnecessary resource around. */
if (data->clipboard_window == None) {
Display *dpy = data->display;
Window parent = RootWindow(dpy, DefaultScreen(dpy));
XSetWindowAttributes xattr;
data->clipboard_window = X11_XCreateWindow(dpy, parent, -10, -10, 1, 1, 0,
CopyFromParent, InputOnly,
CopyFromParent, 0, &xattr);
X11_XSelectInput(dpy, data->clipboard_window, PropertyChangeMask);
X11_XFlush(data->display);
}
return data->clipboard_window;
}
static bool SetSelectionData(SDL_VideoDevice *_this, Atom selection, SDL_ClipboardDataCallback callback,
void *userdata, const char **mime_types, size_t mime_count, Uint32 sequence)
{
SDL_VideoData *videodata = _this->internal;
Display *display = videodata->display;
Window window;
SDLX11_ClipboardData *clipboard;
bool clipboard_owner = false;
window = GetWindow(_this);
if (window == None) {
return SDL_SetError("Couldn't find a window to own the selection");
}
if (selection == XA_PRIMARY) {
clipboard = &videodata->primary_selection;
} else {
clipboard = &videodata->clipboard;
}
clipboard_owner = X11_XGetSelectionOwner(display, selection) == window;
// If we are canceling our own data we need to clean it up
if (clipboard_owner && clipboard->sequence == 0) {
SDL_free(clipboard->userdata);
}
clipboard->callback = callback;
clipboard->userdata = userdata;
clipboard->mime_types = mime_types;
clipboard->mime_count = mime_count;
clipboard->sequence = sequence;
X11_XSetSelectionOwner(display, selection, window, CurrentTime);
return true;
}
static void *CloneDataBuffer(const void *buffer, const size_t len)
{
void *clone = NULL;
if (len > 0 && buffer) {
clone = SDL_malloc(len + sizeof(Uint32));
if (clone) {
SDL_memcpy(clone, buffer, len);
SDL_memset((Uint8 *)clone + len, 0, sizeof(Uint32));
}
}
return clone;
}
/*
* original_buffer is considered unusable after the function is called.
*/
static void *AppendDataBuffer(void *original_buffer, const size_t old_len, const void *buffer, const size_t buffer_len)
{
void *resized_buffer;
if (buffer_len > 0 && buffer) {
resized_buffer = SDL_realloc(original_buffer, old_len + buffer_len + sizeof(Uint32));
if (resized_buffer) {
SDL_memcpy((Uint8 *)resized_buffer + old_len, buffer, buffer_len);
SDL_memset((Uint8 *)resized_buffer + old_len + buffer_len, 0, sizeof(Uint32));
}
return resized_buffer;
} else {
return original_buffer;
}
}
static bool WaitForSelection(SDL_VideoDevice *_this, Atom selection_type, bool *flag)
{
Uint64 waitStart;
Uint64 waitElapsed;
waitStart = SDL_GetTicks();
*flag = true;
while (*flag) {
SDL_PumpEvents();
waitElapsed = SDL_GetTicks() - waitStart;
// Wait one second for a selection response.
if (waitElapsed > 1000) {
*flag = false;
SDL_SetError("Selection timeout");
/* We need to set the selection text so that next time we won't
timeout, otherwise we will hang on every call to this function. */
SetSelectionData(_this, selection_type, SDL_ClipboardTextCallback, NULL,
text_mime_types, SDL_arraysize(text_mime_types), 0);
return false;
}
}
return true;
}
static void *GetSelectionData(SDL_VideoDevice *_this, Atom selection_type,
const char *mime_type, size_t *length)
{
SDL_VideoData *videodata = _this->internal;
Display *display = videodata->display;
Window window;
Window owner;
Atom selection;
Atom seln_type;
int seln_format;
unsigned long count;
unsigned long overflow;
SDLX11_ClipboardData *clipboard;
void *data = NULL;
unsigned char *src = NULL;
bool incr_success = false;
Atom XA_MIME = X11_XInternAtom(display, mime_type, False);
*length = 0;
// Get the window that holds the selection
window = GetWindow(_this);
owner = X11_XGetSelectionOwner(display, selection_type);
if (owner == None) {
// This requires a fallback to ancient X10 cut-buffers. We will just skip those for now
data = NULL;
} else if (owner == window) {
owner = DefaultRootWindow(display);
if (selection_type == XA_PRIMARY) {
clipboard = &videodata->primary_selection;
} else {
clipboard = &videodata->clipboard;
}
if (clipboard->callback) {
const void *clipboard_data = clipboard->callback(clipboard->userdata, mime_type, length);
data = CloneDataBuffer(clipboard_data, *length);
}
} else {
// Request that the selection owner copy the data to our window
owner = window;
selection = videodata->atoms.SDL_SELECTION;
X11_XConvertSelection(display, selection_type, XA_MIME, selection, owner,
CurrentTime);
if (WaitForSelection(_this, selection_type, &videodata->selection_waiting) == false) {
data = NULL;
*length = 0;
}
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) == Success) {
if (seln_type == XA_MIME) {
*length = (size_t)count;
data = CloneDataBuffer(src, count);
} else if (seln_type == videodata->atoms.INCR) {
while (1) {
// Only delete the property after being done with the previous "chunk".
X11_XDeleteProperty(display, owner, selection);
X11_XFlush(display);
if (WaitForSelection(_this, selection_type, &videodata->selection_incr_waiting) == false) {
break;
}
X11_XFree(src);
if (X11_XGetWindowProperty(display, owner, selection, 0, INT_MAX / 4, False,
XA_MIME, &seln_type, &seln_format, &count, &overflow, &src) != Success) {
break;
}
if (count == 0) {
incr_success = true;
break;
}
if (*length == 0) {
*length = (size_t)count;
data = CloneDataBuffer(src, count);
} else {
data = AppendDataBuffer(data, *length, src, count);
*length += (size_t)count;
}
if (data == NULL) {
break;
}
}
if (incr_success == false) {
SDL_free(data);
data = 0;
*length = 0;
}
}
X11_XFree(src);
}
}
return data;
}
const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types)
{
*num_mime_types = SDL_arraysize(text_mime_types);
return text_mime_types;
}
bool X11_SetClipboardData(SDL_VideoDevice *_this)
{
SDL_VideoData *videodata = _this->internal;
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);
}
void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length)
{
SDL_VideoData *videodata = _this->internal;
return GetSelectionData(_this, videodata->atoms.CLIPBOARD, mime_type, length);
}
bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type)
{
size_t length;
void *data;
data = X11_GetClipboardData(_this, mime_type, &length);
if (data) {
SDL_free(data);
}
return length > 0;
}
bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text)
{
return SetSelectionData(_this, XA_PRIMARY, SDL_ClipboardTextCallback, SDL_strdup(text), text_mime_types, SDL_arraysize(text_mime_types), 0);
}
char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this)
{
size_t length;
char *text = GetSelectionData(_this, XA_PRIMARY, text_mime_types[0], &length);
if (!text) {
text = SDL_strdup("");
}
return text;
}
bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this)
{
bool result = false;
char *text = X11_GetPrimarySelectionText(_this);
if (text) {
if (text[0] != '\0') {
result = true;
}
SDL_free(text);
}
return result;
}
void X11_QuitClipboard(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
if (data->primary_selection.sequence == 0) {
SDL_free(data->primary_selection.userdata);
}
if (data->clipboard.sequence == 0) {
SDL_free(data->clipboard.userdata);
}
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,46 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11clipboard_h_
#define SDL_x11clipboard_h_
#include <X11/Xlib.h>
typedef struct X11_ClipboardData {
SDL_ClipboardDataCallback callback;
void *userdata;
const char **mime_types;
size_t mime_count;
Uint32 sequence;
} SDLX11_ClipboardData;
extern const char **X11_GetTextMimeTypes(SDL_VideoDevice *_this, size_t *num_mime_types);
extern bool X11_SetClipboardData(SDL_VideoDevice *_this);
extern void *X11_GetClipboardData(SDL_VideoDevice *_this, const char *mime_type, size_t *length);
extern bool X11_HasClipboardData(SDL_VideoDevice *_this, const char *mime_type);
extern bool X11_SetPrimarySelectionText(SDL_VideoDevice *_this, const char *text);
extern char *X11_GetPrimarySelectionText(SDL_VideoDevice *_this);
extern bool X11_HasPrimarySelectionText(SDL_VideoDevice *_this);
extern void X11_QuitClipboard(SDL_VideoDevice *_this);
Window GetWindow(SDL_VideoDevice *_this);
#endif // SDL_x11clipboard_h_

View file

@ -0,0 +1,211 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#define DEBUG_DYNAMIC_X11 0
#include "SDL_x11dyn.h"
#if DEBUG_DYNAMIC_X11
#include <stdio.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
typedef struct
{
SDL_SharedObject *lib;
const char *libname;
} x11dynlib;
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT NULL
#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR NULL
#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 NULL
#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES NULL
#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR NULL
#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS NULL
#endif
static x11dynlib x11libs[] = {
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XEXT },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR },
{ NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS }
};
static void *X11_GetSym(const char *fnname, int *pHasModule)
{
int i;
void *fn = NULL;
for (i = 0; i < SDL_arraysize(x11libs); i++) {
if (x11libs[i].lib) {
fn = SDL_LoadFunction(x11libs[i].lib, fnname);
if (fn) {
break;
}
}
}
#if DEBUG_DYNAMIC_X11
if (fn)
printf("X11: Found '%s' in %s (%p)\n", fnname, x11libs[i].libname, fn);
else
printf("X11: Symbol '%s' NOT FOUND!\n", fnname);
#endif
if (!fn) {
*pHasModule = 0; // kill this module.
}
return fn;
}
#endif // SDL_VIDEO_DRIVER_X11_DYNAMIC
// Define all the function pointers and wrappers...
#define SDL_X11_SYM(rc, fn, params, args, ret) SDL_DYNX11FN_##fn X11_##fn = NULL;
#include "SDL_x11sym.h"
// Annoying varargs entry point...
#ifdef X_HAVE_UTF8_STRING
SDL_DYNX11FN_XCreateIC X11_XCreateIC = NULL;
SDL_DYNX11FN_XGetICValues X11_XGetICValues = NULL;
SDL_DYNX11FN_XSetICValues X11_XSetICValues = NULL;
SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList = NULL;
#endif
/* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */
#define SDL_X11_MODULE(modname) int SDL_X11_HAVE_##modname = 0;
#include "SDL_x11sym.h"
static int x11_load_refcount = 0;
void SDL_X11_UnloadSymbols(void)
{
// Don't actually unload if more than one module is using the libs...
if (x11_load_refcount > 0) {
if (--x11_load_refcount == 0) {
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
int i;
#endif
// set all the function pointers to NULL.
#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 0;
#define SDL_X11_SYM(rc, fn, params, args, ret) X11_##fn = NULL;
#include "SDL_x11sym.h"
#ifdef X_HAVE_UTF8_STRING
X11_XCreateIC = NULL;
X11_XGetICValues = NULL;
X11_XSetICValues = NULL;
X11_XVaCreateNestedList = NULL;
#endif
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
for (i = 0; i < SDL_arraysize(x11libs); i++) {
if (x11libs[i].lib) {
SDL_UnloadObject(x11libs[i].lib);
x11libs[i].lib = NULL;
}
}
#endif
}
}
}
// returns non-zero if all needed symbols were loaded.
bool SDL_X11_LoadSymbols(void)
{
bool result = true; // always succeed if not using Dynamic X11 stuff.
// deal with multiple modules (dga, x11, etc) needing these symbols...
if (x11_load_refcount++ == 0) {
#ifdef SDL_VIDEO_DRIVER_X11_DYNAMIC
int i;
int *thismod = NULL;
for (i = 0; i < SDL_arraysize(x11libs); i++) {
if (x11libs[i].libname) {
x11libs[i].lib = SDL_LoadObject(x11libs[i].libname);
}
}
#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes
#include "SDL_x11sym.h"
#define SDL_X11_MODULE(modname) thismod = &SDL_X11_HAVE_##modname;
#define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)X11_GetSym(#fn, thismod);
#include "SDL_x11sym.h"
#ifdef X_HAVE_UTF8_STRING
X11_XCreateIC = (SDL_DYNX11FN_XCreateIC)
X11_GetSym("XCreateIC", &SDL_X11_HAVE_UTF8);
X11_XGetICValues = (SDL_DYNX11FN_XGetICValues)
X11_GetSym("XGetICValues", &SDL_X11_HAVE_UTF8);
X11_XSetICValues = (SDL_DYNX11FN_XSetICValues)
X11_GetSym("XSetICValues", &SDL_X11_HAVE_UTF8);
X11_XVaCreateNestedList = (SDL_DYNX11FN_XVaCreateNestedList)
X11_GetSym("XVaCreateNestedList", &SDL_X11_HAVE_UTF8);
#endif
if (SDL_X11_HAVE_BASEXLIB) {
// all required symbols loaded.
SDL_ClearError();
} else {
// in case something got loaded...
SDL_X11_UnloadSymbols();
result = false;
}
#else // no dynamic X11
#define SDL_X11_MODULE(modname) SDL_X11_HAVE_##modname = 1; // default yes
#define SDL_X11_SYM(a, fn, x, y, z) X11_##fn = (SDL_DYNX11FN_##fn)fn;
#include "SDL_x11sym.h"
#ifdef X_HAVE_UTF8_STRING
X11_XCreateIC = XCreateIC;
X11_XGetICValues = XGetICValues;
X11_XSetICValues = XSetICValues;
X11_XVaCreateNestedList = XVaCreateNestedList;
#endif
#endif
}
return result;
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,113 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11dyn_h_
#define SDL_x11dyn_h_
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/Xresource.h>
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
#include <X11/XKBlib.h>
#endif
// Apparently some X11 systems can't include this multiple times...
#ifndef SDL_INCLUDED_XLIBINT_H
#define SDL_INCLUDED_XLIBINT_H 1
#include <X11/Xlibint.h>
#endif
#include <X11/Xproto.h>
#include <X11/extensions/Xext.h>
#ifndef NO_SHARED_MEMORY
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
#include <X11/extensions/Xdbe.h>
#endif
#if defined(SDL_VIDEO_DRIVER_X11_XINPUT2) || defined(SDL_VIDEO_DRIVER_X11_XFIXES)
#include <X11/extensions/XInput2.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
#include <X11/extensions/sync.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
#include <X11/extensions/Xrandr.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
#include <X11/extensions/scrnsaver.h>
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
#include <X11/extensions/shape.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// evil function signatures...
typedef Bool (*SDL_X11_XESetWireToEventRetType)(Display *, XEvent *, xEvent *);
typedef int (*SDL_X11_XSynchronizeRetType)(Display *);
typedef Status (*SDL_X11_XESetEventToWireRetType)(Display *, XEvent *, xEvent *);
extern bool SDL_X11_LoadSymbols(void);
extern void SDL_X11_UnloadSymbols(void);
// Declare all the function pointers and wrappers...
#define SDL_X11_SYM(rc, fn, params, args, ret) \
typedef rc(*SDL_DYNX11FN_##fn) params; \
extern SDL_DYNX11FN_##fn X11_##fn;
#include "SDL_x11sym.h"
// Annoying varargs entry point...
#ifdef X_HAVE_UTF8_STRING
typedef XIC (*SDL_DYNX11FN_XCreateIC)(XIM, ...);
typedef char *(*SDL_DYNX11FN_XGetICValues)(XIC, ...);
typedef char *(*SDL_DYNX11FN_XSetICValues)(XIC, ...);
typedef XVaNestedList (*SDL_DYNX11FN_XVaCreateNestedList)(int, ...);
extern SDL_DYNX11FN_XCreateIC X11_XCreateIC;
extern SDL_DYNX11FN_XGetICValues X11_XGetICValues;
extern SDL_DYNX11FN_XSetICValues X11_XSetICValues;
extern SDL_DYNX11FN_XVaCreateNestedList X11_XVaCreateNestedList;
#endif
/* These SDL_X11_HAVE_* flags are here whether you have dynamic X11 or not. */
#define SDL_X11_MODULE(modname) extern int SDL_X11_HAVE_##modname;
#include "SDL_x11sym.h"
#ifdef __cplusplus
}
#endif
#endif // !defined SDL_x11dyn_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11events_h_
#define SDL_x11events_h_
extern void X11_PumpEvents(SDL_VideoDevice *_this);
extern int X11_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
extern void X11_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_SuspendScreenSaver(SDL_VideoDevice *_this);
extern void X11_ReconcileKeyboardState(SDL_VideoDevice *_this);
extern void X11_GetBorderValues(SDL_WindowData *data);
extern Uint64 X11_GetEventTimestamp(unsigned long time);
extern void X11_HandleKeyEvent(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_KeyboardID keyboardID, XEvent *xevent);
extern void X11_HandleButtonPress(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, float x, float y, unsigned long time);
extern void X11_HandleButtonRelease(SDL_VideoDevice *_this, SDL_WindowData *windowdata, SDL_MouseID mouseID, int button, unsigned long time);
extern SDL_WindowData *X11_FindWindow(SDL_VideoDevice *_this, Window window);
extern bool X11_ProcessHitTest(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y, bool force_new_result);
extern bool X11_TriggerHitTestAction(SDL_VideoDevice *_this, SDL_WindowData *data, const float x, const float y);
#endif // SDL_x11events_h_

View file

@ -0,0 +1,261 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "SDL_x11framebuffer.h"
#include "SDL_x11xsync.h"
#ifndef NO_SHARED_MEMORY
// Shared memory error handler routine
static int shm_error;
static int (*X_handler)(Display *, XErrorEvent *) = NULL;
static int shm_errhandler(Display *d, XErrorEvent *e)
{
if (e->error_code == BadAccess) {
shm_error = True;
return 0;
}
return X_handler(d, e);
}
static bool have_mitshm(Display *dpy)
{
// Only use shared memory on local X servers
return X11_XShmQueryExtension(dpy) ? SDL_X11_HAVE_SHM : false;
}
#endif // !NO_SHARED_MEMORY
bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, SDL_PixelFormat *format,
void **pixels, int *pitch)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XGCValues gcv;
XVisualInfo vinfo;
int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
// Free the old framebuffer surface
X11_DestroyWindowFramebuffer(_this, window);
// Create the graphics context for drawing
gcv.graphics_exposures = False;
data->gc = X11_XCreateGC(display, data->xwindow, GCGraphicsExposures, &gcv);
if (!data->gc) {
return SDL_SetError("Couldn't create graphics context");
}
// Find out the pixel format and depth
if (!X11_GetVisualInfoFromVisual(display, data->visual, &vinfo)) {
return SDL_SetError("Couldn't get window visual information");
}
*format = X11_GetPixelFormatFromVisualInfo(display, &vinfo);
if (*format == SDL_PIXELFORMAT_UNKNOWN) {
return SDL_SetError("Unknown window pixel format");
}
// Calculate pitch
*pitch = (((w * SDL_BYTESPERPIXEL(*format)) + 3) & ~3);
// Create the actual image
#ifndef NO_SHARED_MEMORY
if (have_mitshm(display)) {
XShmSegmentInfo *shminfo = &data->shminfo;
shminfo->shmid = shmget(IPC_PRIVATE, (size_t)h * (*pitch), IPC_CREAT | 0777);
if (shminfo->shmid >= 0) {
shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0);
shminfo->readOnly = False;
if (shminfo->shmaddr != (char *)-1) {
shm_error = False;
X_handler = X11_XSetErrorHandler(shm_errhandler);
X11_XShmAttach(display, shminfo);
X11_XSync(display, False);
X11_XSetErrorHandler(X_handler);
if (shm_error) {
shmdt(shminfo->shmaddr);
}
} else {
shm_error = True;
}
shmctl(shminfo->shmid, IPC_RMID, NULL);
} else {
shm_error = True;
}
if (!shm_error) {
data->ximage = X11_XShmCreateImage(display, data->visual,
vinfo.depth, ZPixmap,
shminfo->shmaddr, shminfo,
w, h);
if (!data->ximage) {
X11_XShmDetach(display, shminfo);
X11_XSync(display, False);
shmdt(shminfo->shmaddr);
} else {
// Done!
data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst;
data->use_mitshm = true;
*pixels = shminfo->shmaddr;
return true;
}
}
}
#endif // not NO_SHARED_MEMORY
*pixels = SDL_malloc((size_t)h * (*pitch));
if (!*pixels) {
return false;
}
data->ximage = X11_XCreateImage(display, data->visual,
vinfo.depth, ZPixmap, 0, (char *)(*pixels),
w, h, 32, 0);
if (!data->ximage) {
SDL_free(*pixels);
return SDL_SetError("Couldn't create XImage");
}
data->ximage->byte_order = (SDL_BYTEORDER == SDL_BIG_ENDIAN) ? MSBFirst : LSBFirst;
return true;
}
bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects,
int numrects)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
int i;
int x, y, w, h;
int window_w, window_h;
SDL_GetWindowSizeInPixels(window, &window_w, &window_h);
#ifndef NO_SHARED_MEMORY
if (data->use_mitshm) {
for (i = 0; i < numrects; ++i) {
x = rects[i].x;
y = rects[i].y;
w = rects[i].w;
h = rects[i].h;
if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
// Clipped?
continue;
}
if (x < 0) {
x += w;
w += rects[i].x;
}
if (y < 0) {
y += h;
h += rects[i].y;
}
if (x + w > window_w) {
w = window_w - x;
}
if (y + h > window_h) {
h = window_h - y;
}
X11_XShmPutImage(display, data->xwindow, data->gc, data->ximage,
x, y, x, y, w, h, False);
}
} else
#endif // !NO_SHARED_MEMORY
{
for (i = 0; i < numrects; ++i) {
x = rects[i].x;
y = rects[i].y;
w = rects[i].w;
h = rects[i].h;
if (w <= 0 || h <= 0 || (x + w) <= 0 || (y + h) <= 0) {
// Clipped?
continue;
}
if (x < 0) {
x += w;
w += rects[i].x;
}
if (y < 0) {
y += h;
h += rects[i].y;
}
if (x + w > window_w) {
w = window_w - x;
}
if (y + h > window_h) {
h = window_h - y;
}
X11_XPutImage(display, data->xwindow, data->gc, data->ximage,
x, y, x, y, w, h);
}
}
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_HandlePresent(data->window);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
X11_XSync(display, False);
return true;
}
void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
Display *display;
if (!data) {
// The window wasn't fully initialized
return;
}
display = data->videodata->display;
if (data->ximage) {
XDestroyImage(data->ximage);
#ifndef NO_SHARED_MEMORY
if (data->use_mitshm) {
X11_XShmDetach(display, &data->shminfo);
X11_XSync(display, False);
shmdt(data->shminfo.shmaddr);
data->use_mitshm = false;
}
#endif // !NO_SHARED_MEMORY
data->ximage = NULL;
}
if (data->gc) {
X11_XFreeGC(display, data->gc);
data->gc = NULL;
}
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,34 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_x11framebuffer_h_
#define SDL_x11framebuffer_h_
#include "SDL_internal.h"
extern bool X11_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window,
SDL_PixelFormat *format,
void **pixels, int *pitch);
extern bool X11_UpdateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window,
const SDL_Rect *rects, int numrects);
extern void X11_DestroyWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_x11framebuffer_h_

View file

@ -0,0 +1,789 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_scancode_tables_c.h"
#include <X11/keysym.h>
#include <X11/XKBlib.h>
#include "../../events/imKStoUCS.h"
#include "../../events/SDL_keysym_to_scancode_c.h"
#include "../../events/SDL_keysym_to_keycode_c.h"
#ifdef X_HAVE_UTF8_STRING
#include <locale.h>
#endif
static SDL_ScancodeTable scancode_set[] = {
SDL_SCANCODE_TABLE_DARWIN,
SDL_SCANCODE_TABLE_XFREE86_1,
SDL_SCANCODE_TABLE_XFREE86_2,
SDL_SCANCODE_TABLE_XVNC,
};
static bool X11_ScancodeIsRemappable(SDL_Scancode scancode)
{
/*
* XKB remappings can assign different keysyms for these scancodes, but
* as these keys are in fixed positions, the scancodes themselves shouldn't
* be switched. Mark them as not being remappable.
*/
switch (scancode) {
case SDL_SCANCODE_ESCAPE:
case SDL_SCANCODE_CAPSLOCK:
case SDL_SCANCODE_NUMLOCKCLEAR:
case SDL_SCANCODE_LSHIFT:
case SDL_SCANCODE_RSHIFT:
case SDL_SCANCODE_LCTRL:
case SDL_SCANCODE_RCTRL:
case SDL_SCANCODE_LALT:
case SDL_SCANCODE_RALT:
case SDL_SCANCODE_LGUI:
case SDL_SCANCODE_RGUI:
return false;
default:
return true;
}
}
// This function only correctly maps letters and numbers for keyboards in US QWERTY layout
static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode)
{
const KeySym keysym = X11_KeyCodeToSym(_this, keycode, 0, 0);
if (keysym == NoSymbol) {
return SDL_SCANCODE_UNKNOWN;
}
return SDL_GetScancodeFromKeySym(keysym, keycode);
}
KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask)
{
SDL_VideoData *data = _this->internal;
KeySym keysym;
unsigned int mods_ret[16];
SDL_zero(mods_ret);
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
if (data->xkb.desc_ptr) {
int num_groups = XkbKeyNumGroups(data->xkb.desc_ptr, keycode);
unsigned char info = XkbKeyGroupInfo(data->xkb.desc_ptr, keycode);
if (num_groups && group >= num_groups) {
int action = XkbOutOfRangeGroupAction(info);
if (action == XkbRedirectIntoRange) {
group = XkbOutOfRangeGroupNumber(info);
if (group >= num_groups) {
group = 0;
}
} else if (action == XkbClampIntoRange) {
group = num_groups - 1;
} else {
group %= num_groups;
}
}
if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) {
keysym = NoSymbol;
}
} else
#endif
{
// TODO: Handle groups and modifiers on the legacy path.
keysym = X11_XKeycodeToKeysym(data->display, keycode, 0);
}
return keysym;
}
bool X11_InitKeyboard(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
int i = 0;
int j = 0;
int min_keycode, max_keycode;
struct
{
SDL_Scancode scancode;
KeySym keysym;
int value;
} fingerprint[] = {
{ SDL_SCANCODE_HOME, XK_Home, 0 },
{ SDL_SCANCODE_PAGEUP, XK_Prior, 0 },
{ SDL_SCANCODE_UP, XK_Up, 0 },
{ SDL_SCANCODE_LEFT, XK_Left, 0 },
{ SDL_SCANCODE_DELETE, XK_Delete, 0 },
{ SDL_SCANCODE_KP_ENTER, XK_KP_Enter, 0 },
};
int best_distance;
int best_index;
int distance;
Bool xkb_repeat = 0;
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
{
int xkb_major = XkbMajorVersion;
int xkb_minor = XkbMinorVersion;
if (X11_XkbQueryExtension(data->display, NULL, &data->xkb.event, NULL, &xkb_major, &xkb_minor)) {
data->xkb.desc_ptr = X11_XkbGetMap(data->display, XkbAllClientInfoMask, XkbUseCoreKbd);
}
// This will remove KeyRelease events for held keys
X11_XkbSetDetectableAutoRepeat(data->display, True, &xkb_repeat);
}
#endif
// Open a connection to the X input manager
#ifdef X_HAVE_UTF8_STRING
if (SDL_X11_HAVE_UTF8) {
/* Set the locale, and call XSetLocaleModifiers before XOpenIM so that
Compose keys will work correctly. */
char *prev_locale = setlocale(LC_ALL, NULL);
char *prev_xmods = X11_XSetLocaleModifiers(NULL);
if (prev_locale) {
prev_locale = SDL_strdup(prev_locale);
}
if (prev_xmods) {
prev_xmods = SDL_strdup(prev_xmods);
}
(void)setlocale(LC_ALL, "");
X11_XSetLocaleModifiers("");
data->im = X11_XOpenIM(data->display, NULL, NULL, NULL);
/* Reset the locale + X locale modifiers back to how they were,
locale first because the X locale modifiers depend on it. */
(void)setlocale(LC_ALL, prev_locale);
X11_XSetLocaleModifiers(prev_xmods);
if (prev_locale) {
SDL_free(prev_locale);
}
if (prev_xmods) {
SDL_free(prev_xmods);
}
}
#endif
// Try to determine which scancodes are being used based on fingerprint
best_distance = SDL_arraysize(fingerprint) + 1;
best_index = -1;
X11_XDisplayKeycodes(data->display, &min_keycode, &max_keycode);
for (i = 0; i < SDL_arraysize(fingerprint); ++i) {
fingerprint[i].value = X11_XKeysymToKeycode(data->display, fingerprint[i].keysym) - min_keycode;
}
for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
int table_size;
const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[i], &table_size);
distance = 0;
for (j = 0; j < SDL_arraysize(fingerprint); ++j) {
if (fingerprint[j].value < 0 || fingerprint[j].value >= table_size) {
distance += 1;
} else if (table[fingerprint[j].value] != fingerprint[j].scancode) {
distance += 1;
}
}
if (distance < best_distance) {
best_distance = distance;
best_index = i;
}
}
if (best_index < 0 || best_distance > 2) {
// This is likely to be SDL_SCANCODE_TABLE_XFREE86_2 with remapped keys, double check a rarely remapped value
int fingerprint_value = X11_XKeysymToKeycode(data->display, 0x1008FF5B /* XF86Documents */) - min_keycode;
if (fingerprint_value == 235) {
for (i = 0; i < SDL_arraysize(scancode_set); ++i) {
if (scancode_set[i] == SDL_SCANCODE_TABLE_XFREE86_2) {
best_index = i;
best_distance = 0;
break;
}
}
}
}
if (best_index >= 0 && best_distance <= 2) {
int table_size;
const SDL_Scancode *table = SDL_GetScancodeTable(scancode_set[best_index], &table_size);
#ifdef DEBUG_KEYBOARD
SDL_Log("Using scancode set %d, min_keycode = %d, max_keycode = %d, table_size = %d", best_index, min_keycode, max_keycode, table_size);
#endif
// This should never happen, but just in case...
if (table_size > (SDL_arraysize(data->key_layout) - min_keycode)) {
table_size = (SDL_arraysize(data->key_layout) - min_keycode);
}
SDL_memcpy(&data->key_layout[min_keycode], table, sizeof(SDL_Scancode) * table_size);
/* Scancodes represent physical locations on the keyboard, unaffected by keyboard mapping.
However, there are a number of extended scancodes that have no standard location, so use
the X11 mapping for all non-character keys.
*/
for (i = min_keycode; i <= max_keycode; ++i) {
SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i);
#ifdef DEBUG_KEYBOARD
{
KeySym sym;
sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0);
SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode,
(unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym));
}
#endif
if (scancode == data->key_layout[i]) {
continue;
}
if ((SDL_GetKeymapKeycode(NULL, scancode, SDL_KMOD_NONE) & (SDLK_SCANCODE_MASK | SDLK_EXTENDED_MASK)) && X11_ScancodeIsRemappable(scancode)) {
// Not a character key and the scancode is safe to remap
#ifdef DEBUG_KEYBOARD
SDL_Log("Changing scancode, was %d (%s), now %d (%s)", data->key_layout[i], SDL_GetScancodeName(data->key_layout[i]), scancode, SDL_GetScancodeName(scancode));
#endif
data->key_layout[i] = scancode;
}
}
} else {
#ifdef DEBUG_SCANCODES
SDL_Log("Keyboard layout unknown, please report the following to the SDL forums/mailing list (https://discourse.libsdl.org/):");
#endif
// Determine key_layout - only works on US QWERTY layout
for (i = min_keycode; i <= max_keycode; ++i) {
SDL_Scancode scancode = X11_KeyCodeToSDLScancode(_this, i);
#ifdef DEBUG_SCANCODES
{
KeySym sym;
sym = X11_KeyCodeToSym(_this, (KeyCode)i, 0);
SDL_Log("code = %d, sym = 0x%X (%s) ", i - min_keycode,
(unsigned int)sym, sym == NoSymbol ? "NoSymbol" : X11_XKeysymToString(sym));
}
if (scancode == SDL_SCANCODE_UNKNOWN) {
SDL_Log("scancode not found");
} else {
SDL_Log("scancode = %d (%s)", scancode, SDL_GetScancodeName(scancode));
}
#endif
data->key_layout[i] = scancode;
}
}
X11_UpdateKeymap(_this, false);
SDL_SetScancodeName(SDL_SCANCODE_APPLICATION, "Menu");
X11_ReconcileKeyboardState(_this);
return true;
}
static unsigned X11_GetNumLockModifierMask(SDL_VideoDevice *_this)
{
SDL_VideoData *videodata = _this->internal;
Display *display = videodata->display;
unsigned num_mask = 0;
int i, j;
XModifierKeymap *xmods;
unsigned n;
xmods = X11_XGetModifierMapping(display);
n = xmods->max_keypermod;
for (i = 3; i < 8; i++) {
for (j = 0; j < n; j++) {
KeyCode kc = xmods->modifiermap[i * n + j];
if (videodata->key_layout[kc] == SDL_SCANCODE_NUMLOCKCLEAR) {
num_mask = 1 << i;
break;
}
}
}
X11_XFreeModifiermap(xmods);
return num_mask;
}
static unsigned X11_GetScrollLockModifierMask(SDL_VideoDevice *_this)
{
SDL_VideoData *videodata = _this->internal;
Display *display = videodata->display;
unsigned num_mask = 0;
int i, j;
XModifierKeymap *xmods;
unsigned n;
xmods = X11_XGetModifierMapping(display);
n = xmods->max_keypermod;
for (i = 3; i < 8; i++) {
for (j = 0; j < n; j++) {
KeyCode kc = xmods->modifiermap[i * n + j];
if (videodata->key_layout[kc] == SDL_SCANCODE_SCROLLLOCK) {
num_mask = 1 << i;
break;
}
}
}
X11_XFreeModifiermap(xmods);
return num_mask;
}
void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event)
{
struct Keymod_masks
{
SDL_Keymod sdl_mask;
unsigned int xkb_mask;
} const keymod_masks[] = {
{ SDL_KMOD_NONE, 0 },
{ SDL_KMOD_SHIFT, ShiftMask },
{ SDL_KMOD_CAPS, LockMask },
{ SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask },
{ SDL_KMOD_MODE, Mod5Mask },
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask },
{ SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask },
{ SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask },
{ SDL_KMOD_LEVEL5, Mod3Mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT, Mod3Mask | ShiftMask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_CAPS, Mod3Mask | LockMask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | ShiftMask | LockMask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE, Mod5Mask | Mod3Mask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod3Mask | Mod5Mask | ShiftMask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | LockMask },
{ SDL_KMOD_LEVEL5 | SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod3Mask | Mod5Mask | ShiftMask | LockMask }
};
SDL_VideoData *data = _this->internal;
SDL_Scancode scancode;
SDL_Keymap *keymap = SDL_CreateKeymap();
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
if (data->xkb.desc_ptr) {
XkbStateRec state;
X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb.desc_ptr);
if (X11_XkbGetState(data->display, XkbUseCoreKbd, &state) == Success) {
data->xkb.current_group = state.group;
}
}
#endif
for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) {
for (int i = 0; i < SDL_arraysize(data->key_layout); ++i) {
// Make sure this is a valid scancode
scancode = data->key_layout[i];
if (scancode == SDL_SCANCODE_UNKNOWN) {
continue;
}
const KeySym keysym = X11_KeyCodeToSym(_this, i, data->xkb.current_group, keymod_masks[m].xkb_mask);
if (keysym != NoSymbol) {
SDL_Keycode keycode = SDL_GetKeyCodeFromKeySym(keysym, i, keymod_masks[m].sdl_mask);
if (!keycode) {
switch (scancode) {
case SDL_SCANCODE_RETURN:
keycode = SDLK_RETURN;
break;
case SDL_SCANCODE_ESCAPE:
keycode = SDLK_ESCAPE;
break;
case SDL_SCANCODE_BACKSPACE:
keycode = SDLK_BACKSPACE;
break;
case SDL_SCANCODE_DELETE:
keycode = SDLK_DELETE;
break;
default:
keycode = SDL_SCANCODE_TO_KEYCODE(scancode);
break;
}
}
SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode);
}
}
}
data->xkb.numlock_mask = X11_GetNumLockModifierMask(_this);
data->xkb.scrolllock_mask = X11_GetScrollLockModifierMask(_this);
SDL_SetKeymap(keymap, send_event);
}
void X11_QuitKeyboard(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
if (data->xkb.desc_ptr) {
X11_XkbFreeKeyboard(data->xkb.desc_ptr, 0, True);
data->xkb.desc_ptr = NULL;
}
#endif
}
void X11_ClearComposition(SDL_WindowData *data)
{
if (data->preedit_length > 0) {
data->preedit_text[0] = '\0';
data->preedit_length = 0;
}
if (data->ime_needs_clear_composition) {
SDL_SendEditingText("", 0, 0);
data->ime_needs_clear_composition = false;
}
}
static void X11_SendEditingEvent(SDL_WindowData *data)
{
if (data->preedit_length == 0) {
X11_ClearComposition(data);
return;
}
bool in_highlight = false;
int start = -1, length = 0, i;
for (i = 0; i < data->preedit_length; ++i) {
if (data->preedit_feedback[i] & (XIMReverse | XIMHighlight)) {
if (start < 0) {
start = i;
in_highlight = true;
}
} else if (in_highlight) {
// Found the end of the highlight
break;
}
}
if (in_highlight) {
length = (i - start);
} else {
start = SDL_clamp(data->preedit_cursor, 0, data->preedit_length);
}
SDL_SendEditingText(data->preedit_text, start, length);
data->ime_needs_clear_composition = true;
}
static int preedit_start_callback(XIC xic, XPointer client_data, XPointer call_data)
{
// No limit on preedit text length
return -1;
}
static void preedit_done_callback(XIC xic, XPointer client_data, XPointer call_data)
{
}
static void preedit_draw_callback(XIC xic, XPointer client_data, XIMPreeditDrawCallbackStruct *call_data)
{
SDL_WindowData *data = (SDL_WindowData *)client_data;
int chg_first = SDL_clamp(call_data->chg_first, 0, data->preedit_length);
int chg_length = SDL_clamp(call_data->chg_length, 0, data->preedit_length - chg_first);
const char *start = data->preedit_text;
if (chg_length > 0) {
// Delete text in range
for (int i = 0; start && *start && i < chg_first; ++i) {
SDL_StepUTF8(&start, NULL);
}
const char *end = start;
for (int i = 0; end && *end && i < chg_length; ++i) {
SDL_StepUTF8(&end, NULL);
}
if (end > start) {
SDL_memmove((char *)start, end, SDL_strlen(end) + 1);
if ((chg_first + chg_length) > data->preedit_length) {
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));
}
}
data->preedit_length -= chg_length;
}
XIMText *text = call_data->text;
if (text) {
// Insert text in range
SDL_assert(!text->encoding_is_wchar);
// The text length isn't calculated as directed by the spec, recalculate it now
if (text->string.multi_byte) {
text->length = SDL_utf8strlen(text->string.multi_byte);
}
size_t string_size = SDL_strlen(text->string.multi_byte);
size_t size = string_size + 1;
if (data->preedit_text) {
size += SDL_strlen(data->preedit_text);
}
char *preedit_text = (char *)SDL_malloc(size * sizeof(*preedit_text));
if (preedit_text) {
size_t pre_size = (start - data->preedit_text);
size_t post_size = start ? SDL_strlen(start) : 0;
if (pre_size > 0) {
SDL_memcpy(&preedit_text[0], data->preedit_text, pre_size);
}
SDL_memcpy(&preedit_text[pre_size], text->string.multi_byte, string_size);
if (post_size > 0) {
SDL_memcpy(&preedit_text[pre_size + string_size], start, post_size);
}
preedit_text[size - 1] = '\0';
}
size_t feedback_size = data->preedit_length + text->length;
XIMFeedback *feedback = (XIMFeedback *)SDL_malloc(feedback_size * sizeof(*feedback));
if (feedback) {
size_t pre_size = (size_t)chg_first;
size_t post_size = (size_t)data->preedit_length - pre_size;
if (pre_size > 0) {
SDL_memcpy(&feedback[0], data->preedit_feedback, pre_size * sizeof(*feedback));
}
SDL_memcpy(&feedback[pre_size], text->feedback, text->length * sizeof(*feedback));
if (post_size > 0) {
SDL_memcpy(&feedback[pre_size + text->length], &data->preedit_feedback[pre_size], post_size * sizeof(*feedback));
}
}
if (preedit_text && feedback) {
SDL_free(data->preedit_text);
data->preedit_text = preedit_text;
SDL_free(data->preedit_feedback);
data->preedit_feedback = feedback;
data->preedit_length += text->length;
} else {
SDL_free(preedit_text);
SDL_free(feedback);
}
}
data->preedit_cursor = call_data->caret;
#ifdef DEBUG_XIM
if (call_data->chg_length > 0) {
SDL_Log("Draw callback deleted %d characters at %d", call_data->chg_length, call_data->chg_first);
}
if (text) {
SDL_Log("Draw callback inserted %s at %d, caret: %d", text->string.multi_byte, call_data->chg_first, call_data->caret);
}
SDL_Log("Pre-edit text: %s", data->preedit_text);
#endif
X11_SendEditingEvent(data);
}
static void preedit_caret_callback(XIC xic, XPointer client_data, XIMPreeditCaretCallbackStruct *call_data)
{
SDL_WindowData *data = (SDL_WindowData *)client_data;
switch (call_data->direction) {
case XIMAbsolutePosition:
if (call_data->position != data->preedit_cursor) {
data->preedit_cursor = call_data->position;
X11_SendEditingEvent(data);
}
break;
case XIMDontChange:
break;
default:
// Not currently supported
break;
}
}
void X11_CreateInputContext(SDL_WindowData *data)
{
#ifdef X_HAVE_UTF8_STRING
SDL_VideoData *videodata = data->videodata;
if (SDL_X11_HAVE_UTF8 && videodata->im) {
const char *hint = SDL_GetHint(SDL_HINT_IME_IMPLEMENTED_UI);
if (hint && SDL_strstr(hint, "composition")) {
XIMCallback draw_callback;
draw_callback.client_data = (XPointer)data;
draw_callback.callback = (XIMProc)preedit_draw_callback;
XIMCallback start_callback;
start_callback.client_data = (XPointer)data;
start_callback.callback = (XIMProc)preedit_start_callback;
XIMCallback done_callback;
done_callback.client_data = (XPointer)data;
done_callback.callback = (XIMProc)preedit_done_callback;
XIMCallback caret_callback;
caret_callback.client_data = (XPointer)data;
caret_callback.callback = (XIMProc)preedit_caret_callback;
XVaNestedList attr = X11_XVaCreateNestedList(0,
XNPreeditStartCallback, &start_callback,
XNPreeditDoneCallback, &done_callback,
XNPreeditDrawCallback, &draw_callback,
XNPreeditCaretCallback, &caret_callback,
NULL);
if (attr) {
data->ic = X11_XCreateIC(videodata->im,
XNInputStyle, XIMPreeditCallbacks | XIMStatusCallbacks,
XNPreeditAttributes, attr,
XNClientWindow, data->xwindow,
NULL);
X11_XFree(attr);
}
}
if (!data->ic) {
data->ic = X11_XCreateIC(videodata->im,
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
XNClientWindow, data->xwindow,
NULL);
}
data->xim_spot.x = -1;
data->xim_spot.y = -1;
}
#endif // X_HAVE_UTF8_STRING
}
static void X11_ResetXIM(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef X_HAVE_UTF8_STRING
SDL_WindowData *data = window->internal;
if (data && data->ic) {
// Clear any partially entered dead keys
char *contents = X11_Xutf8ResetIC(data->ic);
if (contents) {
X11_XFree(contents);
}
}
#endif // X_HAVE_UTF8_STRING
}
bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
{
X11_ResetXIM(_this, window);
return X11_UpdateTextInputArea(_this, window);
}
bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window)
{
X11_ResetXIM(_this, window);
return true;
}
bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef X_HAVE_UTF8_STRING
SDL_WindowData *data = window->internal;
if (data && data->ic) {
XPoint spot;
spot.x = window->text_input_rect.x + window->text_input_cursor;
spot.y = window->text_input_rect.y + window->text_input_rect.h;
if (spot.x != data->xim_spot.x || spot.y != data->xim_spot.y) {
XVaNestedList attr = X11_XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
if (attr) {
X11_XSetICValues(data->ic, XNPreeditAttributes, attr, NULL);
X11_XFree(attr);
}
SDL_copyp(&data->xim_spot, &spot);
}
}
#endif
return true;
}
bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this)
{
SDL_VideoData *videodata = _this->internal;
return videodata->is_steam_deck;
}
void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props)
{
SDL_VideoData *videodata = _this->internal;
if (videodata->is_steam_deck) {
/* For more documentation of the URL parameters, see:
* https://partner.steamgames.com/doc/api/ISteamUtils#ShowFloatingGamepadTextInput
*/
const int k_EFloatingGamepadTextInputModeModeSingleLine = 0; // Enter dismisses the keyboard
const int k_EFloatingGamepadTextInputModeModeMultipleLines = 1; // User needs to explicitly dismiss the keyboard
const int k_EFloatingGamepadTextInputModeModeEmail = 2; // Keyboard is displayed in a special mode that makes it easier to enter emails
const int k_EFloatingGamepadTextInputModeModeNumeric = 3; // Numeric keypad is shown
char deeplink[128];
int mode;
switch (SDL_GetTextInputType(props)) {
case SDL_TEXTINPUT_TYPE_TEXT_EMAIL:
mode = k_EFloatingGamepadTextInputModeModeEmail;
break;
case SDL_TEXTINPUT_TYPE_NUMBER:
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_HIDDEN:
case SDL_TEXTINPUT_TYPE_NUMBER_PASSWORD_VISIBLE:
mode = k_EFloatingGamepadTextInputModeModeNumeric;
break;
default:
if (SDL_GetTextInputMultiline(props)) {
mode = k_EFloatingGamepadTextInputModeModeMultipleLines;
} else {
mode = k_EFloatingGamepadTextInputModeModeSingleLine;
}
break;
}
(void)SDL_snprintf(deeplink, sizeof(deeplink),
"steam://open/keyboard?XPosition=0&YPosition=0&Width=0&Height=0&Mode=%d",
mode);
SDL_OpenURL(deeplink);
videodata->steam_keyboard_open = true;
}
}
void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_VideoData *videodata = _this->internal;
if (videodata->is_steam_deck) {
SDL_OpenURL("steam://close/keyboard");
videodata->steam_keyboard_open = false;
}
}
bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_VideoData *videodata = _this->internal;
return videodata->steam_keyboard_open;
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11keyboard_h_
#define SDL_x11keyboard_h_
extern bool X11_InitKeyboard(SDL_VideoDevice *_this);
extern void X11_UpdateKeymap(SDL_VideoDevice *_this, bool send_event);
extern void X11_QuitKeyboard(SDL_VideoDevice *_this);
extern void X11_CreateInputContext(SDL_WindowData *data);
extern void X11_ClearComposition(SDL_WindowData *data);
extern bool X11_StartTextInput(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern bool X11_StopTextInput(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_UpdateTextInputArea(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this);
extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID props);
extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window);
extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask);
#endif // SDL_x11keyboard_h_

View file

@ -0,0 +1,887 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "SDL_x11dyn.h"
#include "SDL_x11messagebox.h"
#include <X11/keysym.h>
#include <locale.h>
#define SDL_FORK_MESSAGEBOX 1
#define SDL_SET_LOCALE 1
#if SDL_FORK_MESSAGEBOX
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#endif
#define MAX_BUTTONS 8 // Maximum number of buttons supported
#define MIN_BUTTON_WIDTH 64 // Minimum button width
#define MIN_DIALOG_WIDTH 200 // Minimum dialog width
#define MIN_DIALOG_HEIGHT 100 // Minimum dialog height
static const char g_MessageBoxFontLatin1[] =
"-*-*-medium-r-normal--0-120-*-*-p-0-iso8859-1";
static const char* g_MessageBoxFont[] = {
"-*-*-medium-r-normal--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1)
"-*-*-medium-r-*--*-120-*-*-*-*-iso10646-1", // explicitly unicode (iso10646-1)
"-misc-*-*-*-*--*-*-*-*-*-*-iso10646-1", // misc unicode (fix for some systems)
"-*-*-*-*-*--*-*-*-*-*-*-iso10646-1", // just give me anything Unicode.
"-*-*-medium-r-normal--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out.
"-*-*-medium-r-*--*-120-*-*-*-*-iso8859-1", // explicitly latin1, in case low-ASCII works out.
"-misc-*-*-*-*--*-*-*-*-*-*-iso8859-1", // misc latin1 (fix for some systems)
"-*-*-*-*-*--*-*-*-*-*-*-iso8859-1", // just give me anything latin1.
NULL
};
static const SDL_MessageBoxColor g_default_colors[SDL_MESSAGEBOX_COLOR_COUNT] = {
{ 56, 54, 53 }, // SDL_MESSAGEBOX_COLOR_BACKGROUND,
{ 209, 207, 205 }, // SDL_MESSAGEBOX_COLOR_TEXT,
{ 140, 135, 129 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BORDER,
{ 105, 102, 99 }, // SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND,
{ 205, 202, 53 }, // SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED,
};
#define SDL_MAKE_RGB(_r, _g, _b) (((Uint32)(_r) << 16) | \
((Uint32)(_g) << 8) | \
((Uint32)(_b)))
typedef struct SDL_MessageBoxButtonDataX11
{
int x, y; // Text position
int length; // Text length
int text_width; // Text width
SDL_Rect rect; // Rectangle for entire button
const SDL_MessageBoxButtonData *buttondata; // Button data from caller
} SDL_MessageBoxButtonDataX11;
typedef struct TextLineData
{
int width; // Width of this text line
int length; // String length of this text line
const char *text; // Text for this line
} TextLineData;
typedef struct SDL_MessageBoxDataX11
{
Display *display;
int screen;
Window window;
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
XdbeBackBuffer buf;
bool xdbe; // Whether Xdbe is present or not
#endif
long event_mask;
Atom wm_protocols;
Atom wm_delete_message;
int dialog_width; // Dialog box width.
int dialog_height; // Dialog box height.
XFontSet font_set; // for UTF-8 systems
XFontStruct *font_struct; // Latin1 (ASCII) fallback.
int xtext, ytext; // Text position to start drawing at.
int numlines; // Count of Text lines.
int text_height; // Height for text lines.
TextLineData *linedata;
int *pbuttonid; // Pointer to user return buttonID value.
int button_press_index; // Index into buttondata/buttonpos for button which is pressed (or -1).
int mouse_over_index; // Index into buttondata/buttonpos for button mouse is over (or -1).
int numbuttons; // Count of buttons.
const SDL_MessageBoxButtonData *buttondata;
SDL_MessageBoxButtonDataX11 buttonpos[MAX_BUTTONS];
Uint32 color[SDL_MESSAGEBOX_COLOR_COUNT];
const SDL_MessageBoxData *messageboxdata;
} SDL_MessageBoxDataX11;
// Maximum helper for ints.
static SDL_INLINE int IntMax(int a, int b)
{
return (a > b) ? a : b;
}
// Return width and height for a string.
static void GetTextWidthHeight(SDL_MessageBoxDataX11 *data, const char *str, int nbytes, int *pwidth, int *pheight)
{
#ifdef X_HAVE_UTF8_STRING
if (SDL_X11_HAVE_UTF8) {
XRectangle overall_ink, overall_logical;
X11_Xutf8TextExtents(data->font_set, str, nbytes, &overall_ink, &overall_logical);
*pwidth = overall_logical.width;
*pheight = overall_logical.height;
} else
#endif
{
XCharStruct text_structure;
int font_direction, font_ascent, font_descent;
X11_XTextExtents(data->font_struct, str, nbytes,
&font_direction, &font_ascent, &font_descent,
&text_structure);
*pwidth = text_structure.width;
*pheight = text_structure.ascent + text_structure.descent;
}
}
// Return index of button if position x,y is contained therein.
static int GetHitButtonIndex(SDL_MessageBoxDataX11 *data, int x, int y)
{
int i;
int numbuttons = data->numbuttons;
SDL_MessageBoxButtonDataX11 *buttonpos = data->buttonpos;
for (i = 0; i < numbuttons; i++) {
SDL_Rect *rect = &buttonpos[i].rect;
if ((x >= rect->x) &&
(x <= (rect->x + rect->w)) &&
(y >= rect->y) &&
(y <= (rect->y + rect->h))) {
return i;
}
}
return -1;
}
// Initialize SDL_MessageBoxData structure and Display, etc.
static bool X11_MessageBoxInit(SDL_MessageBoxDataX11 *data, const SDL_MessageBoxData *messageboxdata, int *pbuttonid)
{
int i;
int numbuttons = messageboxdata->numbuttons;
const SDL_MessageBoxButtonData *buttondata = messageboxdata->buttons;
const SDL_MessageBoxColor *colorhints;
if (numbuttons > MAX_BUTTONS) {
return SDL_SetError("Too many buttons (%d max allowed)", MAX_BUTTONS);
}
data->dialog_width = MIN_DIALOG_WIDTH;
data->dialog_height = MIN_DIALOG_HEIGHT;
data->messageboxdata = messageboxdata;
data->buttondata = buttondata;
data->numbuttons = numbuttons;
data->pbuttonid = pbuttonid;
data->display = X11_XOpenDisplay(NULL);
if (!data->display) {
return SDL_SetError("Couldn't open X11 display");
}
#ifdef X_HAVE_UTF8_STRING
if (SDL_X11_HAVE_UTF8) {
char **missing = NULL;
int num_missing = 0;
int i_font;
for (i_font = 0; g_MessageBoxFont[i_font]; ++i_font) {
data->font_set = X11_XCreateFontSet(data->display, g_MessageBoxFont[i_font],
&missing, &num_missing, NULL);
if (missing) {
X11_XFreeStringList(missing);
}
if (data->font_set) {
break;
}
}
if (!data->font_set) {
return SDL_SetError("Couldn't load x11 message box font");
}
} else
#endif
{
data->font_struct = X11_XLoadQueryFont(data->display, g_MessageBoxFontLatin1);
if (!data->font_struct) {
return SDL_SetError("Couldn't load font %s", g_MessageBoxFontLatin1);
}
}
if (messageboxdata->colorScheme) {
colorhints = messageboxdata->colorScheme->colors;
} else {
colorhints = g_default_colors;
}
// Convert our SDL_MessageBoxColor r,g,b values to packed RGB format.
for (i = 0; i < SDL_MESSAGEBOX_COLOR_COUNT; i++) {
data->color[i] = SDL_MAKE_RGB(colorhints[i].r, colorhints[i].g, colorhints[i].b);
}
return true;
}
static int CountLinesOfText(const char *text)
{
int result = 0;
while (text && *text) {
const char *lf = SDL_strchr(text, '\n');
result++; // even without an endline, this counts as a line.
text = lf ? lf + 1 : NULL;
}
return result;
}
// Calculate and initialize text and button locations.
static bool X11_MessageBoxInitPositions(SDL_MessageBoxDataX11 *data)
{
int i;
int ybuttons;
int text_width_max = 0;
int button_text_height = 0;
int button_width = MIN_BUTTON_WIDTH;
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
// Go over text and break linefeeds into separate lines.
if (messageboxdata && messageboxdata->message[0]) {
const char *text = messageboxdata->message;
const int linecount = CountLinesOfText(text);
TextLineData *plinedata = (TextLineData *)SDL_malloc(sizeof(TextLineData) * linecount);
if (!plinedata) {
return false;
}
data->linedata = plinedata;
data->numlines = linecount;
for (i = 0; i < linecount; i++, plinedata++) {
const char *lf = SDL_strchr(text, '\n');
const int length = lf ? (lf - text) : SDL_strlen(text);
int height;
plinedata->text = text;
GetTextWidthHeight(data, text, length, &plinedata->width, &height);
// Text and widths are the largest we've ever seen.
data->text_height = IntMax(data->text_height, height);
text_width_max = IntMax(text_width_max, plinedata->width);
plinedata->length = length;
if (lf && (lf > text) && (lf[-1] == '\r')) {
plinedata->length--;
}
text += length + 1;
// Break if there are no more linefeeds.
if (!lf) {
break;
}
}
// Bump up the text height slightly.
data->text_height += 2;
}
// Loop through all buttons and calculate the button widths and height.
for (i = 0; i < data->numbuttons; i++) {
int height;
data->buttonpos[i].buttondata = &data->buttondata[i];
data->buttonpos[i].length = SDL_strlen(data->buttondata[i].text);
GetTextWidthHeight(data, data->buttondata[i].text, SDL_strlen(data->buttondata[i].text),
&data->buttonpos[i].text_width, &height);
button_width = IntMax(button_width, data->buttonpos[i].text_width);
button_text_height = IntMax(button_text_height, height);
}
if (data->numlines) {
// x,y for this line of text.
data->xtext = data->text_height;
data->ytext = data->text_height + data->text_height;
// Bump button y down to bottom of text.
ybuttons = 3 * data->ytext / 2 + (data->numlines - 1) * data->text_height;
// Bump the dialog box width and height up if needed.
data->dialog_width = IntMax(data->dialog_width, 2 * data->xtext + text_width_max);
data->dialog_height = IntMax(data->dialog_height, ybuttons);
} else {
// Button y starts at height of button text.
ybuttons = button_text_height;
}
if (data->numbuttons) {
int x, y;
int width_of_buttons;
int button_spacing = button_text_height;
int button_height = 2 * button_text_height;
// Bump button width up a bit.
button_width += button_text_height;
// Get width of all buttons lined up.
width_of_buttons = data->numbuttons * button_width + (data->numbuttons - 1) * button_spacing;
// Bump up dialog width and height if buttons are wider than text.
data->dialog_width = IntMax(data->dialog_width, width_of_buttons + 2 * button_spacing);
data->dialog_height = IntMax(data->dialog_height, ybuttons + 2 * button_height);
// Location for first button.
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
x = data->dialog_width - (data->dialog_width - width_of_buttons) / 2 - (button_width + button_spacing);
} else {
x = (data->dialog_width - width_of_buttons) / 2;
}
y = ybuttons + (data->dialog_height - ybuttons - button_height) / 2;
for (i = 0; i < data->numbuttons; i++) {
// Button coordinates.
data->buttonpos[i].rect.x = x;
data->buttonpos[i].rect.y = y;
data->buttonpos[i].rect.w = button_width;
data->buttonpos[i].rect.h = button_height;
// Button text coordinates.
data->buttonpos[i].x = x + (button_width - data->buttonpos[i].text_width) / 2;
data->buttonpos[i].y = y + (button_height - button_text_height - 1) / 2 + button_text_height;
// Scoot over for next button.
if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_RIGHT_TO_LEFT) {
x -= button_width + button_spacing;
} else {
x += button_width + button_spacing;
}
}
}
return true;
}
// Free SDL_MessageBoxData data.
static void X11_MessageBoxShutdown(SDL_MessageBoxDataX11 *data)
{
if (data->font_set) {
X11_XFreeFontSet(data->display, data->font_set);
data->font_set = NULL;
}
if (data->font_struct) {
X11_XFreeFont(data->display, data->font_struct);
data->font_struct = NULL;
}
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
if (SDL_X11_HAVE_XDBE && data->xdbe) {
X11_XdbeDeallocateBackBufferName(data->display, data->buf);
}
#endif
if (data->display) {
if (data->window != None) {
X11_XWithdrawWindow(data->display, data->window, data->screen);
X11_XDestroyWindow(data->display, data->window);
data->window = None;
}
X11_XCloseDisplay(data->display);
data->display = NULL;
}
SDL_free(data->linedata);
}
// Create and set up our X11 dialog box indow.
static bool X11_MessageBoxCreateWindow(SDL_MessageBoxDataX11 *data)
{
int x, y;
XSizeHints *sizehints;
XSetWindowAttributes wnd_attr;
Atom _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DIALOG;
Display *display = data->display;
SDL_WindowData *windowdata = NULL;
const SDL_MessageBoxData *messageboxdata = data->messageboxdata;
if (messageboxdata->window) {
SDL_DisplayData *displaydata = SDL_GetDisplayDriverDataForWindow(messageboxdata->window);
windowdata = messageboxdata->window->internal;
data->screen = displaydata->screen;
} else {
data->screen = DefaultScreen(display);
}
data->event_mask = ExposureMask |
ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask |
StructureNotifyMask | FocusChangeMask | PointerMotionMask;
wnd_attr.event_mask = data->event_mask;
data->window = X11_XCreateWindow(
display, RootWindow(display, data->screen),
0, 0,
data->dialog_width, data->dialog_height,
0, CopyFromParent, InputOutput, CopyFromParent,
CWEventMask, &wnd_attr);
if (data->window == None) {
return SDL_SetError("Couldn't create X window");
}
if (windowdata) {
Atom _NET_WM_STATE = X11_XInternAtom(display, "_NET_WM_STATE", False);
Atom stateatoms[16];
size_t statecount = 0;
// Set some message-boxy window states when attached to a parent window...
// we skip the taskbar since this will pop to the front when the parent window is clicked in the taskbar, etc
stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_FOCUSED", False);
stateatoms[statecount++] = X11_XInternAtom(display, "_NET_WM_STATE_MODAL", False);
SDL_assert(statecount <= SDL_arraysize(stateatoms));
X11_XChangeProperty(display, data->window, _NET_WM_STATE, XA_ATOM, 32,
PropModeReplace, (unsigned char *)stateatoms, statecount);
// http://tronche.com/gui/x/icccm/sec-4.html#WM_TRANSIENT_FOR
X11_XSetTransientForHint(display, data->window, windowdata->xwindow);
}
SDL_X11_SetWindowTitle(display, data->window, (char *)messageboxdata->title);
// Let the window manager know this is a dialog box
_NET_WM_WINDOW_TYPE = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
_NET_WM_WINDOW_TYPE_DIALOG = X11_XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
X11_XChangeProperty(display, data->window, _NET_WM_WINDOW_TYPE, XA_ATOM, 32,
PropModeReplace,
(unsigned char *)&_NET_WM_WINDOW_TYPE_DIALOG, 1);
// Allow the window to be deleted by the window manager
data->wm_delete_message = X11_XInternAtom(display, "WM_DELETE_WINDOW", False);
X11_XSetWMProtocols(display, data->window, &data->wm_delete_message, 1);
data->wm_protocols = X11_XInternAtom(display, "WM_PROTOCOLS", False);
if (windowdata) {
XWindowAttributes attrib;
Window dummy;
X11_XGetWindowAttributes(display, windowdata->xwindow, &attrib);
x = attrib.x + (attrib.width - data->dialog_width) / 2;
y = attrib.y + (attrib.height - data->dialog_height) / 3;
X11_XTranslateCoordinates(display, windowdata->xwindow, RootWindow(display, data->screen), x, y, &x, &y, &dummy);
} else {
const SDL_VideoDevice *dev = SDL_GetVideoDevice();
if (dev && dev->displays && dev->num_displays > 0) {
const SDL_VideoDisplay *dpy = dev->displays[0];
const SDL_DisplayData *dpydata = dpy->internal;
x = dpydata->x + ((dpy->current_mode->w - data->dialog_width) / 2);
y = dpydata->y + ((dpy->current_mode->h - data->dialog_height) / 3);
} else { // oh well. This will misposition on a multi-head setup. Init first next time.
x = (DisplayWidth(display, data->screen) - data->dialog_width) / 2;
y = (DisplayHeight(display, data->screen) - data->dialog_height) / 3;
}
}
X11_XMoveWindow(display, data->window, x, y);
sizehints = X11_XAllocSizeHints();
if (sizehints) {
sizehints->flags = USPosition | USSize | PMaxSize | PMinSize;
sizehints->x = x;
sizehints->y = y;
sizehints->width = data->dialog_width;
sizehints->height = data->dialog_height;
sizehints->min_width = sizehints->max_width = data->dialog_width;
sizehints->min_height = sizehints->max_height = data->dialog_height;
X11_XSetWMNormalHints(display, data->window, sizehints);
X11_XFree(sizehints);
}
X11_XMapRaised(display, data->window);
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
// Initialise a back buffer for double buffering
if (SDL_X11_HAVE_XDBE) {
int xdbe_major, xdbe_minor;
if (X11_XdbeQueryExtension(display, &xdbe_major, &xdbe_minor) != 0) {
data->xdbe = true;
data->buf = X11_XdbeAllocateBackBufferName(display, data->window, XdbeUndefined);
} else {
data->xdbe = false;
}
}
#endif
return true;
}
// Draw our message box.
static void X11_MessageBoxDraw(SDL_MessageBoxDataX11 *data, GC ctx)
{
int i;
Drawable window = data->window;
Display *display = data->display;
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
if (SDL_X11_HAVE_XDBE && data->xdbe) {
window = data->buf;
X11_XdbeBeginIdiom(data->display);
}
#endif
X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND]);
X11_XFillRectangle(display, window, ctx, 0, 0, data->dialog_width, data->dialog_height);
X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_TEXT]);
for (i = 0; i < data->numlines; i++) {
TextLineData *plinedata = &data->linedata[i];
#ifdef X_HAVE_UTF8_STRING
if (SDL_X11_HAVE_UTF8) {
X11_Xutf8DrawString(display, window, data->font_set, ctx,
data->xtext, data->ytext + i * data->text_height,
plinedata->text, plinedata->length);
} else
#endif
{
X11_XDrawString(display, window, ctx,
data->xtext, data->ytext + i * data->text_height,
plinedata->text, plinedata->length);
}
}
for (i = 0; i < data->numbuttons; i++) {
SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i];
const SDL_MessageBoxButtonData *buttondata = buttondatax11->buttondata;
int border = (buttondata->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) ? 2 : 0;
int offset = ((data->mouse_over_index == i) && (data->button_press_index == data->mouse_over_index)) ? 1 : 0;
X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND]);
X11_XFillRectangle(display, window, ctx,
buttondatax11->rect.x - border, buttondatax11->rect.y - border,
buttondatax11->rect.w + 2 * border, buttondatax11->rect.h + 2 * border);
X11_XSetForeground(display, ctx, data->color[SDL_MESSAGEBOX_COLOR_BUTTON_BORDER]);
X11_XDrawRectangle(display, window, ctx,
buttondatax11->rect.x, buttondatax11->rect.y,
buttondatax11->rect.w, buttondatax11->rect.h);
X11_XSetForeground(display, ctx, (data->mouse_over_index == i) ? data->color[SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] : data->color[SDL_MESSAGEBOX_COLOR_TEXT]);
#ifdef X_HAVE_UTF8_STRING
if (SDL_X11_HAVE_UTF8) {
X11_Xutf8DrawString(display, window, data->font_set, ctx,
buttondatax11->x + offset,
buttondatax11->y + offset,
buttondata->text, buttondatax11->length);
} else
#endif
{
X11_XDrawString(display, window, ctx,
buttondatax11->x + offset, buttondatax11->y + offset,
buttondata->text, buttondatax11->length);
}
}
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
if (SDL_X11_HAVE_XDBE && data->xdbe) {
XdbeSwapInfo swap_info;
swap_info.swap_window = data->window;
swap_info.swap_action = XdbeUndefined;
X11_XdbeSwapBuffers(data->display, &swap_info, 1);
X11_XdbeEndIdiom(data->display);
}
#endif
}
// NOLINTNEXTLINE(readability-non-const-parameter): cannot make XPointer a const pointer due to typedef
static Bool X11_MessageBoxEventTest(Display *display, XEvent *event, XPointer arg)
{
const SDL_MessageBoxDataX11 *data = (const SDL_MessageBoxDataX11 *)arg;
return ((event->xany.display == data->display) && (event->xany.window == data->window)) ? True : False;
}
// Loop and handle message box event messages until something kills it.
static bool X11_MessageBoxLoop(SDL_MessageBoxDataX11 *data)
{
GC ctx;
XGCValues ctx_vals;
bool close_dialog = false;
bool has_focus = true;
KeySym last_key_pressed = XK_VoidSymbol;
unsigned long gcflags = GCForeground | GCBackground;
#ifdef X_HAVE_UTF8_STRING
const int have_utf8 = SDL_X11_HAVE_UTF8;
#else
const int have_utf8 = 0;
#endif
SDL_zero(ctx_vals);
ctx_vals.foreground = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND];
ctx_vals.background = data->color[SDL_MESSAGEBOX_COLOR_BACKGROUND];
if (!have_utf8) {
gcflags |= GCFont;
ctx_vals.font = data->font_struct->fid;
}
ctx = X11_XCreateGC(data->display, data->window, gcflags, &ctx_vals);
if (ctx == None) {
return SDL_SetError("Couldn't create graphics context");
}
data->button_press_index = -1; // Reset what button is currently depressed.
data->mouse_over_index = -1; // Reset what button the mouse is over.
while (!close_dialog) {
XEvent e;
bool draw = true;
// can't use XWindowEvent() because it can't handle ClientMessage events.
// can't use XNextEvent() because we only want events for this window.
X11_XIfEvent(data->display, &e, X11_MessageBoxEventTest, (XPointer)data);
/* If X11_XFilterEvent returns True, then some input method has filtered the
event, and the client should discard the event. */
if ((e.type != Expose) && X11_XFilterEvent(&e, None)) {
continue;
}
switch (e.type) {
case Expose:
if (e.xexpose.count > 0) {
draw = false;
}
break;
case FocusIn:
// Got focus.
has_focus = true;
break;
case FocusOut:
// lost focus. Reset button and mouse info.
has_focus = false;
data->button_press_index = -1;
data->mouse_over_index = -1;
break;
case MotionNotify:
if (has_focus) {
// Mouse moved...
const int previndex = data->mouse_over_index;
data->mouse_over_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
if (data->mouse_over_index == previndex) {
draw = false;
}
}
break;
case ClientMessage:
if (e.xclient.message_type == data->wm_protocols &&
e.xclient.format == 32 &&
e.xclient.data.l[0] == data->wm_delete_message) {
close_dialog = true;
}
break;
case KeyPress:
// Store key press - we make sure in key release that we got both.
last_key_pressed = X11_XLookupKeysym(&e.xkey, 0);
break;
case KeyRelease:
{
Uint32 mask = 0;
KeySym key = X11_XLookupKeysym(&e.xkey, 0);
// If this is a key release for something we didn't get the key down for, then bail.
if (key != last_key_pressed) {
break;
}
if (key == XK_Escape) {
mask = SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT;
} else if ((key == XK_Return) || (key == XK_KP_Enter)) {
mask = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
}
if (mask) {
int i;
// Look for first button with this mask set, and return it if found.
for (i = 0; i < data->numbuttons; i++) {
SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[i];
if (buttondatax11->buttondata->flags & mask) {
*data->pbuttonid = buttondatax11->buttondata->buttonID;
close_dialog = true;
break;
}
}
}
break;
}
case ButtonPress:
data->button_press_index = -1;
if (e.xbutton.button == Button1) {
// Find index of button they clicked on.
data->button_press_index = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
}
break;
case ButtonRelease:
// If button is released over the same button that was clicked down on, then return it.
if ((e.xbutton.button == Button1) && (data->button_press_index >= 0)) {
int button = GetHitButtonIndex(data, e.xbutton.x, e.xbutton.y);
if (data->button_press_index == button) {
SDL_MessageBoxButtonDataX11 *buttondatax11 = &data->buttonpos[button];
*data->pbuttonid = buttondatax11->buttondata->buttonID;
close_dialog = true;
}
}
data->button_press_index = -1;
break;
}
if (draw) {
// Draw our dialog box.
X11_MessageBoxDraw(data, ctx);
}
}
X11_XFreeGC(data->display, ctx);
return true;
}
static bool X11_ShowMessageBoxImpl(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
bool result = false;
SDL_MessageBoxDataX11 data;
#if SDL_SET_LOCALE
char *origlocale;
#endif
SDL_zero(data);
if (!SDL_X11_LoadSymbols()) {
return false;
}
#if SDL_SET_LOCALE
origlocale = setlocale(LC_ALL, NULL);
if (origlocale) {
origlocale = SDL_strdup(origlocale);
if (!origlocale) {
return false;
}
(void)setlocale(LC_ALL, "");
}
#endif
// This code could get called from multiple threads maybe?
X11_XInitThreads();
// Initialize the return buttonID value to -1 (for error or dialogbox closed).
*buttonID = -1;
// Init and display the message box.
if (X11_MessageBoxInit(&data, messageboxdata, buttonID) &&
X11_MessageBoxInitPositions(&data) &&
X11_MessageBoxCreateWindow(&data)) {
result = X11_MessageBoxLoop(&data);
}
X11_MessageBoxShutdown(&data);
#if SDL_SET_LOCALE
if (origlocale) {
(void)setlocale(LC_ALL, origlocale);
SDL_free(origlocale);
}
#endif
return result;
}
// Display an x11 message box.
bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID)
{
#if SDL_FORK_MESSAGEBOX
// Use a child process to protect against setlocale(). Annoying.
pid_t pid;
int fds[2];
int status = 0;
bool result = true;
if (pipe(fds) == -1) {
return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well.
}
pid = fork();
if (pid == -1) { // failed
close(fds[0]);
close(fds[1]);
return X11_ShowMessageBoxImpl(messageboxdata, buttonID); // oh well.
} else if (pid == 0) { // we're the child
int exitcode = 0;
close(fds[0]);
result = X11_ShowMessageBoxImpl(messageboxdata, buttonID);
if (write(fds[1], &result, sizeof(result)) != sizeof(result)) {
exitcode = 1;
} else if (write(fds[1], buttonID, sizeof(*buttonID)) != sizeof(*buttonID)) {
exitcode = 1;
}
close(fds[1]);
_exit(exitcode); // don't run atexit() stuff, static destructors, etc.
} else { // we're the parent
pid_t rc;
close(fds[1]);
do {
rc = waitpid(pid, &status, 0);
} while ((rc == -1) && (errno == EINTR));
SDL_assert(rc == pid); // not sure what to do if this fails.
if ((rc == -1) || (!WIFEXITED(status)) || (WEXITSTATUS(status) != 0)) {
result = SDL_SetError("msgbox child process failed");
} else if ((read(fds[0], &result, sizeof(result)) != sizeof(result)) ||
(read(fds[0], buttonID, sizeof(*buttonID)) != sizeof(*buttonID))) {
result = SDL_SetError("read from msgbox child process failed");
*buttonID = 0;
}
close(fds[0]);
return result;
}
#else
return X11_ShowMessageBoxImpl(messageboxdata, buttonID);
#endif
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,31 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef SDL_x11messagebox_h_
#define SDL_x11messagebox_h_
#ifdef SDL_VIDEO_DRIVER_X11
extern bool X11_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonID);
#endif // SDL_VIDEO_DRIVER_X11
#endif // SDL_x11messagebox_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,69 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11modes_h_
#define SDL_x11modes_h_
struct SDL_DisplayData
{
int screen;
Visual *visual;
int depth;
int scanline_pad;
int x;
int y;
Uint64 mode_switch_deadline_ns;
bool use_xrandr;
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
RROutput xrandr_output;
char connector_name[16];
#endif
};
struct SDL_DisplayModeData
{
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
RRMode xrandr_mode;
#else
int unused; // just so struct isn't empty.
#endif
};
extern bool X11_InitModes(SDL_VideoDevice *_this);
extern bool X11_GetDisplayModes(SDL_VideoDevice *_this, SDL_VideoDisplay *display);
extern bool X11_SetDisplayMode(SDL_VideoDevice *_this, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
extern void X11_QuitModes(SDL_VideoDevice *_this);
// Some utility functions for working with visuals
extern bool X11_GetVisualInfoFromVisual(Display *display, Visual *visual, XVisualInfo *vinfo);
extern SDL_PixelFormat X11_GetPixelFormatFromVisualInfo(Display *display, XVisualInfo *vinfo);
extern bool X11_GetDisplayBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect);
extern bool X11_GetDisplayUsableBounds(SDL_VideoDevice *_this, SDL_VideoDisplay *sdl_display, SDL_Rect *rect);
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
extern void X11_HandleXRandREvent(SDL_VideoDevice *_this, const XEvent *xevent);
#endif
#endif // SDL_x11modes_h_

View file

@ -0,0 +1,552 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include <X11/cursorfont.h>
#include "SDL_x11video.h"
#include "SDL_x11mouse.h"
#include "SDL_x11xinput2.h"
#include "../SDL_video_c.h"
#include "../../events/SDL_mouse_c.h"
struct SDL_CursorData
{
Cursor cursor;
};
// FIXME: Find a better place to put this...
static Cursor x11_empty_cursor = None;
static bool x11_cursor_visible = true;
static SDL_Cursor *sys_cursors[SDL_HITTEST_RESIZE_LEFT + 1];
static Display *GetDisplay(void)
{
return SDL_GetVideoDevice()->internal->display;
}
static Cursor X11_CreateEmptyCursor(void)
{
if (x11_empty_cursor == None) {
Display *display = GetDisplay();
char data[1];
XColor color;
Pixmap pixmap;
SDL_zeroa(data);
color.red = color.green = color.blue = 0;
pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
data, 1, 1);
if (pixmap) {
x11_empty_cursor = X11_XCreatePixmapCursor(display, pixmap, pixmap,
&color, &color, 0, 0);
X11_XFreePixmap(display, pixmap);
}
}
return x11_empty_cursor;
}
static void X11_DestroyEmptyCursor(void)
{
if (x11_empty_cursor != None) {
X11_XFreeCursor(GetDisplay(), x11_empty_cursor);
x11_empty_cursor = None;
}
}
static SDL_Cursor *X11_CreateCursorAndData(Cursor x11_cursor)
{
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
if (cursor) {
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_free(cursor);
return NULL;
}
data->cursor = x11_cursor;
cursor->internal = data;
}
return cursor;
}
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
static Cursor X11_CreateXCursorCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
Display *display = GetDisplay();
Cursor cursor = None;
XcursorImage *image;
image = X11_XcursorImageCreate(surface->w, surface->h);
if (!image) {
SDL_OutOfMemory();
return None;
}
image->xhot = hot_x;
image->yhot = hot_y;
image->delay = 0;
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
SDL_assert(surface->pitch == surface->w * 4);
SDL_memcpy(image->pixels, surface->pixels, (size_t)surface->h * surface->pitch);
cursor = X11_XcursorImageLoadCursor(display, image);
X11_XcursorImageDestroy(image);
return cursor;
}
#endif // SDL_VIDEO_DRIVER_X11_XCURSOR
static Cursor X11_CreatePixmapCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
Display *display = GetDisplay();
XColor fg, bg;
Cursor cursor = None;
Uint32 *ptr;
Uint8 *data_bits, *mask_bits;
Pixmap data_pixmap, mask_pixmap;
int x, y;
unsigned int rfg, gfg, bfg, rbg, gbg, bbg, fgBits, bgBits;
size_t width_bytes = ((surface->w + 7) & ~((size_t)7)) / 8;
data_bits = SDL_calloc(1, surface->h * width_bytes);
if (!data_bits) {
return None;
}
mask_bits = SDL_calloc(1, surface->h * width_bytes);
if (!mask_bits) {
SDL_free(data_bits);
return None;
}
// Code below assumes ARGB pixel format
SDL_assert(surface->format == SDL_PIXELFORMAT_ARGB8888);
rfg = gfg = bfg = rbg = gbg = bbg = fgBits = bgBits = 0;
for (y = 0; y < surface->h; ++y) {
ptr = (Uint32 *)((Uint8 *)surface->pixels + y * surface->pitch);
for (x = 0; x < surface->w; ++x) {
int alpha = (*ptr >> 24) & 0xff;
int red = (*ptr >> 16) & 0xff;
int green = (*ptr >> 8) & 0xff;
int blue = (*ptr >> 0) & 0xff;
if (alpha > 25) {
mask_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
if ((red + green + blue) > 0x40) {
fgBits++;
rfg += red;
gfg += green;
bfg += blue;
data_bits[y * width_bytes + x / 8] |= (0x01 << (x % 8));
} else {
bgBits++;
rbg += red;
gbg += green;
bbg += blue;
}
}
++ptr;
}
}
if (fgBits) {
fg.red = rfg * 257 / fgBits;
fg.green = gfg * 257 / fgBits;
fg.blue = bfg * 257 / fgBits;
} else {
fg.red = fg.green = fg.blue = 0;
}
if (bgBits) {
bg.red = rbg * 257 / bgBits;
bg.green = gbg * 257 / bgBits;
bg.blue = bbg * 257 / bgBits;
} else {
bg.red = bg.green = bg.blue = 0;
}
data_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
(char *)data_bits,
surface->w, surface->h);
mask_pixmap = X11_XCreateBitmapFromData(display, DefaultRootWindow(display),
(char *)mask_bits,
surface->w, surface->h);
cursor = X11_XCreatePixmapCursor(display, data_pixmap, mask_pixmap,
&fg, &bg, hot_x, hot_y);
X11_XFreePixmap(display, data_pixmap);
X11_XFreePixmap(display, mask_pixmap);
SDL_free(data_bits);
SDL_free(mask_bits);
return cursor;
}
static SDL_Cursor *X11_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
{
Cursor x11_cursor = None;
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
if (SDL_X11_HAVE_XCURSOR) {
x11_cursor = X11_CreateXCursorCursor(surface, hot_x, hot_y);
}
#endif
if (x11_cursor == None) {
x11_cursor = X11_CreatePixmapCursor(surface, hot_x, hot_y);
}
return X11_CreateCursorAndData(x11_cursor);
}
static unsigned int GetLegacySystemCursorShape(SDL_SystemCursor id)
{
switch (id) {
// X Font Cursors reference:
// http://tronche.com/gui/x/xlib/appendix/b/
case SDL_SYSTEM_CURSOR_DEFAULT: return XC_left_ptr;
case SDL_SYSTEM_CURSOR_TEXT: return XC_xterm;
case SDL_SYSTEM_CURSOR_WAIT: return XC_watch;
case SDL_SYSTEM_CURSOR_CROSSHAIR: return XC_tcross;
case SDL_SYSTEM_CURSOR_PROGRESS: return XC_watch;
case SDL_SYSTEM_CURSOR_NWSE_RESIZE: return XC_top_left_corner;
case SDL_SYSTEM_CURSOR_NESW_RESIZE: return XC_top_right_corner;
case SDL_SYSTEM_CURSOR_EW_RESIZE: return XC_sb_h_double_arrow;
case SDL_SYSTEM_CURSOR_NS_RESIZE: return XC_sb_v_double_arrow;
case SDL_SYSTEM_CURSOR_MOVE: return XC_fleur;
case SDL_SYSTEM_CURSOR_NOT_ALLOWED: return XC_pirate;
case SDL_SYSTEM_CURSOR_POINTER: return XC_hand2;
case SDL_SYSTEM_CURSOR_NW_RESIZE: return XC_top_left_corner;
case SDL_SYSTEM_CURSOR_N_RESIZE: return XC_top_side;
case SDL_SYSTEM_CURSOR_NE_RESIZE: return XC_top_right_corner;
case SDL_SYSTEM_CURSOR_E_RESIZE: return XC_right_side;
case SDL_SYSTEM_CURSOR_SE_RESIZE: return XC_bottom_right_corner;
case SDL_SYSTEM_CURSOR_S_RESIZE: return XC_bottom_side;
case SDL_SYSTEM_CURSOR_SW_RESIZE: return XC_bottom_left_corner;
case SDL_SYSTEM_CURSOR_W_RESIZE: return XC_left_side;
case SDL_SYSTEM_CURSOR_COUNT: break; // so the compiler might notice if an enum value is missing here.
}
SDL_assert(0);
return 0;
}
static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
{
SDL_Cursor *cursor = NULL;
Display *dpy = GetDisplay();
Cursor x11_cursor = None;
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
if (SDL_X11_HAVE_XCURSOR) {
x11_cursor = X11_XcursorLibraryLoadCursor(dpy, SDL_GetCSSCursorName(id, NULL));
}
#endif
if (x11_cursor == None) {
x11_cursor = X11_XCreateFontCursor(dpy, GetLegacySystemCursorShape(id));
}
if (x11_cursor != None) {
cursor = X11_CreateCursorAndData(x11_cursor);
}
return cursor;
}
static SDL_Cursor *X11_CreateDefaultCursor(void)
{
SDL_SystemCursor id = SDL_GetDefaultSystemCursor();
return X11_CreateSystemCursor(id);
}
static void X11_FreeCursor(SDL_Cursor *cursor)
{
Cursor x11_cursor = cursor->internal->cursor;
if (x11_cursor != None) {
X11_XFreeCursor(GetDisplay(), x11_cursor);
}
SDL_free(cursor->internal);
SDL_free(cursor);
}
static bool X11_ShowCursor(SDL_Cursor *cursor)
{
Cursor x11_cursor = 0;
if (cursor) {
x11_cursor = cursor->internal->cursor;
} else {
x11_cursor = X11_CreateEmptyCursor();
}
// FIXME: Is there a better way than this?
{
SDL_VideoDevice *video = SDL_GetVideoDevice();
Display *display = GetDisplay();
SDL_Window *window;
x11_cursor_visible = !!cursor;
for (window = video->windows; window; window = window->next) {
SDL_WindowData *data = window->internal;
if (data) {
if (x11_cursor != None) {
X11_XDefineCursor(display, data->xwindow, x11_cursor);
} else {
X11_XUndefineCursor(display, data->xwindow);
}
}
}
X11_XFlush(display);
}
return true;
}
static void X11_WarpMouseInternal(Window xwindow, float x, float y)
{
SDL_VideoData *videodata = SDL_GetVideoDevice()->internal;
Display *display = videodata->display;
bool warp_hack = false;
// XWayland will only warp the cursor if it is hidden, so this workaround is required.
if (videodata->is_xwayland && x11_cursor_visible) {
warp_hack = true;
}
if (warp_hack) {
X11_ShowCursor(NULL);
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
int deviceid = 0;
if (X11_Xinput2IsInitialized()) {
/* It seems XIWarpPointer() doesn't work correctly on multi-head setups:
* https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea
*/
if (ScreenCount(display) == 1) {
X11_XIGetClientPointer(display, None, &deviceid);
}
}
if (deviceid != 0) {
SDL_assert(SDL_X11_HAVE_XINPUT2);
X11_XIWarpPointer(display, deviceid, None, xwindow, 0.0, 0.0, 0, 0, x, y);
} else
#endif
{
X11_XWarpPointer(display, None, xwindow, 0, 0, 0, 0, (int)x, (int)y);
}
if (warp_hack) {
X11_ShowCursor(SDL_GetCursor());
}
X11_XSync(display, False);
videodata->global_mouse_changed = true;
}
static bool X11_WarpMouse(SDL_Window *window, float x, float y)
{
SDL_WindowData *data = window->internal;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
// If we have no barrier, we need to warp
if (data->pointer_barrier_active == false) {
X11_WarpMouseInternal(data->xwindow, x, y);
}
#else
X11_WarpMouseInternal(data->xwindow, x, y);
#endif
return true;
}
static bool X11_WarpMouseGlobal(float x, float y)
{
X11_WarpMouseInternal(DefaultRootWindow(GetDisplay()), x, y);
return true;
}
static bool X11_SetRelativeMouseMode(bool enabled)
{
if (!X11_Xinput2IsInitialized()) {
return SDL_Unsupported();
}
return true;
}
static bool X11_CaptureMouse(SDL_Window *window)
{
Display *display = GetDisplay();
SDL_Window *mouse_focus = SDL_GetMouseFocus();
if (window) {
SDL_WindowData *data = window->internal;
/* If XInput2 is handling the pointer input, non-confinement grabs will always fail with 'AlreadyGrabbed',
* since the pointer is being grabbed by XInput2.
*/
if (!data->xinput2_mouse_enabled || data->mouse_grabbed) {
const unsigned int mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
Window confined = (data->mouse_grabbed ? data->xwindow : None);
const int rc = X11_XGrabPointer(display, data->xwindow, False,
mask, GrabModeAsync, GrabModeAsync,
confined, None, CurrentTime);
if (rc != GrabSuccess) {
return SDL_SetError("X server refused mouse capture");
}
if (data->mouse_grabbed) {
// XGrabPointer can warp the cursor when confining, so update the coordinates.
data->videodata->global_mouse_changed = true;
}
}
} else if (mouse_focus) {
SDL_UpdateWindowGrab(mouse_focus);
} else {
X11_XUngrabPointer(display, CurrentTime);
}
X11_XSync(display, False);
return true;
}
static SDL_MouseButtonFlags X11_GetGlobalMouseState(float *x, float *y)
{
SDL_VideoData *videodata = SDL_GetVideoDevice()->internal;
SDL_DisplayID *displays;
Display *display = GetDisplay();
int i;
// !!! FIXME: should we XSync() here first?
if (!X11_Xinput2IsInitialized()) {
videodata->global_mouse_changed = true;
}
// check if we have this cached since XInput last saw the mouse move.
// !!! FIXME: can we just calculate this from XInput's events?
if (videodata->global_mouse_changed) {
displays = SDL_GetDisplays(NULL);
if (displays) {
for (i = 0; displays[i]; ++i) {
SDL_DisplayData *data = SDL_GetDisplayDriverData(displays[i]);
if (data) {
Window root, child;
int rootx, rooty, winx, winy;
unsigned int mask;
if (X11_XQueryPointer(display, RootWindow(display, data->screen), &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
XWindowAttributes root_attrs;
SDL_MouseButtonFlags buttons = 0;
buttons |= (mask & Button1Mask) ? SDL_BUTTON_LMASK : 0;
buttons |= (mask & Button2Mask) ? SDL_BUTTON_MMASK : 0;
buttons |= (mask & Button3Mask) ? SDL_BUTTON_RMASK : 0;
// Use the SDL state for the extended buttons - it's better than nothing
buttons |= (SDL_GetMouseState(NULL, NULL) & (SDL_BUTTON_X1MASK | SDL_BUTTON_X2MASK));
/* SDL_DisplayData->x,y point to screen origin, and adding them to mouse coordinates relative to root window doesn't do the right thing
* (observed on dual monitor setup with primary display being the rightmost one - mouse was offset to the right).
*
* Adding root position to root-relative coordinates seems to be a better way to get absolute position. */
X11_XGetWindowAttributes(display, root, &root_attrs);
videodata->global_mouse_position.x = root_attrs.x + rootx;
videodata->global_mouse_position.y = root_attrs.y + rooty;
videodata->global_mouse_buttons = buttons;
videodata->global_mouse_changed = false;
break;
}
}
}
SDL_free(displays);
}
}
SDL_assert(!videodata->global_mouse_changed); // The pointer wasn't on any X11 screen?!
*x = (float)videodata->global_mouse_position.x;
*y = (float)videodata->global_mouse_position.y;
return videodata->global_mouse_buttons;
}
void X11_InitMouse(SDL_VideoDevice *_this)
{
SDL_Mouse *mouse = SDL_GetMouse();
mouse->CreateCursor = X11_CreateCursor;
mouse->CreateSystemCursor = X11_CreateSystemCursor;
mouse->ShowCursor = X11_ShowCursor;
mouse->FreeCursor = X11_FreeCursor;
mouse->WarpMouse = X11_WarpMouse;
mouse->WarpMouseGlobal = X11_WarpMouseGlobal;
mouse->SetRelativeMouseMode = X11_SetRelativeMouseMode;
mouse->CaptureMouse = X11_CaptureMouse;
mouse->GetGlobalMouseState = X11_GetGlobalMouseState;
SDL_HitTestResult r = SDL_HITTEST_NORMAL;
while (r <= SDL_HITTEST_RESIZE_LEFT) {
switch (r) {
case SDL_HITTEST_NORMAL: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break;
case SDL_HITTEST_DRAGGABLE: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT); break;
case SDL_HITTEST_RESIZE_TOPLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NW_RESIZE); break;
case SDL_HITTEST_RESIZE_TOP: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_N_RESIZE); break;
case SDL_HITTEST_RESIZE_TOPRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_NE_RESIZE); break;
case SDL_HITTEST_RESIZE_RIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_E_RESIZE); break;
case SDL_HITTEST_RESIZE_BOTTOMRIGHT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SE_RESIZE); break;
case SDL_HITTEST_RESIZE_BOTTOM: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_S_RESIZE); break;
case SDL_HITTEST_RESIZE_BOTTOMLEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_SW_RESIZE); break;
case SDL_HITTEST_RESIZE_LEFT: sys_cursors[r] = X11_CreateSystemCursor(SDL_SYSTEM_CURSOR_W_RESIZE); break;
}
r++;
}
SDL_SetDefaultCursor(X11_CreateDefaultCursor());
}
void X11_QuitMouse(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
SDL_XInput2DeviceInfo *i;
SDL_XInput2DeviceInfo *next;
int j;
for (j = 0; j < SDL_arraysize(sys_cursors); j++) {
X11_FreeCursor(sys_cursors[j]);
sys_cursors[j] = NULL;
}
for (i = data->mouse_device_info; i; i = next) {
next = i->next;
SDL_free(i);
}
data->mouse_device_info = NULL;
X11_DestroyEmptyCursor();
}
void X11_SetHitTestCursor(SDL_HitTestResult rc)
{
if (rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
SDL_SetCursor(NULL);
} else {
X11_ShowCursor(sys_cursors[rc]);
}
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,40 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11mouse_h_
#define SDL_x11mouse_h_
typedef struct SDL_XInput2DeviceInfo
{
int device_id;
bool relative[2];
double minval[2];
double maxval[2];
double prev_coords[2];
struct SDL_XInput2DeviceInfo *next;
} SDL_XInput2DeviceInfo;
extern void X11_InitMouse(SDL_VideoDevice *_this);
extern void X11_QuitMouse(SDL_VideoDevice *_this);
extern void X11_SetHitTestCursor(SDL_HitTestResult rc);
#endif // SDL_x11mouse_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11opengl_h_
#define SDL_x11opengl_h_
#ifdef SDL_VIDEO_OPENGL_GLX
#include <SDL3/SDL_opengl.h>
#include <GL/glx.h>
typedef void (*__GLXextFuncPtr)(void);
typedef enum SDL_GLSwapIntervalTearBehavior
{
SDL_SWAPINTERVALTEAR_UNTESTED,
SDL_SWAPINTERVALTEAR_UNKNOWN,
SDL_SWAPINTERVALTEAR_MESA,
SDL_SWAPINTERVALTEAR_NVIDIA
} SDL_GLSwapIntervalTearBehavior;
struct SDL_GLDriverData
{
int errorBase, eventBase;
bool HAS_GLX_EXT_visual_rating;
bool HAS_GLX_EXT_visual_info;
bool HAS_GLX_EXT_swap_control_tear;
bool HAS_GLX_ARB_context_flush_control;
bool HAS_GLX_ARB_create_context_robustness;
bool HAS_GLX_ARB_create_context_no_error;
/* Max version of OpenGL ES context that can be created if the
implementation supports GLX_EXT_create_context_es2_profile.
major = minor = 0 when unsupported.
*/
struct
{
int major;
int minor;
} es_profile_max_supported_version;
SDL_GLSwapIntervalTearBehavior swap_interval_tear_behavior;
Bool (*glXQueryExtension)(Display *, int *, int *);
__GLXextFuncPtr (*glXGetProcAddress)(const GLubyte *);
XVisualInfo *(*glXChooseVisual)(Display *, int, int *);
GLXContext (*glXCreateContext)(Display *, XVisualInfo *, GLXContext, Bool);
GLXContext (*glXCreateContextAttribsARB)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
GLXFBConfig *(*glXChooseFBConfig)(Display *, int, const int *, int *);
XVisualInfo *(*glXGetVisualFromFBConfig)(Display *, GLXFBConfig);
void (*glXDestroyContext)(Display *, GLXContext);
Bool (*glXMakeCurrent)(Display *, GLXDrawable, GLXContext);
void (*glXSwapBuffers)(Display *, GLXDrawable);
void (*glXQueryDrawable)(Display *, GLXDrawable, int, unsigned int *);
void (*glXSwapIntervalEXT)(Display *, GLXDrawable, int);
int (*glXSwapIntervalSGI)(int);
int (*glXSwapIntervalMESA)(int);
int (*glXGetSwapIntervalMESA)(void);
};
// OpenGL functions
extern bool X11_GL_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern SDL_FunctionPointer X11_GL_GetProcAddress(SDL_VideoDevice *_this, const char *proc);
extern void X11_GL_UnloadLibrary(SDL_VideoDevice *_this);
extern bool X11_GL_UseEGL(SDL_VideoDevice *_this);
extern XVisualInfo *X11_GL_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent);
extern SDL_GLContext X11_GL_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_GL_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window,
SDL_GLContext context);
extern bool X11_GL_SetSwapInterval(SDL_VideoDevice *_this, int interval);
extern bool X11_GL_GetSwapInterval(SDL_VideoDevice *_this, int *interval);
extern bool X11_GL_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_GL_DestroyContext(SDL_VideoDevice *_this, SDL_GLContext context);
#endif // SDL_VIDEO_OPENGL_GLX
#endif // SDL_x11opengl_h_

View file

@ -0,0 +1,152 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_OPENGL_EGL)
#include "SDL_x11video.h"
#include "SDL_x11opengles.h"
#include "SDL_x11opengl.h"
#include "SDL_x11xsync.h"
// EGL implementation of SDL OpenGL support
bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
SDL_VideoData *data = _this->internal;
// If the profile requested is not GL ES, switch over to X11_GL functions
if ((_this->gl_config.profile_mask != SDL_GL_CONTEXT_PROFILE_ES) &&
!SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
#ifdef SDL_VIDEO_OPENGL_GLX
X11_GLES_UnloadLibrary(_this);
_this->GL_LoadLibrary = X11_GL_LoadLibrary;
_this->GL_GetProcAddress = X11_GL_GetProcAddress;
_this->GL_UnloadLibrary = X11_GL_UnloadLibrary;
_this->GL_CreateContext = X11_GL_CreateContext;
_this->GL_MakeCurrent = X11_GL_MakeCurrent;
_this->GL_SetSwapInterval = X11_GL_SetSwapInterval;
_this->GL_GetSwapInterval = X11_GL_GetSwapInterval;
_this->GL_SwapWindow = X11_GL_SwapWindow;
_this->GL_DestroyContext = X11_GL_DestroyContext;
return X11_GL_LoadLibrary(_this, path);
#else
return SDL_SetError("SDL not configured with OpenGL/GLX support");
#endif
}
return SDL_EGL_LoadLibrary(_this, path, (NativeDisplayType)data->display, _this->gl_config.egl_platform);
}
XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent)
{
XVisualInfo *egl_visualinfo = NULL;
EGLint visual_id = 0;
XVisualInfo vi_in;
int out_count = 0;
if (!_this->egl_data) {
// The EGL library wasn't loaded, SDL_GetError() should have info
return NULL;
}
if (_this->egl_data->eglGetConfigAttrib(_this->egl_data->egl_display,
_this->egl_data->egl_config,
EGL_NATIVE_VISUAL_ID,
&visual_id) == EGL_FALSE) {
visual_id = 0;
}
if (visual_id != 0) {
vi_in.screen = screen;
vi_in.visualid = visual_id;
egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
if (transparent && egl_visualinfo) {
Uint32 format = X11_GetPixelFormatFromVisualInfo(display, egl_visualinfo);
if (!SDL_ISPIXELFORMAT_ALPHA(format)) {
// not transparent!
X11_XFree(egl_visualinfo);
egl_visualinfo = NULL;
}
}
}
if(!egl_visualinfo) {
// Use the default visual when all else fails
vi_in.screen = screen;
egl_visualinfo = X11_XGetVisualInfo(display,
VisualScreenMask,
&vi_in, &out_count);
// Return the first transparent Visual
if (transparent) {
int i;
for (i = 0; i < out_count; i++) {
XVisualInfo *v = &egl_visualinfo[i];
Uint32 format = X11_GetPixelFormatFromVisualInfo(display, v);
if (SDL_ISPIXELFORMAT_ALPHA(format)) { // found!
// re-request it to have a copy that can be X11_XFree'ed later
vi_in.screen = screen;
vi_in.visualid = v->visualid;
X11_XFree(egl_visualinfo);
egl_visualinfo = X11_XGetVisualInfo(display, VisualScreenMask | VisualIDMask, &vi_in, &out_count);
return egl_visualinfo;
}
}
}
}
return egl_visualinfo;
}
SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_GLContext context;
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
X11_XSync(display, False);
context = SDL_EGL_CreateContext(_this, data->egl_surface);
X11_XSync(display, False);
return context;
}
SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *data = window->internal;
return data->egl_surface;
}
bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
{
const bool ret = SDL_EGL_SwapBuffers(_this, window->internal->egl_surface); \
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_HandlePresent(window);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
return ret;
}
SDL_EGL_MakeCurrent_impl(X11)
#endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_OPENGL_EGL

View file

@ -0,0 +1,55 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11opengles_h_
#define SDL_x11opengles_h_
#ifdef SDL_VIDEO_OPENGL_EGL
#include "../SDL_sysvideo.h"
#include "../SDL_egl_c.h"
typedef struct SDL_PrivateGLESData
{
// 1401 If the struct-declaration-list contains no named members, the behavior is undefined.
// warning: empty struct has size 0 in C, size 1 in C++ [-Wc++-compat]
int dummy;
} SDL_PrivateGLESData;
// OpenGLES functions
#define X11_GLES_GetAttribute SDL_EGL_GetAttribute
#define X11_GLES_GetProcAddress SDL_EGL_GetProcAddressInternal
#define X11_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
#define X11_GLES_SetSwapInterval SDL_EGL_SetSwapInterval
#define X11_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
#define X11_GLES_DestroyContext SDL_EGL_DestroyContext
extern bool X11_GLES_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern XVisualInfo *X11_GLES_GetVisual(SDL_VideoDevice *_this, Display *display, int screen, bool transparent);
extern SDL_GLContext X11_GLES_CreateContext(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_GLES_MakeCurrent(SDL_VideoDevice *_this, SDL_Window *window, SDL_GLContext context);
extern SDL_EGLSurface X11_GLES_GetEGLSurface(SDL_VideoDevice *_this, SDL_Window *window);
#endif // SDL_VIDEO_OPENGL_EGL
#endif // SDL_x11opengles_h_

View file

@ -0,0 +1,437 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#include "../../events/SDL_pen_c.h"
#include "../SDL_sysvideo.h"
#include "SDL_x11pen.h"
#include "SDL_x11video.h"
#include "SDL_x11xinput2.h"
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
// Does this device have a valuator for pressure sensitivity?
static bool X11_XInput2DeviceIsPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
{
const SDL_VideoData *data = _this->internal;
for (int i = 0; i < dev->num_classes; i++) {
const XIAnyClassInfo *classinfo = dev->classes[i];
if (classinfo->type == XIValuatorClass) {
const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
if (val_classinfo->label == data->atoms.pen_atom_abs_pressure) {
return true;
}
}
}
return false;
}
// Heuristically determines if device is an eraser
static bool X11_XInput2PenIsEraser(SDL_VideoDevice *_this, int deviceid, char *devicename)
{
#define PEN_ERASER_NAME_TAG "eraser" // String constant to identify erasers
SDL_VideoData *data = _this->internal;
if (data->atoms.pen_atom_wacom_tool_type != None) {
Atom type_return;
int format_return;
unsigned long num_items_return;
unsigned long bytes_after_return;
unsigned char *tooltype_name_info = NULL;
// Try Wacom-specific method
if (Success == X11_XIGetProperty(data->display, deviceid,
data->atoms.pen_atom_wacom_tool_type,
0, 32, False,
AnyPropertyType, &type_return, &format_return,
&num_items_return, &bytes_after_return,
&tooltype_name_info) &&
tooltype_name_info != NULL && num_items_return > 0) {
bool result = false;
char *tooltype_name = NULL;
if (type_return == XA_ATOM) {
// Atom instead of string? Un-intern
Atom atom = *((Atom *)tooltype_name_info);
if (atom != None) {
tooltype_name = X11_XGetAtomName(data->display, atom);
}
} else if (type_return == XA_STRING && format_return == 8) {
tooltype_name = (char *)tooltype_name_info;
}
if (tooltype_name) {
if (SDL_strcasecmp(tooltype_name, PEN_ERASER_NAME_TAG) == 0) {
result = true;
}
if (tooltype_name != (char *)tooltype_name_info) {
X11_XFree(tooltype_name_info);
}
X11_XFree(tooltype_name);
return result;
}
}
}
// Non-Wacom device?
/* We assume that a device is an eraser if its name contains the string "eraser".
* Unfortunately there doesn't seem to be a clean way to distinguish these cases (as of 2022-03). */
return (SDL_strcasestr(devicename, PEN_ERASER_NAME_TAG)) ? true : false;
}
// Read out an integer property and store into a preallocated Sint32 array, extending 8 and 16 bit values suitably.
// Returns number of Sint32s written (<= max_words), or 0 on error.
static size_t X11_XInput2PenGetIntProperty(SDL_VideoDevice *_this, int deviceid, Atom property, Sint32 *dest, size_t max_words)
{
const SDL_VideoData *data = _this->internal;
Atom type_return;
int format_return;
unsigned long num_items_return;
unsigned long bytes_after_return;
unsigned char *output;
if (property == None) {
return 0;
}
if (Success != X11_XIGetProperty(data->display, deviceid,
property,
0, max_words, False,
XA_INTEGER, &type_return, &format_return,
&num_items_return, &bytes_after_return,
&output) ||
num_items_return == 0 || output == NULL) {
return 0;
}
if (type_return == XA_INTEGER) {
int k;
const int to_copy = SDL_min(max_words, num_items_return);
if (format_return == 8) {
Sint8 *numdata = (Sint8 *)output;
for (k = 0; k < to_copy; ++k) {
dest[k] = numdata[k];
}
} else if (format_return == 16) {
Sint16 *numdata = (Sint16 *)output;
for (k = 0; k < to_copy; ++k) {
dest[k] = numdata[k];
}
} else {
SDL_memcpy(dest, output, sizeof(Sint32) * to_copy);
}
X11_XFree(output);
return to_copy;
}
return 0; // type mismatch
}
// Identify Wacom devices (if true is returned) and extract their device type and serial IDs
static bool X11_XInput2PenWacomDeviceID(SDL_VideoDevice *_this, int deviceid, Uint32 *wacom_devicetype_id, Uint32 *wacom_serial)
{
SDL_VideoData *data = _this->internal;
Sint32 serial_id_buf[3];
int result;
if ((result = X11_XInput2PenGetIntProperty(_this, deviceid, data->atoms.pen_atom_wacom_serial_ids, serial_id_buf, 3)) == 3) {
*wacom_devicetype_id = serial_id_buf[2];
*wacom_serial = serial_id_buf[1];
return true;
}
*wacom_devicetype_id = *wacom_serial = 0;
return false;
}
typedef struct FindPenByDeviceIDData
{
int x11_deviceid;
void *handle;
} FindPenByDeviceIDData;
static bool FindPenByDeviceID(void *handle, void *userdata)
{
const X11_PenHandle *x11_handle = (const X11_PenHandle *) handle;
FindPenByDeviceIDData *data = (FindPenByDeviceIDData *) userdata;
if (x11_handle->x11_deviceid != data->x11_deviceid) {
return false;
}
data->handle = handle;
return true;
}
X11_PenHandle *X11_FindPenByDeviceID(int deviceid)
{
FindPenByDeviceIDData data;
data.x11_deviceid = deviceid;
data.handle = NULL;
SDL_FindPenByCallback(FindPenByDeviceID, &data);
return (X11_PenHandle *) data.handle;
}
static X11_PenHandle *X11_MaybeAddPen(SDL_VideoDevice *_this, const XIDeviceInfo *dev)
{
SDL_VideoData *data = _this->internal;
SDL_PenCapabilityFlags capabilities = 0;
X11_PenHandle *handle = NULL;
if ((dev->use != XISlavePointer && (dev->use != XIFloatingSlave)) || dev->enabled == 0 || !X11_XInput2DeviceIsPen(_this, dev)) {
return NULL; // Only track physical devices that are enabled and look like pens
} else if ((handle = X11_FindPenByDeviceID(dev->deviceid)) != 0) {
return handle; // already have this pen, skip it.
} else if ((handle = SDL_calloc(1, sizeof (*handle))) == NULL) {
return NULL; // oh well.
}
for (int i = 0; i < SDL_arraysize(handle->valuator_for_axis); i++) {
handle->valuator_for_axis[i] = SDL_X11_PEN_AXIS_VALUATOR_MISSING; // until proven otherwise
}
int total_buttons = 0;
for (int i = 0; i < dev->num_classes; i++) {
const XIAnyClassInfo *classinfo = dev->classes[i];
if (classinfo->type == XIButtonClass) {
const XIButtonClassInfo *button_classinfo = (const XIButtonClassInfo *)classinfo;
total_buttons += button_classinfo->num_buttons;
} else if (classinfo->type == XIValuatorClass) {
const XIValuatorClassInfo *val_classinfo = (const XIValuatorClassInfo *)classinfo;
const Sint8 valuator_nr = val_classinfo->number;
const Atom vname = val_classinfo->label;
const float min = (float)val_classinfo->min;
const float max = (float)val_classinfo->max;
bool use_this_axis = true;
SDL_PenAxis axis = SDL_PEN_AXIS_COUNT;
// afaict, SDL_PEN_AXIS_DISTANCE is never reported by XInput2 (Wayland can offer it, though)
if (vname == data->atoms.pen_atom_abs_pressure) {
axis = SDL_PEN_AXIS_PRESSURE;
} else if (vname == data->atoms.pen_atom_abs_tilt_x) {
axis = SDL_PEN_AXIS_XTILT;
} else if (vname == data->atoms.pen_atom_abs_tilt_y) {
axis = SDL_PEN_AXIS_YTILT;
} else {
use_this_axis = false;
}
// !!! 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.
if (use_this_axis) {
capabilities |= SDL_GetPenCapabilityFromAxis(axis);
handle->valuator_for_axis[axis] = valuator_nr;
handle->axis_min[axis] = min;
handle->axis_max[axis] = max;
}
}
}
// We have a pen if and only if the device measures pressure.
// We checked this in X11_XInput2DeviceIsPen, so just assert it here.
SDL_assert(capabilities & SDL_PEN_CAPABILITY_PRESSURE);
const bool is_eraser = X11_XInput2PenIsEraser(_this, dev->deviceid, dev->name);
Uint32 wacom_devicetype_id = 0;
Uint32 wacom_serial = 0;
X11_XInput2PenWacomDeviceID(_this, dev->deviceid, &wacom_devicetype_id, &wacom_serial);
SDL_PenInfo peninfo;
SDL_zero(peninfo);
peninfo.capabilities = capabilities;
peninfo.max_tilt = -1;
peninfo.wacom_id = wacom_devicetype_id;
peninfo.num_buttons = total_buttons;
peninfo.subtype = is_eraser ? SDL_PEN_TYPE_ERASER : SDL_PEN_TYPE_PEN;
if (is_eraser) {
peninfo.capabilities |= SDL_PEN_CAPABILITY_ERASER;
}
handle->is_eraser = is_eraser;
handle->x11_deviceid = dev->deviceid;
handle->pen = SDL_AddPenDevice(0, dev->name, &peninfo, handle);
if (!handle->pen) {
SDL_free(handle);
return NULL;
}
return handle;
}
X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid)
{
SDL_VideoData *data = _this->internal;
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, deviceid, &num_device_info);
if (device_info) {
SDL_assert(num_device_info == 1);
X11_PenHandle *handle = X11_MaybeAddPen(_this, device_info);
X11_XIFreeDeviceInfo(device_info);
return handle;
}
return NULL;
}
void X11_RemovePenByDeviceID(int deviceid)
{
X11_PenHandle *handle = X11_FindPenByDeviceID(deviceid);
if (handle) {
SDL_RemovePenDevice(0, handle->pen);
SDL_free(handle);
}
}
void X11_InitPen(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
#define LOOKUP_PEN_ATOM(X) X11_XInternAtom(data->display, X, False)
data->atoms.pen_atom_device_product_id = LOOKUP_PEN_ATOM("Device Product ID");
data->atoms.pen_atom_wacom_serial_ids = LOOKUP_PEN_ATOM("Wacom Serial IDs");
data->atoms.pen_atom_wacom_tool_type = LOOKUP_PEN_ATOM("Wacom Tool Type");
data->atoms.pen_atom_abs_pressure = LOOKUP_PEN_ATOM("Abs Pressure");
data->atoms.pen_atom_abs_tilt_x = LOOKUP_PEN_ATOM("Abs Tilt X");
data->atoms.pen_atom_abs_tilt_y = LOOKUP_PEN_ATOM("Abs Tilt Y");
#undef LOOKUP_PEN_ATOM
// Do an initial check on devices. After this, we'll add/remove individual pens when XI_HierarchyChanged events alert us.
int num_device_info = 0;
XIDeviceInfo *device_info = X11_XIQueryDevice(data->display, XIAllDevices, &num_device_info);
if (device_info) {
for (int i = 0; i < num_device_info; i++) {
X11_MaybeAddPen(_this, &device_info[i]);
}
X11_XIFreeDeviceInfo(device_info);
}
}
static void X11_FreePenHandle(SDL_PenID instance_id, void *handle, void *userdata)
{
SDL_free(handle);
}
void X11_QuitPen(SDL_VideoDevice *_this)
{
SDL_RemoveAllPenDevices(X11_FreePenHandle, NULL);
}
static void X11_XInput2NormalizePenAxes(const X11_PenHandle *pen, float *coords)
{
// Normalise axes
for (int axis = 0; axis < SDL_PEN_AXIS_COUNT; ++axis) {
const int valuator = pen->valuator_for_axis[axis];
if (valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
continue;
}
float value = coords[axis];
const float min = pen->axis_min[axis];
const float max = pen->axis_max[axis];
if (axis == SDL_PEN_AXIS_SLIDER) {
value += pen->slider_bias;
}
// min ... 0 ... max
if (min < 0.0) {
// Normalise so that 0 remains 0.0
if (value < 0) {
value = value / (-min);
} else {
if (max == 0.0f) {
value = 0.0f;
} else {
value = value / max;
}
}
} else {
// 0 ... min ... max
// including 0.0 = min
if (max == 0.0f) {
value = 0.0f;
} else {
value = (value - min) / max;
}
}
switch (axis) {
case SDL_PEN_AXIS_XTILT:
case SDL_PEN_AXIS_YTILT:
//if (peninfo->info.max_tilt > 0.0f) {
// value *= peninfo->info.max_tilt; // normalize to physical max
//}
break;
case SDL_PEN_AXIS_ROTATION:
// normalised to -1..1, so let's convert to degrees
value *= 180.0f;
value += pen->rotation_bias;
// handle simple over/underflow
if (value >= 180.0f) {
value -= 360.0f;
} else if (value < -180.0f) {
value += 360.0f;
}
break;
default:
break;
}
coords[axis] = value;
}
}
void X11_PenAxesFromValuators(const X11_PenHandle *pen,
const double *input_values, const unsigned char *mask, int mask_len,
float axis_values[SDL_PEN_AXIS_COUNT])
{
for (int i = 0; i < SDL_PEN_AXIS_COUNT; i++) {
const int valuator = pen->valuator_for_axis[i];
if ((valuator == SDL_X11_PEN_AXIS_VALUATOR_MISSING) || (valuator >= mask_len * 8) || !(XIMaskIsSet(mask, valuator))) {
axis_values[i] = 0.0f;
} else {
axis_values[i] = (float)input_values[valuator];
}
}
X11_XInput2NormalizePenAxes(pen, axis_values);
}
#else
void X11_InitPen(SDL_VideoDevice *_this)
{
(void) _this;
}
void X11_QuitPen(SDL_VideoDevice *_this)
{
(void) _this;
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2

View file

@ -0,0 +1,72 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#ifndef SDL_x11pen_h_
#define SDL_x11pen_h_
// Pressure-sensitive pen support for X11.
#include "SDL_x11video.h"
#include "../../events/SDL_pen_c.h"
// Prep pen support (never fails; pens simply won't be added if there's a problem).
extern void X11_InitPen(SDL_VideoDevice *_this);
// Clean up pen support.
extern void X11_QuitPen(SDL_VideoDevice *_this);
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
// Forward definition for SDL_x11video.h
struct SDL_VideoData;
#define SDL_X11_PEN_AXIS_VALUATOR_MISSING -1
typedef struct X11_PenHandle
{
SDL_PenID pen;
bool is_eraser;
int x11_deviceid;
int valuator_for_axis[SDL_PEN_AXIS_COUNT];
float slider_bias; // shift value to add to PEN_AXIS_SLIDER (before normalisation)
float rotation_bias; // rotation to add to PEN_AXIS_ROTATION (after normalisation)
float axis_min[SDL_PEN_AXIS_COUNT];
float axis_max[SDL_PEN_AXIS_COUNT];
} X11_PenHandle;
// Converts XINPUT2 valuators into pen axis information, including normalisation.
extern void X11_PenAxesFromValuators(const X11_PenHandle *pen,
const double *input_values, const unsigned char *mask, int mask_len,
float axis_values[SDL_PEN_AXIS_COUNT]);
// Add a pen (if this function's further checks validate it).
extern X11_PenHandle *X11_MaybeAddPenByDeviceID(SDL_VideoDevice *_this, int deviceid);
// Remove a pen. It's okay if deviceid is bogus or not a pen, we'll check it.
extern void X11_RemovePenByDeviceID(int deviceid);
// Map X11 device ID to pen ID.
extern X11_PenHandle *X11_FindPenByDeviceID(int deviceid);
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
#endif // SDL_x11pen_h_

View file

@ -0,0 +1,129 @@
/*
Simple DirectMedia Layer
Copyright 2024 Igalia S.L.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_X11)
#include "SDL_x11video.h"
#include "SDL_x11settings.h"
#define SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR "Gdk/WindowScalingFactor"
#define SDL_XSETTINGS_XFT_DPI "Xft/DPI"
static void X11_XsettingsNotify(const char *name, XSettingsAction action, XSettingsSetting *setting, void *data)
{
SDL_VideoDevice *_this = data;
float scale_factor = 1.0;
int i;
if (SDL_strcmp(name, SDL_XSETTINGS_GDK_WINDOW_SCALING_FACTOR) != 0 ||
SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) != 0) {
return;
}
if (setting->type != XSETTINGS_TYPE_INT) {
return;
}
switch (action) {
case XSETTINGS_ACTION_NEW:
SDL_FALLTHROUGH;
case XSETTINGS_ACTION_CHANGED:
scale_factor = setting->data.v_int;
if (SDL_strcmp(name, SDL_XSETTINGS_XFT_DPI) == 0) {
scale_factor = scale_factor / 1024.0f / 96.0f;
}
break;
case XSETTINGS_ACTION_DELETED:
scale_factor = 1.0;
break;
}
if (_this) {
for (i = 0; i < _this->num_displays; ++i) {
SDL_SetDisplayContentScale(_this->displays[i], scale_factor);
}
}
}
void X11_InitXsettings(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
xsettings_data->xsettings = xsettings_client_new(data->display,
DefaultScreen(data->display), X11_XsettingsNotify, NULL, _this);
}
void X11_QuitXsettings(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
if (xsettings_data->xsettings) {
xsettings_client_destroy(xsettings_data->xsettings);
xsettings_data->xsettings = NULL;
}
}
void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent)
{
SDL_VideoData *data = _this->internal;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
if (xsettings_data->xsettings) {
if (!xsettings_client_process_event(xsettings_data->xsettings, xevent)) {
xsettings_client_destroy(xsettings_data->xsettings);
xsettings_data->xsettings = NULL;
}
}
}
int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value) {
SDL_VideoData *data = _this->internal;
SDLX11_SettingsData *xsettings_data = &data->xsettings_data;
XSettingsSetting *setting = NULL;
int res = fallback_value;
if (xsettings_data->xsettings) {
if (xsettings_client_get_setting(xsettings_data->xsettings, key, &setting) != XSETTINGS_SUCCESS) {
goto no_key;
}
if (setting->type != XSETTINGS_TYPE_INT) {
goto no_key;
}
res = setting->data.v_int;
}
no_key:
if (setting) {
xsettings_setting_free(setting);
}
return res;
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright 2024 Igalia S.L.
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11settings_h_
#define SDL_x11settings_h_
#include <X11/Xlib.h>
#include "xsettings-client.h"
typedef struct X11_SettingsData {
XSettingsClient *xsettings;
} SDLX11_SettingsData;
extern void X11_InitXsettings(SDL_VideoDevice *_this);
extern void X11_QuitXsettings(SDL_VideoDevice *_this);
extern void X11_HandleXsettings(SDL_VideoDevice *_this, const XEvent *xevent);
extern int X11_GetXsettingsIntKey(SDL_VideoDevice *_this, const char *key, int fallback_value);
#endif // SDL_x11settings_h_

View file

@ -0,0 +1,111 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "SDL_x11shape.h"
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
static Uint8 *GenerateShapeMask(SDL_Surface *shape)
{
int x, y;
const size_t ppb = 8;
const size_t bytes_per_scanline = (shape->w + (ppb - 1)) / ppb;
const Uint8 *a;
Uint8 *mask;
Uint8 *mask_scanline;
Uint8 mask_value;
mask = (Uint8 *)SDL_calloc(1, shape->h * bytes_per_scanline);
if (mask) {
for (y = 0; y < shape->h; y++) {
a = (const Uint8 *)shape->pixels + y * shape->pitch;
mask_scanline = mask + y * bytes_per_scanline;
for (x = 0; x < shape->w; x++) {
mask_value = (*a == SDL_ALPHA_TRANSPARENT) ? 0 : 1;
mask_scanline[x / ppb] |= mask_value << (x % ppb);
a += 4;
}
}
}
return mask;
}
#endif // SDL_VIDEO_DRIVER_X11_XSHAPE
bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape)
{
bool result = false;
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
SDL_WindowData *windowdata = window->internal;
// Generate a set of spans for the region
if (shape) {
SDL_Surface *stretched = NULL;
Uint8 *mask;
Pixmap pixmap;
if (shape->w != window->w || shape->h != window->h) {
stretched = SDL_CreateSurface(window->w, window->h, SDL_PIXELFORMAT_ARGB32);
if (!stretched) {
return false;
}
if (!SDL_StretchSurface(shape, NULL, stretched, NULL, SDL_SCALEMODE_LINEAR)) {
SDL_DestroySurface(stretched);
return false;
}
shape = stretched;
}
mask = GenerateShapeMask(shape);
if (mask) {
pixmap = X11_XCreateBitmapFromData(windowdata->videodata->display, windowdata->xwindow, (const char *)mask, shape->w, shape->h);
X11_XShapeCombineMask(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, pixmap, ShapeSet);
SDL_free(mask);
result = true;
}
if (stretched) {
SDL_DestroySurface(stretched);
}
} else {
Region region = X11_XCreateRegion();
XRectangle rect;
rect.x = 0;
rect.y = 0;
rect.width = window->w;
rect.height = window->h;
X11_XUnionRectWithRegion(&rect, region, region);
X11_XShapeCombineRegion(windowdata->videodata->display, windowdata->xwindow, ShapeInput, 0, 0, region, ShapeSet);
X11_XDestroyRegion(region);
result = true;
}
#endif // SDL_VIDEO_DRIVER_X11_XSHAPE
return result;
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,28 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11shape_h_
#define SDL_x11shape_h_
extern bool X11_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
#endif // SDL_x11shape_h_

View file

@ -0,0 +1,354 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/* *INDENT-OFF* */ // clang-format off
#ifndef SDL_X11_MODULE
#define SDL_X11_MODULE(modname)
#endif
#ifndef SDL_X11_SYM
#define SDL_X11_SYM(rc,fn,params,args,ret)
#endif
SDL_X11_MODULE(BASEXLIB)
SDL_X11_SYM(XSizeHints*,XAllocSizeHints,(void),(),return)
SDL_X11_SYM(XWMHints*,XAllocWMHints,(void),(),return)
SDL_X11_SYM(XClassHint*,XAllocClassHint,(void),(),return)
SDL_X11_SYM(int,XChangePointerControl,(Display* a,Bool b,Bool c,int d,int e,int f),(a,b,c,d,e,f),return)
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)
SDL_X11_SYM(Bool,XCheckIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return)
SDL_X11_SYM(int,XClearWindow,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(int,XCloseDisplay,(Display* a),(a),return)
SDL_X11_SYM(int,XConvertSelection,(Display* a,Atom b,Atom c,Atom d,Window e,Time f),(a,b,c,d,e,f),return)
SDL_X11_SYM(Pixmap,XCreateBitmapFromData,(Display *dpy,Drawable d,_Xconst char *data,unsigned int width,unsigned int height),(dpy,d,data,width,height),return)
SDL_X11_SYM(Colormap,XCreateColormap,(Display* a,Window b,Visual* c,int d),(a,b,c,d),return)
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)
SDL_X11_SYM(Cursor,XCreateFontCursor,(Display* a,unsigned int b),(a,b),return)
SDL_X11_SYM(XFontSet,XCreateFontSet,(Display* a, _Xconst char* b, char*** c, int* d, char** e),(a,b,c,d,e),return)
SDL_X11_SYM(GC,XCreateGC,(Display* a,Drawable b,unsigned long c,XGCValues* d),(a,b,c,d),return)
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)
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)
SDL_X11_SYM(int,XDefineCursor,(Display* a,Window b,Cursor c),(a,b,c),return)
SDL_X11_SYM(int,XDeleteProperty,(Display* a,Window b,Atom c),(a,b,c),return)
SDL_X11_SYM(int,XDestroyWindow,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(int,XDisplayKeycodes,(Display* a,int* b,int* c),(a,b,c),return)
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)
SDL_X11_SYM(char*,XDisplayName,(_Xconst char* a),(a),return)
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)
SDL_X11_SYM(int,XEventsQueued,(Display* a,int b),(a,b),return)
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)
SDL_X11_SYM(Bool,XFilterEvent,(XEvent *event,Window w),(event,w),return)
SDL_X11_SYM(int,XFlush,(Display* a),(a),return)
SDL_X11_SYM(int,XFree,(void*a),(a),return)
SDL_X11_SYM(int,XFreeCursor,(Display* a,Cursor b),(a,b),return)
SDL_X11_SYM(void,XFreeFontSet,(Display* a, XFontSet b),(a,b),)
SDL_X11_SYM(int,XFreeGC,(Display* a,GC b),(a,b),return)
SDL_X11_SYM(int,XFreeFont,(Display* a, XFontStruct* b),(a,b),return)
SDL_X11_SYM(int,XFreeModifiermap,(XModifierKeymap* a),(a),return)
SDL_X11_SYM(int,XFreePixmap,(Display* a,Pixmap b),(a,b),return)
SDL_X11_SYM(void,XFreeStringList,(char** a),(a),)
SDL_X11_SYM(char*,XGetAtomName,(Display *a,Atom b),(a,b),return)
SDL_X11_SYM(int,XGetInputFocus,(Display *a,Window *b,int *c),(a,b,c),return)
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)
SDL_X11_SYM(XModifierKeymap*,XGetModifierMapping,(Display* a),(a),return)
SDL_X11_SYM(int,XGetPointerControl,(Display* a,int* b,int* c,int* d),(a,b,c,d),return)
SDL_X11_SYM(Window,XGetSelectionOwner,(Display* a,Atom b),(a,b),return)
SDL_X11_SYM(XVisualInfo*,XGetVisualInfo,(Display* a,long b,XVisualInfo* c,int* d),(a,b,c,d),return)
SDL_X11_SYM(Status,XGetWindowAttributes,(Display* a,Window b,XWindowAttributes* c),(a,b,c),return)
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)
SDL_X11_SYM(XWMHints*,XGetWMHints,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(Status,XGetWMNormalHints,(Display *a,Window b, XSizeHints *c, long *d),(a,b,c,d),return)
SDL_X11_SYM(int,XIfEvent,(Display* a,XEvent *b,Bool (*c)(Display*,XEvent*,XPointer),XPointer d),(a,b,c,d),return)
SDL_X11_SYM(int,XGrabKeyboard,(Display* a,Window b,Bool c,int d,int e,Time f),(a,b,c,d,e,f),return)
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)
SDL_X11_SYM(int,XGrabServer,(Display* a),(a),return)
SDL_X11_SYM(Status,XIconifyWindow,(Display* a,Window b,int c),(a,b,c),return)
SDL_X11_SYM(KeyCode,XKeysymToKeycode,(Display* a,KeySym b),(a,b),return)
SDL_X11_SYM(char*,XKeysymToString,(KeySym a),(a),return)
SDL_X11_SYM(int,XInstallColormap,(Display* a,Colormap b),(a,b),return)
SDL_X11_SYM(Atom,XInternAtom,(Display* a,_Xconst char* b,Bool c),(a,b,c),return)
SDL_X11_SYM(XPixmapFormatValues*,XListPixmapFormats,(Display* a,int* b),(a,b),return)
SDL_X11_SYM(XFontStruct*,XLoadQueryFont,(Display* a,_Xconst char* b),(a,b),return)
SDL_X11_SYM(KeySym,XLookupKeysym,(XKeyEvent* a,int b),(a,b),return)
SDL_X11_SYM(int,XLookupString,(XKeyEvent* a,char* b,int c,KeySym* d,XComposeStatus* e),(a,b,c,d,e),return)
SDL_X11_SYM(int,XMapRaised,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(Status,XMatchVisualInfo,(Display* a,int b,int c,int d,XVisualInfo* e),(a,b,c,d,e),return)
SDL_X11_SYM(int,XMissingExtension,(Display* a,_Xconst char* b),(a,b),return)
SDL_X11_SYM(int,XMoveWindow,(Display* a,Window b,int c,int d),(a,b,c,d),return)
SDL_X11_SYM(Display*,XOpenDisplay,(_Xconst char* a),(a),return)
SDL_X11_SYM(Status,XInitThreads,(void),(),return)
SDL_X11_SYM(int,XPeekEvent,(Display* a,XEvent* b),(a,b),return)
SDL_X11_SYM(int,XPending,(Display* a),(a),return)
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)
SDL_X11_SYM(int,XQueryKeymap,(Display* a,char b[32]),(a,b),return)
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)
SDL_X11_SYM(int,XRaiseWindow,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(int,XReparentWindow,(Display* a,Window b,Window c,int d,int e),(a,b,c,d,e),return)
SDL_X11_SYM(int,XResetScreenSaver,(Display* a),(a),return)
SDL_X11_SYM(int,XResizeWindow,(Display* a,Window b,unsigned int c,unsigned int d),(a,b,c,d),return)
SDL_X11_SYM(int,XScreenNumberOfScreen,(Screen* a),(a),return)
SDL_X11_SYM(int,XSelectInput,(Display* a,Window b,long c),(a,b,c),return)
SDL_X11_SYM(Status,XSendEvent,(Display* a,Window b,Bool c,long d,XEvent* e),(a,b,c,d,e),return)
SDL_X11_SYM(XErrorHandler,XSetErrorHandler,(XErrorHandler a),(a),return)
SDL_X11_SYM(int,XSetForeground,(Display* a,GC b,unsigned long c),(a,b,c),return)
SDL_X11_SYM(XIOErrorHandler,XSetIOErrorHandler,(XIOErrorHandler a),(a),return)
SDL_X11_SYM(int,XSetInputFocus,(Display *a,Window b,int c,Time d),(a,b,c,d),return)
SDL_X11_SYM(int,XSetSelectionOwner,(Display* a,Atom b,Window c,Time d),(a,b,c,d),return)
SDL_X11_SYM(int,XSetTransientForHint,(Display* a,Window b,Window c),(a,b,c),return)
SDL_X11_SYM(void,XSetTextProperty,(Display* a,Window b,XTextProperty* c,Atom d),(a,b,c,d),)
SDL_X11_SYM(int,XSetWindowBackground,(Display* a,Window b,unsigned long c),(a,b,c),return)
SDL_X11_SYM(void,XSetWMHints,(Display* a,Window b,XWMHints* c),(a,b,c),)
SDL_X11_SYM(void,XSetWMNormalHints,(Display* a,Window b,XSizeHints* c),(a,b,c),)
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),)
SDL_X11_SYM(Status,XSetWMProtocols,(Display* a,Window b,Atom* c,int d),(a,b,c,d),return)
SDL_X11_SYM(int,XStoreColors,(Display* a,Colormap b,XColor* c,int d),(a,b,c,d),return)
SDL_X11_SYM(int,XStoreName,(Display* a,Window b,_Xconst char* c),(a,b,c),return)
SDL_X11_SYM(Status,XStringListToTextProperty,(char** a,int b,XTextProperty* c),(a,b,c),return)
SDL_X11_SYM(int,XSync,(Display* a,Bool b),(a,b),return)
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)
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)
SDL_X11_SYM(int,XUndefineCursor,(Display* a,Window b),(a,b),return)
SDL_X11_SYM(int,XUngrabKeyboard,(Display* a,Time b),(a,b),return)
SDL_X11_SYM(int,XUngrabPointer,(Display* a,Time b),(a,b),return)
SDL_X11_SYM(int,XUngrabServer,(Display* a),(a),return)
SDL_X11_SYM(int,XUninstallColormap,(Display* a,Colormap b),(a,b),return)
SDL_X11_SYM(int,XUnloadFont,(Display* a,Font b),(a,b),return)
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)
SDL_X11_SYM(int,XWindowEvent,(Display* a,Window b,long c,XEvent* d),(a,b,c,d),return)
SDL_X11_SYM(Status,XWithdrawWindow,(Display* a,Window b,int c),(a,b,c),return)
SDL_X11_SYM(VisualID,XVisualIDFromVisual,(Visual* a),(a),return)
SDL_X11_SYM(char*,XGetDefault,(Display* a,_Xconst char* b, _Xconst char* c),(a,b,c),return)
SDL_X11_SYM(Bool,XQueryExtension,(Display* a,_Xconst char* b,int* c,int* d,int* e),(a,b,c,d,e),return)
SDL_X11_SYM(char *,XDisplayString,(Display* a),(a),return)
SDL_X11_SYM(int,XGetErrorText,(Display* a,int b,char* c,int d),(a,b,c,d),return)
SDL_X11_SYM(void,_XEatData,(Display* a,unsigned long b),(a,b),)
SDL_X11_SYM(void,_XFlush,(Display* a),(a),)
SDL_X11_SYM(void,_XFlushGCCache,(Display* a,GC b),(a,b),)
SDL_X11_SYM(int,_XRead,(Display* a,char* b,long c),(a,b,c),return)
SDL_X11_SYM(void,_XReadPad,(Display* a,char* b,long c),(a,b,c),)
SDL_X11_SYM(void,_XSend,(Display* a,_Xconst char* b,long c),(a,b,c),)
SDL_X11_SYM(Status,_XReply,(Display* a,xReply* b,int c,Bool d),(a,b,c,d),return)
SDL_X11_SYM(unsigned long,_XSetLastRequestRead,(Display* a,xGenericReply* b),(a,b),return)
SDL_X11_SYM(SDL_X11_XSynchronizeRetType,XSynchronize,(Display* a,Bool b),(a,b),return)
SDL_X11_SYM(SDL_X11_XESetWireToEventRetType,XESetWireToEvent,(Display* a,int b,SDL_X11_XESetWireToEventRetType c),(a,b,c),return)
SDL_X11_SYM(SDL_X11_XESetEventToWireRetType,XESetEventToWire,(Display* a,int b,SDL_X11_XESetEventToWireRetType c),(a,b,c),return)
SDL_X11_SYM(void,XRefreshKeyboardMapping,(XMappingEvent *a),(a),)
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)
SDL_X11_SYM(Bool,XSupportsLocale,(void),(),return)
SDL_X11_SYM(Status,XmbTextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return)
SDL_X11_SYM(Region,XCreateRegion,(void),(),return)
SDL_X11_SYM(int,XUnionRectWithRegion,(XRectangle *a, Region b, Region c),(a,b,c), return)
SDL_X11_SYM(void,XDestroyRegion,(Region),(a),)
SDL_X11_SYM(void,XrmInitialize,(void),(),)
SDL_X11_SYM(char*,XResourceManagerString,(Display *display),(display),)
SDL_X11_SYM(XrmDatabase,XrmGetStringDatabase,(char *data),(data),)
SDL_X11_SYM(void,XrmDestroyDatabase,(XrmDatabase db),(db),)
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),)
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
SDL_X11_MODULE(XFIXES)
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)
SDL_X11_SYM(void, XFixesDestroyPointerBarrier, (Display* a, PointerBarrier b), (a,b),)
SDL_X11_SYM(int, XIBarrierReleasePointer,(Display* a, int b, PointerBarrier c, BarrierEventID d), (a,b,c,d), return) // this is actually Xinput2
SDL_X11_SYM(Status, XFixesQueryVersion,(Display* a, int* b, int* c), (a,b,c), return)
SDL_X11_SYM(Status, XFixesSelectSelectionInput, (Display* a, Window b, Atom c, unsigned long d), (a,b,c,d), return)
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
SDL_X11_MODULE(XSYNC)
SDL_X11_SYM(Status, XSyncQueryExtension, (Display* a, int* b, int* c), (a, b, c), return)
SDL_X11_SYM(Status, XSyncInitialize, (Display* a, int* b, int* c), (a, b, c), return)
SDL_X11_SYM(XSyncCounter, XSyncCreateCounter, (Display* a, XSyncValue b), (a, b), return)
SDL_X11_SYM(Status, XSyncDestroyCounter, (Display* a, XSyncCounter b), (a, b), return)
SDL_X11_SYM(Status, XSyncSetCounter, (Display* a, XSyncCounter b, XSyncValue c), (a, b, c), return)
#endif
#ifdef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return)
SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),)
#endif
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f),(a,b,c,d,e,f),return)
#if NeedWidePrototypes
SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return)
#else
SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return)
#endif
SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c),(a,b,c),return)
SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c),(a,b,c),return)
SDL_X11_SYM(XkbDescPtr,XkbGetMap,(Display* a,unsigned int b,unsigned int c),(a,b,c),return)
SDL_X11_SYM(void,XkbFreeClientMap,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
SDL_X11_SYM(void,XkbFreeKeyboard,(XkbDescPtr a,unsigned int b, Bool c),(a,b,c),)
SDL_X11_SYM(Bool,XkbSetDetectableAutoRepeat,(Display* a, Bool b, Bool* c),(a,b,c),return)
#endif
// XKeycodeToKeysym is a deprecated function
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#if NeedWidePrototypes
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,unsigned int b,int c),(a,b,c),return)
#else
SDL_X11_SYM(KeySym,XKeycodeToKeysym,(Display* a,KeyCode b,int c),(a,b,c),return)
#endif
#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
#pragma GCC diagnostic pop
#endif
#ifdef X_HAVE_UTF8_STRING
SDL_X11_MODULE(UTF8)
SDL_X11_SYM(int,Xutf8TextListToTextProperty,(Display* a,char** b,int c,XICCEncodingStyle d,XTextProperty* e),(a,b,c,d,e),return)
SDL_X11_SYM(int,Xutf8LookupString,(XIC a,XKeyPressedEvent* b,char* c,int d,KeySym* e,Status* f),(a,b,c,d,e,f),return)
// SDL_X11_SYM(XIC,XCreateIC,(XIM, ...),return) !!! ARGH!
SDL_X11_SYM(void,XDestroyIC,(XIC a),(a),)
/* SDL_X11_SYM(char*,XGetICValues,(XIC, ...),return) !!! ARGH! */
/* SDL_X11_SYM(char*,XSetICValues,(XIC, ...),return) !!! ARGH! */
/* SDL_X11_SYM(XVaNestedList,XVaCreateNestedList,(int, ...),return) !!! ARGH! */
SDL_X11_SYM(void,XSetICFocus,(XIC a),(a),)
SDL_X11_SYM(void,XUnsetICFocus,(XIC a),(a),)
SDL_X11_SYM(XIM,XOpenIM,(Display* a,struct _XrmHashBucketRec* b,char* c,char* d),(a,b,c,d),return)
SDL_X11_SYM(Status,XCloseIM,(XIM a),(a),return)
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),)
SDL_X11_SYM(int,Xutf8TextExtents,(XFontSet a, _Xconst char* b, int c, XRectangle* d, XRectangle* e),(a,b,c,d,e),return)
SDL_X11_SYM(char*,XSetLocaleModifiers,(const char *a),(a),return)
SDL_X11_SYM(char*,Xutf8ResetIC,(XIC a),(a),return)
#endif
#ifndef NO_SHARED_MEMORY
SDL_X11_MODULE(SHM)
SDL_X11_SYM(Status,XShmAttach,(Display* a,XShmSegmentInfo* b),(a,b),return)
SDL_X11_SYM(Status,XShmDetach,(Display* a,XShmSegmentInfo* b),(a,b),return)
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)
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)
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)
SDL_X11_SYM(Bool,XShmQueryExtension,(Display* a),(a),return)
#endif
/*
* Not required...these only exist in code in headers on some 64-bit platforms,
* and are removed via macros elsewhere, so it's safe for them to be missing.
*/
#ifdef LONG64
SDL_X11_MODULE(IO_32BIT)
SDL_X11_SYM(int,_XData32,(Display *dpy,register _Xconst long *data,unsigned len),(dpy,data,len),return)
SDL_X11_SYM(void,_XRead32,(Display *dpy,register long *data,long len),(dpy,data,len),)
#endif
/*
* These only show up on some variants of Unix.
*/
#ifdef SDL_PLATFORM_OSF
SDL_X11_MODULE(OSF_ENTRY_POINTS)
SDL_X11_SYM(void,_SmtBufferOverflow,(Display *dpy,register smtDisplayPtr p),(dpy,p),)
SDL_X11_SYM(void,_SmtIpError,(Display *dpy,register smtDisplayPtr p,int i),(dpy,p,i),)
SDL_X11_SYM(int,ipAllocateData,(ChannelPtr a,IPCard b,IPDataPtr * c),(a,b,c),return)
SDL_X11_SYM(int,ipUnallocateAndSendData,(ChannelPtr a,IPCard b),(a,b),return)
#endif
// XCursor support
#ifdef SDL_VIDEO_DRIVER_X11_XCURSOR
SDL_X11_MODULE(XCURSOR)
SDL_X11_SYM(XcursorImage*,XcursorImageCreate,(int a,int b),(a,b),return)
SDL_X11_SYM(void,XcursorImageDestroy,(XcursorImage *a),(a),)
SDL_X11_SYM(Cursor,XcursorImageLoadCursor,(Display *a,const XcursorImage *b),(a,b),return)
SDL_X11_SYM(Cursor,XcursorLibraryLoadCursor,(Display *a, const char *b),(a,b),return)
#endif
// Xdbe support
#ifdef SDL_VIDEO_DRIVER_X11_XDBE
SDL_X11_MODULE(XDBE)
SDL_X11_SYM(Status,XdbeQueryExtension,(Display *dpy,int *major_version_return,int *minor_version_return),(dpy,major_version_return,minor_version_return),return)
SDL_X11_SYM(XdbeBackBuffer,XdbeAllocateBackBufferName,(Display *dpy,Window window,XdbeSwapAction swap_action),(dpy,window,swap_action),return)
SDL_X11_SYM(Status,XdbeDeallocateBackBufferName,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return)
SDL_X11_SYM(Status,XdbeSwapBuffers,(Display *dpy,XdbeSwapInfo *swap_info,int num_windows),(dpy,swap_info,num_windows),return)
SDL_X11_SYM(Status,XdbeBeginIdiom,(Display *dpy),(dpy),return)
SDL_X11_SYM(Status,XdbeEndIdiom,(Display *dpy),(dpy),return)
SDL_X11_SYM(XdbeScreenVisualInfo*,XdbeGetVisualInfo,(Display *dpy,Drawable *screen_specifiers,int *num_screens),(dpy,screen_specifiers,num_screens),return)
SDL_X11_SYM(void,XdbeFreeVisualInfo,(XdbeScreenVisualInfo *visual_info),(visual_info),)
SDL_X11_SYM(XdbeBackBufferAttributes*,XdbeGetBackBufferAttributes,(Display *dpy,XdbeBackBuffer buffer),(dpy,buffer),return)
#endif
// XInput2 support for multiple mice, tablets, etc.
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_X11_MODULE(XINPUT2)
SDL_X11_SYM(XIDeviceInfo*,XIQueryDevice,(Display *a,int b,int *c),(a,b,c),return)
SDL_X11_SYM(void,XIFreeDeviceInfo,(XIDeviceInfo *a),(a),)
SDL_X11_SYM(int,XISelectEvents,(Display *a,Window b,XIEventMask *c,int d),(a,b,c,d),return)
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)
SDL_X11_SYM(int,XIUngrabTouchBegin, (Display *a,int b,Window c, int d,XIGrabModifiers *e),(a, b, c, d, e),return)
SDL_X11_SYM(Status,XIQueryVersion,(Display *a,int *b,int *c),(a,b,c),return)
SDL_X11_SYM(XIEventMask*,XIGetSelectedEvents,(Display *a,Window b,int *c),(a,b,c),return)
SDL_X11_SYM(Bool,XIGetClientPointer,(Display *a,Window b,int *c),(a,b,c),return)
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)
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)
#endif
// XRandR support
#ifdef SDL_VIDEO_DRIVER_X11_XRANDR
SDL_X11_MODULE(XRANDR)
SDL_X11_SYM(Status,XRRQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return)
SDL_X11_SYM(Bool,XRRQueryExtension,(Display *dpy,int *event_base_return,int *error_base_return),(dpy,event_base_return,error_base_return),return)
SDL_X11_SYM(XRRScreenConfiguration *,XRRGetScreenInfo,(Display *dpy,Drawable draw),(dpy,draw),return)
SDL_X11_SYM(SizeID,XRRConfigCurrentConfiguration,(XRRScreenConfiguration *config,Rotation *rotation),(config,rotation),return)
SDL_X11_SYM(short,XRRConfigCurrentRate,(XRRScreenConfiguration *config),(config),return)
SDL_X11_SYM(short *,XRRConfigRates,(XRRScreenConfiguration *config,int sizeID,int *nrates),(config,sizeID,nrates),return)
SDL_X11_SYM(XRRScreenSize *,XRRConfigSizes,(XRRScreenConfiguration *config,int *nsizes),(config,nsizes),return)
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)
SDL_X11_SYM(void,XRRFreeScreenConfigInfo,(XRRScreenConfiguration *config),(config),)
SDL_X11_SYM(void,XRRSetScreenSize,(Display *dpy, Window window,int width, int height,int mmWidth, int mmHeight),(dpy,window,width,height,mmWidth,mmHeight),)
SDL_X11_SYM(Status,XRRGetScreenSizeRange,(Display *dpy, Window window,int *minWidth, int *minHeight, int *maxWidth, int *maxHeight),(dpy,window,minWidth,minHeight,maxWidth,maxHeight),return)
SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResources,(Display *dpy, Window window),(dpy, window),return)
SDL_X11_SYM(XRRScreenResources *,XRRGetScreenResourcesCurrent,(Display *dpy, Window window),(dpy, window),return)
SDL_X11_SYM(void,XRRFreeScreenResources,(XRRScreenResources *resources),(resources),)
SDL_X11_SYM(XRROutputInfo *,XRRGetOutputInfo,(Display *dpy, XRRScreenResources *resources, RROutput output),(dpy,resources,output),return)
SDL_X11_SYM(void,XRRFreeOutputInfo,(XRROutputInfo *outputInfo),(outputInfo),)
SDL_X11_SYM(XRRCrtcInfo *,XRRGetCrtcInfo,(Display *dpy, XRRScreenResources *resources, RRCrtc crtc),(dpy,resources,crtc),return)
SDL_X11_SYM(void,XRRFreeCrtcInfo,(XRRCrtcInfo *crtcInfo),(crtcInfo),)
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)
SDL_X11_SYM(Atom*,XRRListOutputProperties,(Display *dpy, RROutput output, int *nprop),(dpy,output,nprop),return)
SDL_X11_SYM(XRRPropertyInfo*,XRRQueryOutputProperty,(Display *dpy,RROutput output, Atom property),(dpy,output,property),return)
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)
SDL_X11_SYM(RROutput,XRRGetOutputPrimary,(Display *dpy,Window window),(dpy,window),return)
SDL_X11_SYM(void,XRRSelectInput,(Display *dpy, Window window, int mask),(dpy,window,mask),)
SDL_X11_SYM(Status,XRRGetCrtcTransform,(Display *dpy,RRCrtc crtc,XRRCrtcTransformAttributes **attributes),(dpy,crtc,attributes),return)
#endif
// MIT-SCREEN-SAVER support
#ifdef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
SDL_X11_MODULE(XSS)
SDL_X11_SYM(Bool,XScreenSaverQueryExtension,(Display *dpy,int *event_base,int *error_base),(dpy,event_base,error_base),return)
SDL_X11_SYM(Status,XScreenSaverQueryVersion,(Display *dpy,int *major_versionp,int *minor_versionp),(dpy,major_versionp,minor_versionp),return)
SDL_X11_SYM(void,XScreenSaverSuspend,(Display *dpy,Bool suspend),(dpy,suspend),return)
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XSHAPE
SDL_X11_MODULE(XSHAPE)
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),)
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),)
#endif
#undef SDL_X11_MODULE
#undef SDL_X11_SYM
/* *INDENT-ON* */ // clang-format on

View file

@ -0,0 +1,46 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include "SDL_x11touch.h"
#include "SDL_x11xinput2.h"
#include "../../events/SDL_touch_c.h"
void X11_InitTouch(SDL_VideoDevice *_this)
{
X11_InitXinput2Multitouch(_this);
}
void X11_QuitTouch(SDL_VideoDevice *_this)
{
SDL_QuitTouch();
}
void X11_ResetTouch(SDL_VideoDevice *_this)
{
X11_QuitTouch(_this);
X11_InitTouch(_this);
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,30 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11touch_h_
#define SDL_x11touch_h_
extern void X11_InitTouch(SDL_VideoDevice *_this);
extern void X11_QuitTouch(SDL_VideoDevice *_this);
extern void X11_ResetTouch(SDL_VideoDevice *_this);
#endif // SDL_x11touch_h_

View file

@ -0,0 +1,505 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include <unistd.h> // For getpid() and readlink()
#include "../../core/linux/SDL_system_theme.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../SDL_pixels_c.h"
#include "../SDL_sysvideo.h"
#include "SDL_x11framebuffer.h"
#include "SDL_x11pen.h"
#include "SDL_x11touch.h"
#include "SDL_x11video.h"
#include "SDL_x11xfixes.h"
#include "SDL_x11xinput2.h"
#include "SDL_x11messagebox.h"
#include "SDL_x11shape.h"
#include "SDL_x11xsync.h"
#ifdef SDL_VIDEO_OPENGL_EGL
#include "SDL_x11opengles.h"
#endif
// Initialization/Query functions
static bool X11_VideoInit(SDL_VideoDevice *_this);
static void X11_VideoQuit(SDL_VideoDevice *_this);
// X11 driver bootstrap functions
static void X11_DeleteDevice(SDL_VideoDevice *device)
{
SDL_VideoData *data = device->internal;
if (device->vulkan_config.loader_handle) {
device->Vulkan_UnloadLibrary(device);
}
if (data->display) {
X11_XCloseDisplay(data->display);
}
if (data->request_display) {
X11_XCloseDisplay(data->request_display);
}
SDL_free(data->windowlist);
if (device->wakeup_lock) {
SDL_DestroyMutex(device->wakeup_lock);
}
SDL_free(device->internal);
SDL_free(device);
SDL_X11_UnloadSymbols();
}
static bool X11_IsXWayland(Display *d)
{
int opcode, event, error;
return X11_XQueryExtension(d, "XWAYLAND", &opcode, &event, &error) == True;
}
static bool X11_CheckCurrentDesktop(const char *name)
{
SDL_Environment *env = SDL_GetEnvironment();
const char *desktopVar = SDL_GetEnvironmentVariable(env, "DESKTOP_SESSION");
if (desktopVar && SDL_strcasecmp(desktopVar, name) == 0) {
return true;
}
desktopVar = SDL_GetEnvironmentVariable(env, "XDG_CURRENT_DESKTOP");
if (desktopVar && SDL_strcasestr(desktopVar, name)) {
return true;
}
return false;
}
static SDL_VideoDevice *X11_CreateDevice(void)
{
SDL_VideoDevice *device;
SDL_VideoData *data;
const char *display = NULL; // Use the DISPLAY environment variable
Display *x11_display = NULL;
if (!SDL_X11_LoadSymbols()) {
return NULL;
}
/* Need for threading gl calls. This is also required for the proprietary
nVidia driver to be threaded. */
X11_XInitThreads();
// Open the display first to be sure that X11 is available
x11_display = X11_XOpenDisplay(display);
if (!x11_display) {
SDL_X11_UnloadSymbols();
return NULL;
}
// Initialize all variables that we clean on shutdown
device = (SDL_VideoDevice *)SDL_calloc(1, sizeof(SDL_VideoDevice));
if (!device) {
return NULL;
}
data = (struct SDL_VideoData *)SDL_calloc(1, sizeof(SDL_VideoData));
if (!data) {
SDL_free(device);
return NULL;
}
device->internal = data;
data->global_mouse_changed = true;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
data->active_cursor_confined_window = NULL;
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
data->display = x11_display;
data->request_display = X11_XOpenDisplay(display);
if (!data->request_display) {
X11_XCloseDisplay(data->display);
SDL_free(device->internal);
SDL_free(device);
SDL_X11_UnloadSymbols();
return NULL;
}
device->wakeup_lock = SDL_CreateMutex();
#ifdef X11_DEBUG
X11_XSynchronize(data->display, True);
#endif
/* Steam Deck will have an on-screen keyboard, so check their environment
* variable so we can make use of SDL_StartTextInput.
*/
data->is_steam_deck = SDL_GetHintBoolean("SteamDeck", false);
// Set the function pointers
device->VideoInit = X11_VideoInit;
device->VideoQuit = X11_VideoQuit;
device->ResetTouch = X11_ResetTouch;
device->GetDisplayModes = X11_GetDisplayModes;
device->GetDisplayBounds = X11_GetDisplayBounds;
device->GetDisplayUsableBounds = X11_GetDisplayUsableBounds;
device->GetWindowICCProfile = X11_GetWindowICCProfile;
device->SetDisplayMode = X11_SetDisplayMode;
device->SuspendScreenSaver = X11_SuspendScreenSaver;
device->PumpEvents = X11_PumpEvents;
device->WaitEventTimeout = X11_WaitEventTimeout;
device->SendWakeupEvent = X11_SendWakeupEvent;
device->CreateSDLWindow = X11_CreateWindow;
device->SetWindowTitle = X11_SetWindowTitle;
device->SetWindowIcon = X11_SetWindowIcon;
device->SetWindowPosition = X11_SetWindowPosition;
device->SetWindowSize = X11_SetWindowSize;
device->SetWindowMinimumSize = X11_SetWindowMinimumSize;
device->SetWindowMaximumSize = X11_SetWindowMaximumSize;
device->SetWindowAspectRatio = X11_SetWindowAspectRatio;
device->GetWindowBordersSize = X11_GetWindowBordersSize;
device->SetWindowOpacity = X11_SetWindowOpacity;
device->SetWindowParent = X11_SetWindowParent;
device->SetWindowModal = X11_SetWindowModal;
device->ShowWindow = X11_ShowWindow;
device->HideWindow = X11_HideWindow;
device->RaiseWindow = X11_RaiseWindow;
device->MaximizeWindow = X11_MaximizeWindow;
device->MinimizeWindow = X11_MinimizeWindow;
device->RestoreWindow = X11_RestoreWindow;
device->SetWindowBordered = X11_SetWindowBordered;
device->SetWindowResizable = X11_SetWindowResizable;
device->SetWindowAlwaysOnTop = X11_SetWindowAlwaysOnTop;
device->SetWindowFullscreen = X11_SetWindowFullscreen;
device->SetWindowMouseGrab = X11_SetWindowMouseGrab;
device->SetWindowKeyboardGrab = X11_SetWindowKeyboardGrab;
device->DestroyWindow = X11_DestroyWindow;
device->CreateWindowFramebuffer = X11_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = X11_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = X11_DestroyWindowFramebuffer;
device->SetWindowHitTest = X11_SetWindowHitTest;
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
device->UpdateWindowShape = X11_UpdateWindowShape;
device->FlashWindow = X11_FlashWindow;
device->ShowWindowSystemMenu = X11_ShowWindowSystemMenu;
device->SetWindowFocusable = X11_SetWindowFocusable;
device->SyncWindow = X11_SyncWindow;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
device->SetWindowMouseRect = X11_SetWindowMouseRect;
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
#ifdef SDL_VIDEO_OPENGL_GLX
device->GL_LoadLibrary = X11_GL_LoadLibrary;
device->GL_GetProcAddress = X11_GL_GetProcAddress;
device->GL_UnloadLibrary = X11_GL_UnloadLibrary;
device->GL_CreateContext = X11_GL_CreateContext;
device->GL_MakeCurrent = X11_GL_MakeCurrent;
device->GL_SetSwapInterval = X11_GL_SetSwapInterval;
device->GL_GetSwapInterval = X11_GL_GetSwapInterval;
device->GL_SwapWindow = X11_GL_SwapWindow;
device->GL_DestroyContext = X11_GL_DestroyContext;
device->GL_GetEGLSurface = NULL;
#endif
#ifdef SDL_VIDEO_OPENGL_EGL
#ifdef SDL_VIDEO_OPENGL_GLX
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_FORCE_EGL, false)) {
#endif
device->GL_LoadLibrary = X11_GLES_LoadLibrary;
device->GL_GetProcAddress = X11_GLES_GetProcAddress;
device->GL_UnloadLibrary = X11_GLES_UnloadLibrary;
device->GL_CreateContext = X11_GLES_CreateContext;
device->GL_MakeCurrent = X11_GLES_MakeCurrent;
device->GL_SetSwapInterval = X11_GLES_SetSwapInterval;
device->GL_GetSwapInterval = X11_GLES_GetSwapInterval;
device->GL_SwapWindow = X11_GLES_SwapWindow;
device->GL_DestroyContext = X11_GLES_DestroyContext;
device->GL_GetEGLSurface = X11_GLES_GetEGLSurface;
#ifdef SDL_VIDEO_OPENGL_GLX
}
#endif
#endif
device->GetTextMimeTypes = X11_GetTextMimeTypes;
device->SetClipboardData = X11_SetClipboardData;
device->GetClipboardData = X11_GetClipboardData;
device->HasClipboardData = X11_HasClipboardData;
device->SetPrimarySelectionText = X11_SetPrimarySelectionText;
device->GetPrimarySelectionText = X11_GetPrimarySelectionText;
device->HasPrimarySelectionText = X11_HasPrimarySelectionText;
device->StartTextInput = X11_StartTextInput;
device->StopTextInput = X11_StopTextInput;
device->UpdateTextInputArea = X11_UpdateTextInputArea;
device->HasScreenKeyboardSupport = X11_HasScreenKeyboardSupport;
device->ShowScreenKeyboard = X11_ShowScreenKeyboard;
device->HideScreenKeyboard = X11_HideScreenKeyboard;
device->IsScreenKeyboardShown = X11_IsScreenKeyboardShown;
device->free = X11_DeleteDevice;
#ifdef SDL_VIDEO_VULKAN
device->Vulkan_LoadLibrary = X11_Vulkan_LoadLibrary;
device->Vulkan_UnloadLibrary = X11_Vulkan_UnloadLibrary;
device->Vulkan_GetInstanceExtensions = X11_Vulkan_GetInstanceExtensions;
device->Vulkan_CreateSurface = X11_Vulkan_CreateSurface;
device->Vulkan_DestroySurface = X11_Vulkan_DestroySurface;
device->Vulkan_GetPresentationSupport = X11_Vulkan_GetPresentationSupport;
#endif
#ifdef SDL_USE_LIBDBUS
if (SDL_SystemTheme_Init())
device->system_theme = SDL_SystemTheme_Get();
#endif
device->device_caps = VIDEO_DEVICE_CAPS_HAS_POPUP_WINDOW_SUPPORT;
/* Openbox doesn't send the new window dimensions when entering fullscreen, so the events must be synthesized.
* This is otherwise not wanted, as it can break fullscreen window positioning on multi-monitor configurations.
*/
if (!X11_CheckCurrentDesktop("openbox")) {
device->device_caps |= VIDEO_DEVICE_CAPS_SENDS_DISPLAY_CHANGES;
}
data->is_xwayland = X11_IsXWayland(x11_display);
if (data->is_xwayland) {
device->device_caps |= VIDEO_DEVICE_CAPS_MODE_SWITCHING_EMULATED |
VIDEO_DEVICE_CAPS_DISABLE_MOUSE_WARP_ON_FULLSCREEN_TRANSITIONS;
}
return device;
}
VideoBootStrap X11_bootstrap = {
"x11", "SDL X11 video driver",
X11_CreateDevice,
X11_ShowMessageBox,
false
};
static int (*handler)(Display *, XErrorEvent *) = NULL;
static int X11_CheckWindowManagerErrorHandler(Display *d, XErrorEvent *e)
{
if (e->error_code == BadWindow) {
return 0;
} else {
return handler(d, e);
}
}
static void X11_CheckWindowManager(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
Display *display = data->display;
Atom _NET_SUPPORTING_WM_CHECK;
int status, real_format;
Atom real_type;
unsigned long items_read = 0, items_left = 0;
unsigned char *propdata = NULL;
Window wm_window = 0;
#ifdef DEBUG_WINDOW_MANAGER
char *wm_name;
#endif
// Set up a handler to gracefully catch errors
X11_XSync(display, False);
handler = X11_XSetErrorHandler(X11_CheckWindowManagerErrorHandler);
_NET_SUPPORTING_WM_CHECK = X11_XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False);
status = X11_XGetWindowProperty(display, DefaultRootWindow(display), _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
if (status == Success) {
if (items_read) {
wm_window = ((Window *)propdata)[0];
}
if (propdata) {
X11_XFree(propdata);
propdata = NULL;
}
}
if (wm_window) {
status = X11_XGetWindowProperty(display, wm_window, _NET_SUPPORTING_WM_CHECK, 0L, 1L, False, XA_WINDOW, &real_type, &real_format, &items_read, &items_left, &propdata);
if (status != Success || !items_read || wm_window != ((Window *)propdata)[0]) {
wm_window = None;
}
if (status == Success && propdata) {
X11_XFree(propdata);
propdata = NULL;
}
}
// Reset the error handler, we're done checking
X11_XSync(display, False);
X11_XSetErrorHandler(handler);
if (!wm_window) {
#ifdef DEBUG_WINDOW_MANAGER
printf("Couldn't get _NET_SUPPORTING_WM_CHECK property\n");
#endif
return;
}
data->net_wm = true;
#ifdef DEBUG_WINDOW_MANAGER
wm_name = X11_GetWindowTitle(_this, wm_window);
printf("Window manager: %s\n", wm_name);
SDL_free(wm_name);
#endif
}
static bool X11_VideoInit(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
// Get the process PID to be associated to the window
data->pid = getpid();
// I have no idea how random this actually is, or has to be.
data->window_group = (XID)(((size_t)data->pid) ^ ((size_t)_this));
// Look up some useful Atoms
#define GET_ATOM(X) data->atoms.X = X11_XInternAtom(data->display, #X, False)
GET_ATOM(WM_PROTOCOLS);
GET_ATOM(WM_DELETE_WINDOW);
GET_ATOM(WM_TAKE_FOCUS);
GET_ATOM(WM_NAME);
GET_ATOM(WM_TRANSIENT_FOR);
GET_ATOM(_NET_WM_STATE);
GET_ATOM(_NET_WM_STATE_HIDDEN);
GET_ATOM(_NET_WM_STATE_FOCUSED);
GET_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
GET_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
GET_ATOM(_NET_WM_STATE_FULLSCREEN);
GET_ATOM(_NET_WM_STATE_ABOVE);
GET_ATOM(_NET_WM_STATE_SKIP_TASKBAR);
GET_ATOM(_NET_WM_STATE_SKIP_PAGER);
GET_ATOM(_NET_WM_MOVERESIZE);
GET_ATOM(_NET_WM_STATE_MODAL);
GET_ATOM(_NET_WM_ALLOWED_ACTIONS);
GET_ATOM(_NET_WM_ACTION_FULLSCREEN);
GET_ATOM(_NET_WM_NAME);
GET_ATOM(_NET_WM_ICON_NAME);
GET_ATOM(_NET_WM_ICON);
GET_ATOM(_NET_WM_PING);
GET_ATOM(_NET_WM_SYNC_REQUEST);
GET_ATOM(_NET_WM_SYNC_REQUEST_COUNTER);
GET_ATOM(_NET_WM_WINDOW_OPACITY);
GET_ATOM(_NET_WM_USER_TIME);
GET_ATOM(_NET_ACTIVE_WINDOW);
GET_ATOM(_NET_FRAME_EXTENTS);
GET_ATOM(_SDL_WAKEUP);
GET_ATOM(UTF8_STRING);
GET_ATOM(PRIMARY);
GET_ATOM(CLIPBOARD);
GET_ATOM(INCR);
GET_ATOM(SDL_SELECTION);
GET_ATOM(TARGETS);
GET_ATOM(SDL_FORMATS);
GET_ATOM(XdndAware);
GET_ATOM(XdndEnter);
GET_ATOM(XdndLeave);
GET_ATOM(XdndPosition);
GET_ATOM(XdndStatus);
GET_ATOM(XdndTypeList);
GET_ATOM(XdndActionCopy);
GET_ATOM(XdndDrop);
GET_ATOM(XdndFinished);
GET_ATOM(XdndSelection);
GET_ATOM(XKLAVIER_STATE);
// Detect the window manager
X11_CheckWindowManager(_this);
if (!X11_InitModes(_this)) {
return false;
}
if (!X11_InitXinput2(_this)) {
// Assume a mouse and keyboard are attached
SDL_AddKeyboard(SDL_DEFAULT_KEYBOARD_ID, NULL, false);
SDL_AddMouse(SDL_DEFAULT_MOUSE_ID, NULL, false);
}
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
X11_InitXfixes(_this);
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
X11_InitXsettings(_this);
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
X11_InitXsync(_this);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
#ifndef X_HAVE_UTF8_STRING
#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.
#endif
if (!X11_InitKeyboard(_this)) {
return false;
}
X11_InitMouse(_this);
X11_InitTouch(_this);
X11_InitPen(_this);
return true;
}
void X11_VideoQuit(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
if (data->clipboard_window) {
X11_XDestroyWindow(data->display, data->clipboard_window);
}
if (data->xsettings_window) {
X11_XDestroyWindow(data->display, data->xsettings_window);
}
#ifdef X_HAVE_UTF8_STRING
if (data->im) {
X11_XCloseIM(data->im);
}
#endif
X11_QuitModes(_this);
X11_QuitKeyboard(_this);
X11_QuitMouse(_this);
X11_QuitTouch(_this);
X11_QuitPen(_this);
X11_QuitClipboard(_this);
X11_QuitXsettings(_this);
}
bool X11_UseDirectColorVisuals(void)
{
if (SDL_GetHintBoolean(SDL_HINT_VIDEO_X11_NODIRECTCOLOR, false)) {
return false;
}
return true;
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,177 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11video_h_
#define SDL_x11video_h_
#include "../SDL_sysvideo.h"
#include "../../core/linux/SDL_dbus.h"
#include "../../core/linux/SDL_ime.h"
#include "SDL_x11dyn.h"
#include "SDL_x11clipboard.h"
#include "SDL_x11events.h"
#include "SDL_x11keyboard.h"
#include "SDL_x11modes.h"
#include "SDL_x11mouse.h"
#include "SDL_x11opengl.h"
#include "SDL_x11settings.h"
#include "SDL_x11window.h"
#include "SDL_x11vulkan.h"
// Private display data
struct SDL_VideoData
{
Display *display;
Display *request_display;
pid_t pid;
XIM im;
Uint64 screensaver_activity;
int numwindows;
SDL_WindowData **windowlist;
int windowlistlength;
XID window_group;
Window clipboard_window;
SDLX11_ClipboardData clipboard;
SDLX11_ClipboardData primary_selection;
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
SDL_Window *active_cursor_confined_window;
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
Window xsettings_window;
SDLX11_SettingsData xsettings_data;
// This is true for ICCCM2.0-compliant window managers
bool net_wm;
// Useful atoms
struct {
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
Atom WM_TAKE_FOCUS;
Atom WM_NAME;
Atom WM_TRANSIENT_FOR;
Atom _NET_WM_STATE;
Atom _NET_WM_STATE_HIDDEN;
Atom _NET_WM_STATE_FOCUSED;
Atom _NET_WM_STATE_MAXIMIZED_VERT;
Atom _NET_WM_STATE_MAXIMIZED_HORZ;
Atom _NET_WM_STATE_FULLSCREEN;
Atom _NET_WM_STATE_ABOVE;
Atom _NET_WM_STATE_SKIP_TASKBAR;
Atom _NET_WM_STATE_SKIP_PAGER;
Atom _NET_WM_STATE_MODAL;
Atom _NET_WM_MOVERESIZE;
Atom _NET_WM_ALLOWED_ACTIONS;
Atom _NET_WM_ACTION_FULLSCREEN;
Atom _NET_WM_NAME;
Atom _NET_WM_ICON_NAME;
Atom _NET_WM_ICON;
Atom _NET_WM_PING;
Atom _NET_WM_SYNC_REQUEST;
Atom _NET_WM_SYNC_REQUEST_COUNTER;
Atom _NET_WM_WINDOW_OPACITY;
Atom _NET_WM_USER_TIME;
Atom _NET_ACTIVE_WINDOW;
Atom _NET_FRAME_EXTENTS;
Atom _SDL_WAKEUP;
Atom UTF8_STRING;
Atom PRIMARY;
Atom CLIPBOARD;
Atom INCR;
Atom SDL_SELECTION;
Atom TARGETS;
Atom SDL_FORMATS;
Atom XdndAware;
Atom XdndEnter;
Atom XdndLeave;
Atom XdndPosition;
Atom XdndStatus;
Atom XdndTypeList;
Atom XdndActionCopy;
Atom XdndDrop;
Atom XdndFinished;
Atom XdndSelection;
Atom XKLAVIER_STATE;
// Pen atoms (these have names that don't map well to C symbols)
Atom pen_atom_device_product_id;
Atom pen_atom_abs_pressure;
Atom pen_atom_abs_tilt_x;
Atom pen_atom_abs_tilt_y;
Atom pen_atom_wacom_serial_ids;
Atom pen_atom_wacom_tool_type;
} atoms;
SDL_Scancode key_layout[256];
bool selection_waiting;
bool selection_incr_waiting;
bool broken_pointer_grab; // true if XGrabPointer seems unreliable.
Uint64 last_mode_change_deadline;
bool global_mouse_changed;
SDL_Point global_mouse_position;
Uint32 global_mouse_buttons;
SDL_XInput2DeviceInfo *mouse_device_info;
int xinput_master_pointer_device;
bool xinput_hierarchy_changed;
int xrandr_event_base;
struct
{
#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM
XkbDescPtr desc_ptr;
#endif
int event;
unsigned int current_group;
unsigned int xkb_modifiers;
SDL_Keymod sdl_modifiers;
Uint32 numlock_mask;
Uint32 scrolllock_mask;
} xkb;
KeyCode filter_code;
Time filter_time;
#ifdef SDL_VIDEO_VULKAN
// Vulkan variables only valid if _this->vulkan_config.loader_handle is not NULL
SDL_SharedObject *vulkan_xlib_xcb_library;
PFN_XGetXCBConnection vulkan_XGetXCBConnection;
#endif
// Used to interact with the on-screen keyboard
bool is_steam_deck;
bool steam_keyboard_open;
bool is_xwayland;
};
extern bool X11_UseDirectColorVisuals(void);
#endif // SDL_x11video_h_

View file

@ -0,0 +1,290 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
#include "../SDL_vulkan_internal.h"
#include "SDL_x11video.h"
#include "SDL_x11vulkan.h"
#include <X11/Xlib.h>
// #include <xcb/xcb.h>
#ifdef SDL_PLATFORM_OPENBSD
#define DEFAULT_VULKAN "libvulkan.so"
#define DEFAULT_X11_XCB "libX11-xcb.so"
#else
#define DEFAULT_VULKAN "libvulkan.so.1"
#define DEFAULT_X11_XCB "libX11-xcb.so.1"
#endif
/*
typedef uint32_t xcb_window_t;
typedef uint32_t xcb_visualid_t;
*/
bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path)
{
SDL_VideoData *videoData = _this->internal;
VkExtensionProperties *extensions = NULL;
Uint32 extensionCount = 0;
bool hasSurfaceExtension = false;
bool hasXlibSurfaceExtension = false;
bool hasXCBSurfaceExtension = false;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
Uint32 i;
if (_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan already loaded");
}
// Load the Vulkan loader library
if (!path) {
path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY);
}
if (!path) {
path = DEFAULT_VULKAN;
}
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
if (!_this->vulkan_config.loader_handle) {
return false;
}
SDL_strlcpy(_this->vulkan_config.loader_path, path, SDL_arraysize(_this->vulkan_config.loader_path));
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddr) {
goto fail;
}
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) {
goto fail;
}
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
(PFN_vkEnumerateInstanceExtensionProperties)
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
&extensionCount);
if (!extensions) {
goto fail;
}
for (i = 0; i < extensionCount; i++) {
if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasSurfaceExtension = true;
} else if (SDL_strcmp(VK_KHR_XCB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasXCBSurfaceExtension = true;
} else if (SDL_strcmp(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) {
hasXlibSurfaceExtension = true;
}
}
SDL_free(extensions);
if (!hasSurfaceExtension) {
SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension");
goto fail;
}
if (hasXlibSurfaceExtension) {
videoData->vulkan_xlib_xcb_library = NULL;
} else if (!hasXCBSurfaceExtension) {
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");
goto fail;
} else {
const char *libX11XCBLibraryName = SDL_GetHint(SDL_HINT_X11_XCB_LIBRARY);
if (!libX11XCBLibraryName || !*libX11XCBLibraryName) {
libX11XCBLibraryName = DEFAULT_X11_XCB;
}
videoData->vulkan_xlib_xcb_library = SDL_LoadObject(libX11XCBLibraryName);
if (!videoData->vulkan_xlib_xcb_library) {
goto fail;
}
videoData->vulkan_XGetXCBConnection =
(PFN_XGetXCBConnection)SDL_LoadFunction(videoData->vulkan_xlib_xcb_library, "XGetXCBConnection");
if (!videoData->vulkan_XGetXCBConnection) {
SDL_UnloadObject(videoData->vulkan_xlib_xcb_library);
goto fail;
}
}
return true;
fail:
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
return false;
}
void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this)
{
SDL_VideoData *videoData = _this->internal;
if (_this->vulkan_config.loader_handle) {
if (videoData->vulkan_xlib_xcb_library) {
SDL_UnloadObject(videoData->vulkan_xlib_xcb_library);
}
SDL_UnloadObject(_this->vulkan_config.loader_handle);
_this->vulkan_config.loader_handle = NULL;
}
}
char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this,
Uint32 *count)
{
SDL_VideoData *videoData = _this->internal;
if (videoData->vulkan_xlib_xcb_library) {
static const char *const extensionsForXCB[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_XCB_SURFACE_EXTENSION_NAME,
};
if(count) {
*count = SDL_arraysize(extensionsForXCB);
}
return extensionsForXCB;
} else {
static const char *const extensionsForXlib[] = {
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
};
if(count) {
*count = SDL_arraysize(extensionsForXlib);
}
return extensionsForXlib;
}
}
bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface)
{
SDL_VideoData *videoData = _this->internal;
SDL_WindowData *windowData = window->internal;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
if (videoData->vulkan_xlib_xcb_library) {
PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR =
(PFN_vkCreateXcbSurfaceKHR)vkGetInstanceProcAddr(instance,
"vkCreateXcbSurfaceKHR");
VkXcbSurfaceCreateInfoKHR createInfo;
VkResult result;
if (!vkCreateXcbSurfaceKHR) {
return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
SDL_zero(createInfo);
createInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
createInfo.connection = videoData->vulkan_XGetXCBConnection(videoData->display);
if (!createInfo.connection) {
return SDL_SetError("XGetXCBConnection failed");
}
createInfo.window = (xcb_window_t)windowData->xwindow;
result = vkCreateXcbSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
return SDL_SetError("vkCreateXcbSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
}
} else {
PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR =
(PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance,
"vkCreateXlibSurfaceKHR");
VkXlibSurfaceCreateInfoKHR createInfo;
VkResult result;
if (!vkCreateXlibSurfaceKHR) {
return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
SDL_zero(createInfo);
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
createInfo.dpy = videoData->display;
createInfo.window = (xcb_window_t)windowData->xwindow;
result = vkCreateXlibSurfaceKHR(instance, &createInfo, allocator, surface);
if (result != VK_SUCCESS) {
return SDL_SetError("vkCreateXlibSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
}
}
return true; // success!
}
void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator)
{
if (_this->vulkan_config.loader_handle) {
SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator);
}
}
bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex)
{
SDL_VideoData *videoData = _this->internal;
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
const char *forced_visual_id;
VisualID visualid;
if (!_this->vulkan_config.loader_handle) {
return SDL_SetError("Vulkan is not loaded");
}
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
forced_visual_id = SDL_GetHint(SDL_HINT_VIDEO_X11_WINDOW_VISUALID);
if (forced_visual_id) {
visualid = SDL_strtol(forced_visual_id, NULL, 0);
} else {
visualid = X11_XVisualIDFromVisual(DefaultVisual(videoData->display, DefaultScreen(videoData->display)));
}
if (videoData->vulkan_xlib_xcb_library) {
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)vkGetInstanceProcAddr(
instance,
"vkGetPhysicalDeviceXcbPresentationSupportKHR");
if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) {
return SDL_SetError(VK_KHR_XCB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
return vkGetPhysicalDeviceXcbPresentationSupportKHR(physicalDevice,
queueFamilyIndex,
videoData->vulkan_XGetXCBConnection(videoData->display),
visualid);
} else {
PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR =
(PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)vkGetInstanceProcAddr(
instance,
"vkGetPhysicalDeviceXlibPresentationSupportKHR");
if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) {
return SDL_SetError(VK_KHR_XLIB_SURFACE_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
}
return vkGetPhysicalDeviceXlibPresentationSupportKHR(physicalDevice,
queueFamilyIndex,
videoData->display,
visualid);
}
}
#endif

View file

@ -0,0 +1,52 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11vulkan_h_
#define SDL_x11vulkan_h_
#include <SDL3/SDL_vulkan.h>
#if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_X11)
typedef struct xcb_connection_t xcb_connection_t;
typedef xcb_connection_t *(*PFN_XGetXCBConnection)(Display *dpy);
extern bool X11_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path);
extern void X11_Vulkan_UnloadLibrary(SDL_VideoDevice *_this);
extern char const* const* X11_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, Uint32 *count);
extern bool X11_Vulkan_CreateSurface(SDL_VideoDevice *_this,
SDL_Window *window,
VkInstance instance,
const struct VkAllocationCallbacks *allocator,
VkSurfaceKHR *surface);
extern void X11_Vulkan_DestroySurface(SDL_VideoDevice *_this,
VkInstance instance,
VkSurfaceKHR surface,
const struct VkAllocationCallbacks *allocator);
extern bool X11_Vulkan_GetPresentationSupport(SDL_VideoDevice *_this,
VkInstance instance,
VkPhysicalDevice physicalDevice,
Uint32 queueFamilyIndex);
#endif
#endif // SDL_x11vulkan_h_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11window_h_
#define SDL_x11window_h_
/* We need to queue the focus in/out changes because they may occur during
video mode changes and we can respond to them by triggering more mode
changes.
*/
#define PENDING_FOCUS_TIME 200
#ifdef SDL_VIDEO_OPENGL_EGL
#include <EGL/egl.h>
#endif
typedef enum
{
PENDING_FOCUS_NONE,
PENDING_FOCUS_IN,
PENDING_FOCUS_OUT
} PendingFocusEnum;
struct SDL_WindowData
{
SDL_Window *window;
Window xwindow;
Visual *visual;
Colormap colormap;
#ifndef NO_SHARED_MEMORY
// MIT shared memory extension information
bool use_mitshm;
XShmSegmentInfo shminfo;
#endif
XImage *ximage;
GC gc;
XIC ic;
bool created;
int border_left;
int border_right;
int border_top;
int border_bottom;
bool xinput2_mouse_enabled;
bool xinput2_keyboard_enabled;
bool mouse_grabbed;
Uint64 last_focus_event_time;
PendingFocusEnum pending_focus;
Uint64 pending_focus_time;
bool pending_move;
SDL_Point pending_move_point;
XConfigureEvent last_xconfigure;
struct SDL_VideoData *videodata;
unsigned long user_time;
Atom xdnd_req;
Window xdnd_source;
bool flashing_window;
Uint64 flash_cancel_time;
SDL_Window *keyboard_focus;
#ifdef SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
#endif
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
bool pointer_barrier_active;
PointerBarrier barrier[4];
SDL_Rect barrier_rect;
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
XSyncCounter resize_counter;
XSyncValue resize_id;
bool resize_in_progress;
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
SDL_Rect expected;
SDL_DisplayMode requested_fullscreen_mode;
enum
{
X11_PENDING_OP_NONE = 0x00,
X11_PENDING_OP_RESTORE = 0x01,
X11_PENDING_OP_MINIMIZE = 0x02,
X11_PENDING_OP_MAXIMIZE = 0x04,
X11_PENDING_OP_FULLSCREEN = 0x08,
X11_PENDING_OP_MOVE = 0x10,
X11_PENDING_OP_RESIZE = 0x20
} pending_operation;
enum
{
X11_SIZE_MOVE_EVENTS_DISABLE = 0x01, // Events are completely disabled.
X11_SIZE_MOVE_EVENTS_WAIT_FOR_BORDERS = 0x02, // Events are disabled until a _NET_FRAME_EXTENTS event arrives.
} size_move_event_flags;
bool pending_size;
bool pending_position;
bool window_was_maximized;
bool previous_borders_nonzero;
bool toggle_borders;
bool fullscreen_borders_forced_on;
SDL_HitTestResult hit_test_result;
XPoint xim_spot;
char *preedit_text;
XIMFeedback *preedit_feedback;
int preedit_length;
int preedit_cursor;
bool ime_needs_clear_composition;
};
extern void X11_SetNetWMState(SDL_VideoDevice *_this, Window xwindow, SDL_WindowFlags flags);
extern Uint32 X11_GetNetWMState(SDL_VideoDevice *_this, SDL_Window *window, Window xwindow);
extern bool X11_CreateWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_PropertiesID create_props);
extern char *X11_GetWindowTitle(SDL_VideoDevice *_this, Window xwindow);
extern void X11_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
extern bool X11_SetWindowPosition(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowMaximumSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowAspectRatio(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_GetWindowBordersSize(SDL_VideoDevice *_this, SDL_Window *window, int *top, int *left, int *bottom, int *right);
extern bool X11_SetWindowOpacity(SDL_VideoDevice *_this, SDL_Window *window, float opacity);
extern bool X11_SetWindowParent(SDL_VideoDevice *_this, SDL_Window *window, SDL_Window *parent);
extern bool X11_SetWindowModal(SDL_VideoDevice *_this, SDL_Window *window, bool modal);
extern void X11_SetWindowSize(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_HideWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, bool bordered);
extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, bool resizable);
extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, bool on_top);
extern SDL_FullscreenResult X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size);
extern bool X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern bool X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, bool grabbed);
extern void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_SetWindowHitTest(SDL_Window *window, bool enabled);
extern void X11_AcceptDragAndDrop(SDL_Window *window, bool accept);
extern bool X11_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
extern void X11_ShowWindowSystemMenu(SDL_Window *window, int x, int y);
extern bool X11_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_SetWindowFocusable(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
extern bool SDL_X11_SetWindowTitle(Display *display, Window xwindow, char *title);
extern void X11_UpdateWindowPosition(SDL_Window *window, bool use_current_position);
extern void X11_SetWindowMinMax(SDL_Window *window, bool use_current);
#endif // SDL_x11window_h_

View file

@ -0,0 +1,214 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XFIXES)
#include "SDL_x11video.h"
#include "SDL_x11xfixes.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_touch_c.h"
static bool xfixes_initialized = true;
static int xfixes_selection_notify_event = 0;
static int query_xfixes_version(Display *display, int major, int minor)
{
// We don't care if this fails, so long as it sets major/minor on it's way out the door.
X11_XFixesQueryVersion(display, &major, &minor);
return (major * 1000) + minor;
}
static bool xfixes_version_atleast(const int version, const int wantmajor, const int wantminor)
{
return version >= ((wantmajor * 1000) + wantminor);
}
void X11_InitXfixes(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
int version = 0;
int event, error;
int fixes_opcode;
Atom XA_CLIPBOARD = data->atoms.CLIPBOARD;
if (!SDL_X11_HAVE_XFIXES ||
!X11_XQueryExtension(data->display, "XFIXES", &fixes_opcode, &event, &error)) {
return;
}
// Selection tracking is available in all versions of XFixes
xfixes_selection_notify_event = event + XFixesSelectionNotify;
X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display),
XA_CLIPBOARD, XFixesSetSelectionOwnerNotifyMask);
X11_XFixesSelectSelectionInput(data->display, DefaultRootWindow(data->display),
XA_PRIMARY, XFixesSetSelectionOwnerNotifyMask);
// We need at least 5.0 for barriers.
version = query_xfixes_version(data->display, 5, 0);
if (!xfixes_version_atleast(version, 5, 0)) {
return; // X server does not support the version we want at all.
}
xfixes_initialized = 1;
}
bool X11_XfixesIsInitialized(void)
{
return xfixes_initialized;
}
int X11_GetXFixesSelectionNotifyEvent(void)
{
return xfixes_selection_notify_event;
}
bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window)
{
if (SDL_RectEmpty(&window->mouse_rect)) {
X11_ConfineCursorWithFlags(_this, window, NULL, 0);
} else {
if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
X11_ConfineCursorWithFlags(_this, window, &window->mouse_rect, 0);
} else {
// Save the state for when we get focus again
SDL_WindowData *wdata = window->internal;
SDL_memcpy(&wdata->barrier_rect, &window->mouse_rect, sizeof(wdata->barrier_rect));
wdata->pointer_barrier_active = true;
}
}
return true;
}
bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags)
{
/* Yaakuro: For some reason Xfixes when confining inside a rect where the
* edges exactly match, a rectangle the cursor 'slips' out of the barrier.
* To prevent that the lines for the barriers will span the whole screen.
*/
SDL_VideoData *data = _this->internal;
SDL_WindowData *wdata;
if (!X11_XfixesIsInitialized()) {
return SDL_Unsupported();
}
// If there is already a set of barriers active, disable them.
if (data->active_cursor_confined_window) {
X11_DestroyPointerBarrier(_this, data->active_cursor_confined_window);
}
SDL_assert(window != NULL);
wdata = window->internal;
/* If user did not specify an area to confine, destroy the barrier that was/is assigned to
* this window it was assigned */
if (rect) {
int x1, y1, x2, y2;
SDL_Rect bounds;
SDL_GetWindowPosition(window, &bounds.x, &bounds.y);
SDL_GetWindowSize(window, &bounds.w, &bounds.h);
/** Negative values are not allowed. Clip values relative to the specified window. */
x1 = bounds.x + SDL_max(rect->x, 0);
y1 = bounds.y + SDL_max(rect->y, 0);
x2 = SDL_min(bounds.x + rect->x + rect->w, bounds.x + bounds.w);
y2 = SDL_min(bounds.y + rect->y + rect->h, bounds.y + bounds.h);
if ((wdata->barrier_rect.x != rect->x) ||
(wdata->barrier_rect.y != rect->y) ||
(wdata->barrier_rect.w != rect->w) ||
(wdata->barrier_rect.h != rect->h)) {
wdata->barrier_rect = *rect;
}
// Use the display bounds to ensure the barriers don't have corner gaps
SDL_GetDisplayBounds(SDL_GetDisplayForWindow(window), &bounds);
/** Create the left barrier */
wdata->barrier[0] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
x1, bounds.y,
x1, bounds.y + bounds.h,
BarrierPositiveX,
0, NULL);
/** Create the right barrier */
wdata->barrier[1] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
x2, bounds.y,
x2, bounds.y + bounds.h,
BarrierNegativeX,
0, NULL);
/** Create the top barrier */
wdata->barrier[2] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
bounds.x, y1,
bounds.x + bounds.w, y1,
BarrierPositiveY,
0, NULL);
/** Create the bottom barrier */
wdata->barrier[3] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
bounds.x, y2,
bounds.x + bounds.w, y2,
BarrierNegativeY,
0, NULL);
X11_XFlush(data->display);
// Lets remember current active confined window.
data->active_cursor_confined_window = window;
/* User activated the confinement for this window. We use this later to reactivate
* the confinement if it got deactivated by FocusOut or UnmapNotify */
wdata->pointer_barrier_active = true;
} else {
X11_DestroyPointerBarrier(_this, window);
// Only set barrier inactive when user specified NULL and not handled by focus out.
if (flags != X11_BARRIER_HANDLED_BY_EVENT) {
wdata->pointer_barrier_active = false;
}
}
return true;
}
void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window)
{
int i;
SDL_VideoData *data = _this->internal;
if (window) {
SDL_WindowData *wdata = window->internal;
for (i = 0; i < 4; i++) {
if (wdata->barrier[i] > 0) {
X11_XFixesDestroyPointerBarrier(data->display, wdata->barrier[i]);
wdata->barrier[i] = 0;
}
}
X11_XFlush(data->display);
}
data->active_cursor_confined_window = NULL;
}
#endif // SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XFIXES

View file

@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11xfixes_h_
#define SDL_x11xfixes_h_
#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
#define X11_BARRIER_HANDLED_BY_EVENT 1
extern void X11_InitXfixes(SDL_VideoDevice *_this);
extern bool X11_XfixesIsInitialized(void);
extern bool X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags);
extern void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window);
extern int X11_GetXFixesSelectionNotifyEvent(void);
#endif // SDL_VIDEO_DRIVER_X11_XFIXES
#endif // SDL_x11xfixes_h_

View file

@ -0,0 +1,829 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11pen.h"
#include "SDL_x11video.h"
#include "SDL_x11xinput2.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_pen_c.h"
#include "../../events/SDL_touch_c.h"
#define MAX_AXIS 16
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
static bool xinput2_initialized;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
static bool xinput2_multitouch_supported;
#endif
/* Opcode returned X11_XQueryExtension
* It will be used in event processing
* to know that the event came from
* this extension */
static int xinput2_opcode;
static void parse_valuators(const double *input_values, const unsigned char *mask, int mask_len,
double *output_values, int output_values_len)
{
int i = 0, z = 0;
int top = mask_len * 8;
if (top > MAX_AXIS) {
top = MAX_AXIS;
}
SDL_memset(output_values, 0, output_values_len * sizeof(double));
for (; i < top && z < output_values_len; i++) {
if (XIMaskIsSet(mask, i)) {
const int value = (int)*input_values;
output_values[z] = value;
input_values++;
}
z++;
}
}
static int query_xinput2_version(Display *display, int major, int minor)
{
// We don't care if this fails, so long as it sets major/minor on it's way out the door.
X11_XIQueryVersion(display, &major, &minor);
return (major * 1000) + minor;
}
static bool xinput2_version_atleast(const int version, const int wantmajor, const int wantminor)
{
return version >= ((wantmajor * 1000) + wantminor);
}
static SDL_WindowData *xinput2_get_sdlwindowdata(SDL_VideoData *videodata, Window window)
{
int i;
for (i = 0; i < videodata->numwindows; i++) {
SDL_WindowData *d = videodata->windowlist[i];
if (d->xwindow == window) {
return d;
}
}
return NULL;
}
static SDL_Window *xinput2_get_sdlwindow(SDL_VideoData *videodata, Window window)
{
const SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, window);
return windowdata ? windowdata->window : NULL;
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
static void xinput2_normalize_touch_coordinates(SDL_Window *window, double in_x, double in_y, float *out_x, float *out_y)
{
if (window) {
if (window->w == 1) {
*out_x = 0.5f;
} else {
*out_x = (float)in_x / (window->w - 1);
}
if (window->h == 1) {
*out_y = 0.5f;
} else {
*out_y = (float)in_y / (window->h - 1);
}
} else {
// couldn't find the window...
*out_x = (float)in_x;
*out_y = (float)in_y;
}
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
bool X11_InitXinput2(SDL_VideoDevice *_this)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *data = _this->internal;
int version = 0;
XIEventMask eventmask;
unsigned char mask[4] = { 0, 0, 0, 0 };
int event, err;
/* XInput2 is required for relative mouse mode, so you probably want to leave this enabled */
if (!SDL_GetHintBoolean("SDL_VIDEO_X11_XINPUT2", true)) {
return false;
}
/*
* Initialize XInput 2
* According to http://who-t.blogspot.com/2009/05/xi2-recipes-part-1.html its better
* to inform Xserver what version of Xinput we support.The server will store the version we support.
* "As XI2 progresses it becomes important that you use this call as the server may treat the client
* differently depending on the supported version".
*
* FIXME:event and err are not needed but if not passed X11_XQueryExtension returns SegmentationFault
*/
if (!SDL_X11_HAVE_XINPUT2 ||
!X11_XQueryExtension(data->display, "XInputExtension", &xinput2_opcode, &event, &err)) {
return false; // X server does not have XInput at all
}
// We need at least 2.2 for Multitouch, 2.0 otherwise.
version = query_xinput2_version(data->display, 2, 2);
if (!xinput2_version_atleast(version, 2, 0)) {
return false; // X server does not support the version we want at all.
}
xinput2_initialized = true;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH // Multitouch needs XInput 2.2
xinput2_multitouch_supported = xinput2_version_atleast(version, 2, 2);
#endif
// Enable raw motion events for this display
SDL_zero(eventmask);
SDL_zeroa(mask);
eventmask.deviceid = XIAllMasterDevices;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
XISetMask(mask, XI_RawMotion);
XISetMask(mask, XI_RawButtonPress);
XISetMask(mask, XI_RawButtonRelease);
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
// Enable raw touch events if supported
if (X11_Xinput2IsMultitouchSupported()) {
XISetMask(mask, XI_RawTouchBegin);
XISetMask(mask, XI_RawTouchUpdate);
XISetMask(mask, XI_RawTouchEnd);
}
#endif
X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
SDL_zero(eventmask);
SDL_zeroa(mask);
eventmask.deviceid = XIAllDevices;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
XISetMask(mask, XI_HierarchyChanged);
X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
X11_Xinput2UpdateDevices(_this, true);
return true;
#else
return false;
#endif
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
// xi2 device went away? take it out of the list.
static void xinput2_remove_device_info(SDL_VideoData *videodata, const int device_id)
{
SDL_XInput2DeviceInfo *prev = NULL;
SDL_XInput2DeviceInfo *devinfo;
for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
if (devinfo->device_id == device_id) {
SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
if (!prev) {
videodata->mouse_device_info = devinfo->next;
} else {
prev->next = devinfo->next;
}
SDL_free(devinfo);
return;
}
prev = devinfo;
}
}
static SDL_XInput2DeviceInfo *xinput2_get_device_info(SDL_VideoData *videodata, const int device_id)
{
// cache device info as we see new devices.
SDL_XInput2DeviceInfo *prev = NULL;
SDL_XInput2DeviceInfo *devinfo;
XIDeviceInfo *xidevinfo;
int axis = 0;
int i;
for (devinfo = videodata->mouse_device_info; devinfo; devinfo = devinfo->next) {
if (devinfo->device_id == device_id) {
SDL_assert((devinfo == videodata->mouse_device_info) == (prev == NULL));
if (prev) { // move this to the front of the list, assuming we'll get more from this one.
prev->next = devinfo->next;
devinfo->next = videodata->mouse_device_info;
videodata->mouse_device_info = devinfo;
}
return devinfo;
}
prev = devinfo;
}
// don't know about this device yet, query and cache it.
devinfo = (SDL_XInput2DeviceInfo *)SDL_calloc(1, sizeof(SDL_XInput2DeviceInfo));
if (!devinfo) {
return NULL;
}
xidevinfo = X11_XIQueryDevice(videodata->display, device_id, &i);
if (!xidevinfo) {
SDL_free(devinfo);
return NULL;
}
devinfo->device_id = device_id;
/* !!! FIXME: this is sort of hacky because we only care about the first two axes we see, but any given
!!! FIXME: axis could be relative or absolute, and they might not even be the X and Y axes!
!!! FIXME: But we go on, for now. Maybe we need a more robust mouse API in SDL3... */
for (i = 0; i < xidevinfo->num_classes; i++) {
const XIValuatorClassInfo *v = (const XIValuatorClassInfo *)xidevinfo->classes[i];
if (v->type == XIValuatorClass) {
devinfo->relative[axis] = (v->mode == XIModeRelative);
devinfo->minval[axis] = v->min;
devinfo->maxval[axis] = v->max;
if (++axis >= 2) {
break;
}
}
}
X11_XIFreeDeviceInfo(xidevinfo);
devinfo->next = videodata->mouse_device_info;
videodata->mouse_device_info = devinfo;
return devinfo;
}
#endif
void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *videodata = _this->internal;
if (cookie->extension != xinput2_opcode) {
return;
}
switch (cookie->evtype) {
case XI_HierarchyChanged:
{
const XIHierarchyEvent *hierev = (const XIHierarchyEvent *)cookie->data;
int i;
for (i = 0; i < hierev->num_info; i++) {
// pen stuff...
if ((hierev->info[i].flags & (XISlaveRemoved | XIDeviceDisabled)) != 0) {
X11_RemovePenByDeviceID(hierev->info[i].deviceid); // it's okay if this thing isn't actually a pen, it'll handle it.
} else if ((hierev->info[i].flags & (XISlaveAdded | XIDeviceEnabled)) != 0) {
X11_MaybeAddPenByDeviceID(_this, hierev->info[i].deviceid); // this will do more checks to make sure this is valid.
}
// not pen stuff...
if (hierev->info[i].flags & XISlaveRemoved) {
xinput2_remove_device_info(videodata, hierev->info[i].deviceid);
}
}
videodata->xinput_hierarchy_changed = true;
} break;
// !!! 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?
//case XI_PropertyEvent:
//case XI_DeviceChanged:
case XI_RawMotion:
{
const XIRawEvent *rawev = (const XIRawEvent *)cookie->data;
const bool is_pen = X11_FindPenByDeviceID(rawev->sourceid) != NULL;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_XInput2DeviceInfo *devinfo;
double coords[2];
double processed_coords[2];
int i;
Uint64 timestamp = X11_GetEventTimestamp(rawev->time);
videodata->global_mouse_changed = true;
if (is_pen) {
break; // Pens check for XI_Motion instead
}
devinfo = xinput2_get_device_info(videodata, rawev->deviceid);
if (!devinfo) {
break; // oh well.
}
parse_valuators(rawev->raw_values, rawev->valuators.mask,
rawev->valuators.mask_len, coords, 2);
for (i = 0; i < 2; i++) {
if (devinfo->relative[i]) {
processed_coords[i] = coords[i];
} else {
processed_coords[i] = devinfo->prev_coords[i] - coords[i]; // convert absolute to relative
}
}
// Relative mouse motion is delivered to the window with keyboard focus
if (mouse->relative_mode && SDL_GetKeyboardFocus()) {
SDL_SendMouseMotion(timestamp, mouse->focus, (SDL_MouseID)rawev->sourceid, true, (float)processed_coords[0], (float)processed_coords[1]);
}
devinfo->prev_coords[0] = coords[0];
devinfo->prev_coords[1] = coords[1];
} break;
case XI_KeyPress:
case XI_KeyRelease:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
SDL_WindowData *windowdata = X11_FindWindow(_this, xev->event);
XEvent xevent;
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
break;
}
if (cookie->evtype == XI_KeyPress) {
xevent.type = KeyPress;
} else {
xevent.type = KeyRelease;
}
xevent.xkey.serial = xev->serial;
xevent.xkey.send_event = xev->send_event;
xevent.xkey.display = xev->display;
xevent.xkey.window = xev->event;
xevent.xkey.root = xev->root;
xevent.xkey.subwindow = xev->child;
xevent.xkey.time = xev->time;
xevent.xkey.x = (int)xev->event_x;
xevent.xkey.y = (int)xev->event_y;
xevent.xkey.x_root = (int)xev->root_x;
xevent.xkey.y_root = (int)xev->root_y;
xevent.xkey.state = xev->mods.effective;
xevent.xkey.keycode = xev->detail;
xevent.xkey.same_screen = 1;
X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent);
} break;
case XI_RawButtonPress:
case XI_RawButtonRelease:
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
case XI_RawTouchBegin:
case XI_RawTouchUpdate:
case XI_RawTouchEnd:
#endif
{
videodata->global_mouse_changed = true;
} break;
case XI_ButtonPress:
case XI_ButtonRelease:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
const int button = xev->detail;
const bool down = (cookie->evtype == XI_ButtonPress);
if (pen) {
// Only report button event; if there was also pen movement / pressure changes, we expect an XI_Motion event first anyway.
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (button == 1) { // button 1 is the pen tip
SDL_SendPenTouch(0, pen->pen, window, pen->is_eraser, down);
} else {
SDL_SendPenButton(0, pen->pen, window, button - 1, down);
}
} else {
// Otherwise assume a regular mouse
SDL_WindowData *windowdata = xinput2_get_sdlwindowdata(videodata, xev->event);
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
break;
}
if (down) {
X11_HandleButtonPress(_this, windowdata, (SDL_MouseID)xev->sourceid, button,
(float)xev->event_x, (float)xev->event_y, xev->time);
} else {
X11_HandleButtonRelease(_this, windowdata, (SDL_MouseID)xev->sourceid, button, xev->time);
}
}
} break;
/* Register to receive XI_Motion (which deactivates MotionNotify), so that we can distinguish
real mouse motions from synthetic ones, for multitouch and pen support. */
case XI_Motion:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
bool pointer_emulated = ((xev->flags & XIPointerEmulated) != 0);
#else
bool pointer_emulated = false;
#endif
videodata->global_mouse_changed = true;
X11_PenHandle *pen = X11_FindPenByDeviceID(xev->deviceid);
if (pen) {
if (xev->deviceid != xev->sourceid) {
// Discard events from "Master" devices to avoid duplicates.
break;
}
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
SDL_SendPenMotion(0, pen->pen, window, (float) xev->event_x, (float) xev->event_y);
float axes[SDL_PEN_AXIS_COUNT];
X11_PenAxesFromValuators(pen, xev->valuators.values, xev->valuators.mask, xev->valuators.mask_len, axes);
for (int i = 0; i < SDL_arraysize(axes); i++) {
if (pen->valuator_for_axis[i] != SDL_X11_PEN_AXIS_VALUATOR_MISSING) {
SDL_SendPenAxis(0, pen->pen, window, (SDL_PenAxis) i, axes[i]);
}
}
} else if (!pointer_emulated && xev->deviceid == videodata->xinput_master_pointer_device) {
// Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
SDL_Mouse *mouse = SDL_GetMouse();
if (!mouse->relative_mode) {
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
if (window) {
X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
}
}
}
} break;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
case XI_TouchBegin:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
float x, y;
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_DOWN, x, y, 1.0);
} break;
case XI_TouchEnd:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
float x, y;
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_UP, x, y, 1.0);
} break;
case XI_TouchUpdate:
{
const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
float x, y;
SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
SDL_SendTouchMotion(0, xev->sourceid, xev->detail, window, x, y, 1.0);
} break;
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
}
void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
{
}
void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
SDL_VideoData *data = NULL;
XIEventMask eventmask;
unsigned char mask[4] = { 0, 0, 0, 0 };
SDL_WindowData *window_data = NULL;
if (!X11_Xinput2IsMultitouchSupported()) {
return;
}
data = _this->internal;
window_data = window->internal;
eventmask.deviceid = XIAllMasterDevices;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
XISetMask(mask, XI_Motion);
X11_XISelectEvents(data->display, window_data->xwindow, &eventmask, 1);
#endif
}
bool X11_Xinput2IsInitialized(void)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
return xinput2_initialized;
#else
return false;
#endif
}
bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window)
{
SDL_WindowData *windowdata = window->internal;
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
const SDL_VideoData *data = _this->internal;
if (X11_Xinput2IsInitialized()) {
XIEventMask eventmask;
unsigned char mask[4] = { 0, 0, 0, 0 };
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
eventmask.deviceid = XIAllDevices;
// This is not enabled by default because these events are only delivered to the window with mouse focus, not keyboard focus
#ifdef USE_XINPUT2_KEYBOARD
XISetMask(mask, XI_KeyPress);
XISetMask(mask, XI_KeyRelease);
windowdata->xinput2_keyboard_enabled = true;
#endif
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_Motion);
windowdata->xinput2_mouse_enabled = true;
XISetMask(mask, XI_Enter);
XISetMask(mask, XI_Leave);
// Hotplugging:
XISetMask(mask, XI_DeviceChanged);
XISetMask(mask, XI_HierarchyChanged);
XISetMask(mask, XI_PropertyEvent); // E.g., when swapping tablet pens
if (X11_XISelectEvents(data->display, windowdata->xwindow, &eventmask, 1) != Success) {
SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, "Could not enable XInput2 event handling");
windowdata->xinput2_keyboard_enabled = false;
windowdata->xinput2_mouse_enabled = false;
}
}
#endif
if (windowdata->xinput2_keyboard_enabled || windowdata->xinput2_mouse_enabled) {
return true;
}
return false;
}
bool X11_Xinput2IsMultitouchSupported(void)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
return xinput2_initialized && xinput2_multitouch_supported;
#else
return true;
#endif
}
void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
unsigned char mask[4] = { 0, 0, 0, 0 };
XIGrabModifiers mods;
XIEventMask eventmask;
if (!X11_Xinput2IsMultitouchSupported()) {
return;
}
mods.modifiers = XIAnyModifier;
mods.status = 0;
eventmask.deviceid = XIAllDevices;
eventmask.mask_len = sizeof(mask);
eventmask.mask = mask;
XISetMask(eventmask.mask, XI_TouchBegin);
XISetMask(eventmask.mask, XI_TouchUpdate);
XISetMask(eventmask.mask, XI_TouchEnd);
XISetMask(eventmask.mask, XI_Motion);
X11_XIGrabTouchBegin(display, XIAllDevices, data->xwindow, True, &eventmask, 1, &mods);
#endif
}
void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XIGrabModifiers mods;
if (!X11_Xinput2IsMultitouchSupported()) {
return;
}
mods.modifiers = XIAnyModifier;
mods.status = 0;
X11_XIUngrabTouchBegin(display, XIAllDevices, data->xwindow, 1, &mods);
#endif
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
static void AddDeviceID(Uint32 deviceID, Uint32 **list, int *count)
{
int new_count = (*count + 1);
Uint32 *new_list = (Uint32 *)SDL_realloc(*list, new_count * sizeof(*new_list));
if (!new_list) {
// Oh well, we'll drop this one
return;
}
new_list[new_count - 1] = deviceID;
*count = new_count;
*list = new_list;
}
static bool HasDeviceID(Uint32 deviceID, const Uint32 *list, int count)
{
for (int i = 0; i < count; ++i) {
if (deviceID == list[i]) {
return true;
}
}
return false;
}
static void AddDeviceID64(Uint64 deviceID, Uint64 **list, int *count)
{
int new_count = (*count + 1);
Uint64 *new_list = (Uint64 *)SDL_realloc(*list, new_count * sizeof(*new_list));
if (!new_list) {
// Oh well, we'll drop this one
return;
}
new_list[new_count - 1] = deviceID;
*count = new_count;
*list = new_list;
}
static bool HasDeviceID64(Uint64 deviceID, const Uint64 *list, int count)
{
for (int i = 0; i < count; ++i) {
if (deviceID == list[i]) {
return true;
}
}
return false;
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check)
{
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2
SDL_VideoData *data = _this->internal;
XIDeviceInfo *info;
int ndevices;
int old_keyboard_count = 0;
SDL_KeyboardID *old_keyboards = NULL;
int new_keyboard_count = 0;
SDL_KeyboardID *new_keyboards = NULL;
int old_mouse_count = 0;
SDL_MouseID *old_mice = NULL;
int new_mouse_count = 0;
SDL_MouseID *new_mice = NULL;
int old_touch_count = 0;
Uint64 *old_touch_devices = NULL;
int new_touch_count = 0;
Uint64 *new_touch_devices = NULL;
bool send_event = !initial_check;
SDL_assert(X11_Xinput2IsInitialized());
info = X11_XIQueryDevice(data->display, XIAllDevices, &ndevices);
old_keyboards = SDL_GetKeyboards(&old_keyboard_count);
old_mice = SDL_GetMice(&old_mouse_count);
old_touch_devices = SDL_GetTouchDevices(&old_touch_count);
for (int i = 0; i < ndevices; i++) {
XIDeviceInfo *dev = &info[i];
switch (dev->use) {
case XIMasterKeyboard:
case XISlaveKeyboard:
{
SDL_KeyboardID keyboardID = (SDL_KeyboardID)dev->deviceid;
AddDeviceID(keyboardID, &new_keyboards, &new_keyboard_count);
if (!HasDeviceID(keyboardID, old_keyboards, old_keyboard_count)) {
SDL_AddKeyboard(keyboardID, dev->name, send_event);
}
}
break;
case XIMasterPointer:
data->xinput_master_pointer_device = dev->deviceid;
SDL_FALLTHROUGH;
case XISlavePointer:
{
SDL_MouseID mouseID = (SDL_MouseID)dev->deviceid;
AddDeviceID(mouseID, &new_mice, &new_mouse_count);
if (!HasDeviceID(mouseID, old_mice, old_mouse_count)) {
SDL_AddMouse(mouseID, dev->name, send_event);
}
}
break;
default:
break;
}
#ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
for (int j = 0; j < dev->num_classes; j++) {
Uint64 touchID;
SDL_TouchDeviceType touchType;
XIAnyClassInfo *class = dev->classes[j];
XITouchClassInfo *t = (XITouchClassInfo *)class;
// Only touch devices
if (class->type != XITouchClass) {
continue;
}
touchID = (Uint64)t->sourceid;
AddDeviceID64(touchID, &new_touch_devices, &new_touch_count);
if (!HasDeviceID64(touchID, old_touch_devices, old_touch_count)) {
if (t->mode == XIDependentTouch) {
touchType = SDL_TOUCH_DEVICE_INDIRECT_RELATIVE;
} else { // XIDirectTouch
touchType = SDL_TOUCH_DEVICE_DIRECT;
}
SDL_AddTouch(touchID, touchType, dev->name);
}
}
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
}
for (int i = old_keyboard_count; i--;) {
if (!HasDeviceID(old_keyboards[i], new_keyboards, new_keyboard_count)) {
SDL_RemoveKeyboard(old_keyboards[i], send_event);
}
}
for (int i = old_mouse_count; i--;) {
if (!HasDeviceID(old_mice[i], new_mice, new_mouse_count)) {
SDL_RemoveMouse(old_mice[i], send_event);
}
}
for (int i = old_touch_count; i--;) {
if (!HasDeviceID64(old_touch_devices[i], new_touch_devices, new_touch_count)) {
SDL_DelTouch(old_touch_devices[i]);
}
}
SDL_free(old_keyboards);
SDL_free(new_keyboards);
SDL_free(old_mice);
SDL_free(new_mice);
SDL_free(old_touch_devices);
SDL_free(new_touch_devices);
X11_XIFreeDeviceInfo(info);
#endif // SDL_VIDEO_DRIVER_X11_XINPUT2
}
#endif // SDL_VIDEO_DRIVER_X11

View file

@ -0,0 +1,44 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11xinput2_h_
#define SDL_x11xinput2_h_
#ifndef SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
/* Define XGenericEventCookie as forward declaration when
*xinput2 is not available in order to compile */
struct XGenericEventCookie;
typedef struct XGenericEventCookie XGenericEventCookie;
#endif
extern bool X11_InitXinput2(SDL_VideoDevice *_this);
extern void X11_InitXinput2Multitouch(SDL_VideoDevice *_this);
extern void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie);
extern bool X11_Xinput2IsInitialized(void);
extern bool X11_Xinput2IsMultitouchSupported(void);
extern void X11_Xinput2SelectTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_Xinput2GrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
extern void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this, bool initial_check);
#endif // SDL_x11xinput2_h_

View file

@ -0,0 +1,148 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_X11) && defined(SDL_VIDEO_DRIVER_X11_XSYNC)
#include "SDL_x11video.h"
#include "SDL_x11xsync.h"
static int xsync_initialized = 0;
static int query_xsync_version(Display *display, int major, int minor)
{
/* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
X11_XSyncInitialize(display, &major, &minor);
return (major * 1000) + minor;
}
static bool xsync_version_atleast(const int version, const int wantmajor, const int wantminor)
{
return version >= ((wantmajor * 1000) + wantminor);
}
void X11_InitXsync(SDL_VideoDevice *_this)
{
SDL_VideoData *data = _this->internal;
int version = 0;
int event, error;
int sync_opcode;
if (!SDL_X11_HAVE_XSYNC ||
!X11_XQueryExtension(data->display, "SYNC", &sync_opcode, &event, &error)) {
return;
}
/* We need at least 5.0 for barriers. */
version = query_xsync_version(data->display, 5, 0);
if (!xsync_version_atleast(version, 3, 0)) {
return; /* X server does not support the version we want at all. */
}
xsync_initialized = 1;
}
int X11_XsyncIsInitialized(void)
{
return xsync_initialized;
}
int X11_InitResizeSync(SDL_Window *window)
{
SDL_assert(window != NULL);
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
XSyncCounter counter;
CARD32 counter_id;
if (!X11_XsyncIsInitialized()){
return SDL_Unsupported();
}
counter = X11_XSyncCreateCounter(display, (XSyncValue){0, 0});
data->resize_counter = counter;
data->resize_id.lo = 0;
data->resize_id.hi = 0;
data->resize_in_progress = false;
if (counter == None){
return SDL_Unsupported();
}
counter_id = counter;
X11_XChangeProperty(display, data->xwindow, counter_prop, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&counter_id, 1);
return 0;
}
void X11_TermResizeSync(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
Atom counter_prop = data->videodata->atoms._NET_WM_SYNC_REQUEST_COUNTER;
XSyncCounter counter = data->resize_counter;
X11_XDeleteProperty(display, data->xwindow, counter_prop);
if (counter != None) {
X11_XSyncDestroyCounter(display, counter);
}
}
void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event)
{
SDL_WindowData *data = window->internal;
data->resize_id.lo = event->data.l[2];
data->resize_id.hi = event->data.l[3];
data->resize_in_progress = false;
}
void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event)
{
SDL_WindowData *data = window->internal;
if (data->resize_id.lo || data->resize_id.hi) {
data->resize_in_progress = true;
}
}
void X11_HandlePresent(SDL_Window *window)
{
SDL_WindowData *data = window->internal;
Display *display = data->videodata->display;
XSyncCounter counter = data->resize_counter;
if ((counter == None) || (!data->resize_in_progress)) {
return;
}
X11_XSyncSetCounter(display, counter, data->resize_id);
data->resize_id.lo = 0;
data->resize_id.hi = 0;
data->resize_in_progress = false;
}
#endif /* SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XSYNC */

View file

@ -0,0 +1,39 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL_internal.h"
#ifndef SDL_x11xsync_h_
#define SDL_x11xsync_h_
#ifdef SDL_VIDEO_DRIVER_X11_XSYNC
extern void X11_InitXsync(SDL_VideoDevice *_this);
extern int X11_XsyncIsInitialized(void);
int X11_InitResizeSync(SDL_Window *window);
void X11_TermResizeSync(SDL_Window *window);
void X11_HandleSyncRequest(SDL_Window *window, XClientMessageEvent *event);
void X11_HandleConfigure(SDL_Window *window, XConfigureEvent *event);
void X11_HandlePresent(SDL_Window *window);
#endif /* SDL_VIDEO_DRIVER_X11_XSYNC */
#endif /* SDL_x11xsync_h_ */

View file

@ -0,0 +1,753 @@
/*
* Copyright 2007 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Author: Soren Sandmann <sandmann@redhat.com> */
#include "SDL_internal.h"
#include "edid.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#define TRUE 1
#define FALSE 0
static int
get_bit (int in, int bit)
{
return (in & (1 << bit)) >> bit;
}
static int
get_bits (int in, int begin, int end)
{
int mask = (1 << (end - begin + 1)) - 1;
return (in >> begin) & mask;
}
static int
decode_header (const uchar *edid)
{
if (SDL_memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
return TRUE;
return FALSE;
}
static int
decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
{
int is_model_year;
/* Manufacturer Code */
info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6);
info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3;
info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4);
info->manufacturer_code[3] = '\0';
info->manufacturer_code[0] += 'A' - 1;
info->manufacturer_code[1] += 'A' - 1;
info->manufacturer_code[2] += 'A' - 1;
/* Product Code */
info->product_code = edid[0x0b] << 8 | edid[0x0a];
/* Serial Number */
info->serial_number =
edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | (Uint32)edid[0x0f] << 24;
/* Week and Year */
is_model_year = FALSE;
switch (edid[0x10])
{
case 0x00:
info->production_week = -1;
break;
case 0xff:
info->production_week = -1;
is_model_year = TRUE;
break;
default:
info->production_week = edid[0x10];
break;
}
if (is_model_year)
{
info->production_year = -1;
info->model_year = 1990 + edid[0x11];
}
else
{
info->production_year = 1990 + edid[0x11];
info->model_year = -1;
}
return TRUE;
}
static int
decode_edid_version (const uchar *edid, MonitorInfo *info)
{
info->major_version = edid[0x12];
info->minor_version = edid[0x13];
return TRUE;
}
static int
decode_display_parameters (const uchar *edid, MonitorInfo *info)
{
/* Digital vs Analog */
info->is_digital = get_bit (edid[0x14], 7);
if (info->is_digital)
{
int bits;
static const int bit_depth[8] =
{
-1, 6, 8, 10, 12, 14, 16, -1
};
static const Interface interfaces[6] =
{
UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
};
bits = get_bits (edid[0x14], 4, 6);
info->ad.digital.bits_per_primary = bit_depth[bits];
bits = get_bits (edid[0x14], 0, 3);
if (bits <= 5)
info->ad.digital.interface = interfaces[bits];
else
info->ad.digital.interface = UNDEFINED;
}
else
{
int bits = get_bits (edid[0x14], 5, 6);
static const double levels[][3] =
{
{ 0.7, 0.3, 1.0 },
{ 0.714, 0.286, 1.0 },
{ 1.0, 0.4, 1.4 },
{ 0.7, 0.0, 0.7 },
};
info->ad.analog.video_signal_level = levels[bits][0];
info->ad.analog.sync_signal_level = levels[bits][1];
info->ad.analog.total_signal_level = levels[bits][2];
info->ad.analog.blank_to_black = get_bit (edid[0x14], 4);
info->ad.analog.separate_hv_sync = get_bit (edid[0x14], 3);
info->ad.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
info->ad.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
info->ad.analog.serration_on_vsync = get_bit (edid[0x14], 0);
}
/* Screen Size / Aspect Ratio */
if (edid[0x15] == 0 && edid[0x16] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = -1.0;
}
else if (edid[0x16] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = 100.0 / (edid[0x15] + 99);
}
else if (edid[0x15] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = 100.0 / (edid[0x16] + 99);
info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
}
else
{
info->width_mm = 10 * edid[0x15];
info->height_mm = 10 * edid[0x16];
}
/* Gamma */
if (edid[0x17] == 0xFF)
info->gamma = -1.0;
else
info->gamma = (edid[0x17] + 100.0) / 100.0;
/* Features */
info->standby = get_bit (edid[0x18], 7);
info->suspend = get_bit (edid[0x18], 6);
info->active_off = get_bit (edid[0x18], 5);
if (info->is_digital)
{
info->ad.digital.rgb444 = TRUE;
if (get_bit (edid[0x18], 3))
info->ad.digital.ycrcb444 = 1;
if (get_bit (edid[0x18], 4))
info->ad.digital.ycrcb422 = 1;
}
else
{
int bits = get_bits (edid[0x18], 3, 4);
ColorType color_type[4] =
{
MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
};
info->ad.analog.color_type = color_type[bits];
}
info->srgb_is_standard = get_bit (edid[0x18], 2);
/* In 1.3 this is called "has preferred timing" */
info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
/* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
info->continuous_frequency = get_bit (edid[0x18], 0);
return TRUE;
}
static double
decode_fraction (int high, int low)
{
double result = 0.0;
int i;
high = (high << 2) | low;
for (i = 0; i < 10; ++i)
result += get_bit (high, i) * SDL_pow (2, i - 10);
return result;
}
static int
decode_color_characteristics (const uchar *edid, MonitorInfo *info)
{
info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
return TRUE;
}
static int
decode_established_timings (const uchar *edid, MonitorInfo *info)
{
static const Timing established[][8] =
{
{
{ 800, 600, 60 },
{ 800, 600, 56 },
{ 640, 480, 75 },
{ 640, 480, 72 },
{ 640, 480, 67 },
{ 640, 480, 60 },
{ 720, 400, 88 },
{ 720, 400, 70 }
},
{
{ 1280, 1024, 75 },
{ 1024, 768, 75 },
{ 1024, 768, 70 },
{ 1024, 768, 60 },
{ 1024, 768, 87 },
{ 832, 624, 75 },
{ 800, 600, 75 },
{ 800, 600, 72 }
},
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1152, 870, 75 }
},
};
int i, j, idx;
idx = 0;
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 8; ++j)
{
int byte = edid[0x23 + i];
if (get_bit (byte, j) && established[i][j].frequency != 0)
info->established[idx++] = established[i][j];
}
}
return TRUE;
}
static int
decode_standard_timings (const uchar *edid, MonitorInfo *info)
{
int i;
for (i = 0; i < 8; i++)
{
int first = edid[0x26 + 2 * i];
int second = edid[0x27 + 2 * i];
if (first != 0x01 && second != 0x01)
{
int w = 8 * (first + 31);
int h = 0;
switch (get_bits (second, 6, 7))
{
case 0x00: h = (w / 16) * 10; break;
case 0x01: h = (w / 4) * 3; break;
case 0x02: h = (w / 5) * 4; break;
case 0x03: h = (w / 16) * 9; break;
}
info->standard[i].width = w;
info->standard[i].height = h;
info->standard[i].frequency = get_bits (second, 0, 5) + 60;
}
}
return TRUE;
}
static void
decode_lf_string (const uchar *s, int n_chars, char *result)
{
int i;
for (i = 0; i < n_chars; ++i)
{
if (s[i] == 0x0a)
{
*result++ = '\0';
break;
}
else if (s[i] == 0x00)
{
/* Convert embedded 0's to spaces */
*result++ = ' ';
}
else
{
*result++ = s[i];
}
}
}
static void
decode_display_descriptor (const uchar *desc,
MonitorInfo *info)
{
switch (desc[0x03])
{
case 0xFC:
decode_lf_string (desc + 5, 13, info->dsc_product_name);
break;
case 0xFF:
decode_lf_string (desc + 5, 13, info->dsc_serial_number);
break;
case 0xFE:
decode_lf_string (desc + 5, 13, info->dsc_string);
break;
case 0xFD:
/* Range Limits */
break;
case 0xFB:
/* Color Point */
break;
case 0xFA:
/* Timing Identifications */
break;
case 0xF9:
/* Color Management */
break;
case 0xF8:
/* Timing Codes */
break;
case 0xF7:
/* Established Timings */
break;
case 0x10:
break;
}
}
static void
decode_detailed_timing (const uchar *timing,
DetailedTiming *detailed)
{
int bits;
StereoType stereo[] =
{
NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
};
detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
detailed->v_front_porch =
get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
detailed->v_sync =
get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
detailed->right_border = timing[0x0f];
detailed->top_border = timing[0x10];
detailed->interlaced = get_bit (timing[0x11], 7);
/* Stereo */
bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
detailed->stereo = stereo[bits];
/* Sync */
bits = timing[0x11];
detailed->digital_sync = get_bit (bits, 4);
if (detailed->digital_sync)
{
detailed->ad.digital.composite = !get_bit (bits, 3);
if (detailed->ad.digital.composite)
{
detailed->ad.digital.serrations = get_bit (bits, 2);
detailed->ad.digital.negative_vsync = FALSE;
}
else
{
detailed->ad.digital.serrations = FALSE;
detailed->ad.digital.negative_vsync = !get_bit (bits, 2);
}
detailed->ad.digital.negative_hsync = !get_bit (bits, 0);
}
else
{
detailed->ad.analog.bipolar = get_bit (bits, 3);
detailed->ad.analog.serrations = get_bit (bits, 2);
detailed->ad.analog.sync_on_green = !get_bit (bits, 1);
}
}
static int
decode_descriptors (const uchar *edid, MonitorInfo *info)
{
int i;
int timing_idx;
timing_idx = 0;
for (i = 0; i < 4; ++i)
{
int index = 0x36 + i * 18;
if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
{
decode_display_descriptor (edid + index, info);
}
else
{
decode_detailed_timing (
edid + index, &(info->detailed_timings[timing_idx++]));
}
}
info->n_detailed_timings = timing_idx;
return TRUE;
}
static void
decode_check_sum (const uchar *edid,
MonitorInfo *info)
{
int i;
uchar check = 0;
for (i = 0; i < 128; ++i)
check += edid[i];
info->checksum = check;
}
MonitorInfo *
decode_edid (const uchar *edid)
{
MonitorInfo *info = SDL_calloc (1, sizeof (MonitorInfo));
decode_check_sum (edid, info);
if (!decode_header (edid) ||
!decode_vendor_and_product_identification (edid, info) ||
!decode_edid_version (edid, info) ||
!decode_display_parameters (edid, info) ||
!decode_color_characteristics (edid, info) ||
!decode_established_timings (edid, info) ||
!decode_standard_timings (edid, info) ||
!decode_descriptors (edid, info)) {
SDL_free(info);
return NULL;
}
return info;
}
static const char *
yesno (int v)
{
return v? "yes" : "no";
}
void
dump_monitor_info (MonitorInfo *info)
{
int i;
printf ("Checksum: %d (%s)\n",
info->checksum, info->checksum? "incorrect" : "correct");
printf ("Manufacturer Code: %s\n", info->manufacturer_code);
printf ("Product Code: 0x%x\n", info->product_code);
printf ("Serial Number: %u\n", info->serial_number);
if (info->production_week != -1)
printf ("Production Week: %d\n", info->production_week);
else
printf ("Production Week: unspecified\n");
if (info->production_year != -1)
printf ("Production Year: %d\n", info->production_year);
else
printf ("Production Year: unspecified\n");
if (info->model_year != -1)
printf ("Model Year: %d\n", info->model_year);
else
printf ("Model Year: unspecified\n");
printf ("EDID revision: %d.%d\n", info->major_version, info->minor_version);
printf ("Display is %s\n", info->is_digital? "digital" : "analog");
if (info->is_digital)
{
const char *interface;
if (info->ad.digital.bits_per_primary != -1)
printf ("Bits Per Primary: %d\n", info->ad.digital.bits_per_primary);
else
printf ("Bits Per Primary: undefined\n");
switch (info->ad.digital.interface)
{
case DVI: interface = "DVI"; break;
case HDMI_A: interface = "HDMI-a"; break;
case HDMI_B: interface = "HDMI-b"; break;
case MDDI: interface = "MDDI"; break;
case DISPLAY_PORT: interface = "DisplayPort"; break;
case UNDEFINED: interface = "undefined"; break;
default: interface = "unknown"; break;
}
printf ("Interface: %s\n", interface);
printf ("RGB 4:4:4: %s\n", yesno (info->ad.digital.rgb444));
printf ("YCrCb 4:4:4: %s\n", yesno (info->ad.digital.ycrcb444));
printf ("YCrCb 4:2:2: %s\n", yesno (info->ad.digital.ycrcb422));
}
else
{
const char *s;
printf ("Video Signal Level: %f\n", info->ad.analog.video_signal_level);
printf ("Sync Signal Level: %f\n", info->ad.analog.sync_signal_level);
printf ("Total Signal Level: %f\n", info->ad.analog.total_signal_level);
printf ("Blank to Black: %s\n",
yesno (info->ad.analog.blank_to_black));
printf ("Separate HV Sync: %s\n",
yesno (info->ad.analog.separate_hv_sync));
printf ("Composite Sync on H: %s\n",
yesno (info->ad.analog.composite_sync_on_h));
printf ("Serration on VSync: %s\n",
yesno (info->ad.analog.serration_on_vsync));
switch (info->ad.analog.color_type)
{
case UNDEFINED_COLOR: s = "undefined"; break;
case MONOCHROME: s = "monochrome"; break;
case RGB: s = "rgb"; break;
case OTHER_COLOR: s = "other color"; break;
default: s = "unknown"; break;
}
printf ("Color: %s\n", s);
}
if (info->width_mm == -1)
printf ("Width: undefined\n");
else
printf ("Width: %d mm\n", info->width_mm);
if (info->height_mm == -1)
printf ("Height: undefined\n");
else
printf ("Height: %d mm\n", info->height_mm);
if (info->aspect_ratio > 0)
printf ("Aspect Ratio: %f\n", info->aspect_ratio);
else
printf ("Aspect Ratio: undefined\n");
if (info->gamma >= 0)
printf ("Gamma: %f\n", info->gamma);
else
printf ("Gamma: undefined\n");
printf ("Standby: %s\n", yesno (info->standby));
printf ("Suspend: %s\n", yesno (info->suspend));
printf ("Active Off: %s\n", yesno (info->active_off));
printf ("SRGB is Standard: %s\n", yesno (info->srgb_is_standard));
printf ("Preferred Timing Includes Native: %s\n",
yesno (info->preferred_timing_includes_native));
printf ("Continuous Frequency: %s\n", yesno (info->continuous_frequency));
printf ("Red X: %f\n", info->red_x);
printf ("Red Y: %f\n", info->red_y);
printf ("Green X: %f\n", info->green_x);
printf ("Green Y: %f\n", info->green_y);
printf ("Blue X: %f\n", info->blue_x);
printf ("Blue Y: %f\n", info->blue_y);
printf ("White X: %f\n", info->white_x);
printf ("White Y: %f\n", info->white_y);
printf ("Established Timings:\n");
for (i = 0; i < 24; ++i)
{
Timing *timing = &(info->established[i]);
if (timing->frequency == 0)
break;
printf (" %d x %d @ %d Hz\n",
timing->width, timing->height, timing->frequency);
}
printf ("Standard Timings:\n");
for (i = 0; i < 8; ++i)
{
Timing *timing = &(info->standard[i]);
if (timing->frequency == 0)
break;
printf (" %d x %d @ %d Hz\n",
timing->width, timing->height, timing->frequency);
}
for (i = 0; i < info->n_detailed_timings; ++i)
{
DetailedTiming *timing = &(info->detailed_timings[i]);
const char *s;
printf ("Timing%s: \n",
(i == 0 && info->preferred_timing_includes_native)?
" (Preferred)" : "");
printf (" Pixel Clock: %d\n", timing->pixel_clock);
printf (" H Addressable: %d\n", timing->h_addr);
printf (" H Blank: %d\n", timing->h_blank);
printf (" H Front Porch: %d\n", timing->h_front_porch);
printf (" H Sync: %d\n", timing->h_sync);
printf (" V Addressable: %d\n", timing->v_addr);
printf (" V Blank: %d\n", timing->v_blank);
printf (" V Front Porch: %d\n", timing->v_front_porch);
printf (" V Sync: %d\n", timing->v_sync);
printf (" Width: %d mm\n", timing->width_mm);
printf (" Height: %d mm\n", timing->height_mm);
printf (" Right Border: %d\n", timing->right_border);
printf (" Top Border: %d\n", timing->top_border);
switch (timing->stereo)
{
default:
case NO_STEREO: s = "No Stereo"; break;
case FIELD_RIGHT: s = "Field Sequential, Right on Sync"; break;
case FIELD_LEFT: s = "Field Sequential, Left on Sync"; break;
case TWO_WAY_RIGHT_ON_EVEN: s = "Two-way, Right on Even"; break;
case TWO_WAY_LEFT_ON_EVEN: s = "Two-way, Left on Even"; break;
case FOUR_WAY_INTERLEAVED: s = "Four-way Interleaved"; break;
case SIDE_BY_SIDE: s = "Side-by-Side"; break;
}
printf (" Stereo: %s\n", s);
if (timing->digital_sync)
{
printf (" Digital Sync:\n");
printf (" composite: %s\n", yesno (timing->ad.digital.composite));
printf (" serrations: %s\n", yesno (timing->ad.digital.serrations));
printf (" negative vsync: %s\n",
yesno (timing->ad.digital.negative_vsync));
printf (" negative hsync: %s\n",
yesno (timing->ad.digital.negative_hsync));
}
else
{
printf (" Analog Sync:\n");
printf (" bipolar: %s\n", yesno (timing->ad.analog.bipolar));
printf (" serrations: %s\n", yesno (timing->ad.analog.serrations));
printf (" sync on green: %s\n", yesno (
timing->ad.analog.sync_on_green));
}
}
printf ("Detailed Product information:\n");
printf (" Product Name: %s\n", info->dsc_product_name);
printf (" Serial Number: %s\n", info->dsc_serial_number);
printf (" Unspecified String: %s\n", info->dsc_string);
}

191
vendor/sdl-3.2.10/src/video/x11/edid.h vendored Normal file
View file

@ -0,0 +1,191 @@
/*
* Copyright 2007 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Author: Soren Sandmann <sandmann@redhat.com> */
typedef unsigned char uchar;
typedef struct MonitorInfo MonitorInfo;
typedef struct Timing Timing;
typedef struct DetailedTiming DetailedTiming;
typedef enum
{
UNDEFINED,
DVI,
HDMI_A,
HDMI_B,
MDDI,
DISPLAY_PORT
} Interface;
typedef enum
{
UNDEFINED_COLOR,
MONOCHROME,
RGB,
OTHER_COLOR
} ColorType;
typedef enum
{
NO_STEREO,
FIELD_RIGHT,
FIELD_LEFT,
TWO_WAY_RIGHT_ON_EVEN,
TWO_WAY_LEFT_ON_EVEN,
FOUR_WAY_INTERLEAVED,
SIDE_BY_SIDE
} StereoType;
struct Timing
{
int width;
int height;
int frequency;
};
struct DetailedTiming
{
int pixel_clock;
int h_addr;
int h_blank;
int h_sync;
int h_front_porch;
int v_addr;
int v_blank;
int v_sync;
int v_front_porch;
int width_mm;
int height_mm;
int right_border;
int top_border;
int interlaced;
StereoType stereo;
int digital_sync;
union
{
struct
{
int bipolar;
int serrations;
int sync_on_green;
} analog;
struct
{
int composite;
int serrations;
int negative_vsync;
int negative_hsync;
} digital;
} ad;
};
struct MonitorInfo
{
int checksum;
char manufacturer_code[4];
int product_code;
unsigned int serial_number;
int production_week; // -1 if not specified
int production_year; // -1 if not specified
int model_year; // -1 if not specified
int major_version;
int minor_version;
int is_digital;
union
{
struct
{
int bits_per_primary;
Interface interface;
int rgb444;
int ycrcb444;
int ycrcb422;
} digital;
struct
{
double video_signal_level;
double sync_signal_level;
double total_signal_level;
int blank_to_black;
int separate_hv_sync;
int composite_sync_on_h;
int composite_sync_on_green;
int serration_on_vsync;
ColorType color_type;
} analog;
} ad;
int width_mm; // -1 if not specified
int height_mm; // -1 if not specified
double aspect_ratio; // -1.0 if not specififed
double gamma; // -1.0 if not specified
int standby;
int suspend;
int active_off;
int srgb_is_standard;
int preferred_timing_includes_native;
int continuous_frequency;
double red_x;
double red_y;
double green_x;
double green_y;
double blue_x;
double blue_y;
double white_x;
double white_y;
Timing established[24]; // Terminated by 0x0x0
Timing standard[8];
int n_detailed_timings;
DetailedTiming detailed_timings[4]; /* If monitor has a preferred
* mode, it is the first one
* (whether it has, is
* determined by the
* preferred_timing_includes
* bit.
*/
// Optional product description
char dsc_serial_number[14];
char dsc_product_name[14];
char dsc_string[14]; // Unspecified ASCII data
};
MonitorInfo *decode_edid(const uchar *data);
void dump_monitor_info(MonitorInfo *info);
char *make_display_name(const char *output_name,
const MonitorInfo *info);

View file

@ -0,0 +1,859 @@
/*
* Copyright © 2001, 2007 Red Hat, Inc.
* Copyright 2024 Igalia S.L.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Red Hat not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Red Hat makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Owen Taylor, Red Hat, Inc.
*/
#include "SDL_internal.h"
#ifdef SDL_VIDEO_DRIVER_X11
#include "SDL_x11video.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "xsettings-client.h"
struct _XSettingsClient
{
Display *display;
int screen;
XSettingsNotifyFunc notify;
XSettingsWatchFunc watch;
void *cb_data;
XSettingsGrabFunc grab;
XSettingsGrabFunc ungrab;
Window manager_window;
Atom manager_atom;
Atom selection_atom;
Atom xsettings_atom;
XSettingsList *settings;
};
static void
notify_changes (XSettingsClient *client,
XSettingsList *old_list)
{
XSettingsList *old_iter = old_list;
XSettingsList *new_iter = client->settings;
if (!client->notify)
return;
while (old_iter || new_iter)
{
int cmp;
if (old_iter && new_iter)
cmp = strcmp (old_iter->setting->name, new_iter->setting->name);
else if (old_iter)
cmp = -1;
else
cmp = 1;
if (cmp < 0)
{
client->notify (old_iter->setting->name,
XSETTINGS_ACTION_DELETED,
NULL,
client->cb_data);
}
else if (cmp == 0)
{
if (!xsettings_setting_equal (old_iter->setting,
new_iter->setting))
client->notify (old_iter->setting->name,
XSETTINGS_ACTION_CHANGED,
new_iter->setting,
client->cb_data);
}
else
{
client->notify (new_iter->setting->name,
XSETTINGS_ACTION_NEW,
new_iter->setting,
client->cb_data);
}
if (old_iter)
old_iter = old_iter->next;
if (new_iter)
new_iter = new_iter->next;
}
}
static int
ignore_errors (Display *display, XErrorEvent *event)
{
return True;
}
static char local_byte_order = '\0';
#define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
static XSettingsResult
fetch_card16 (XSettingsBuffer *buffer,
CARD16 *result)
{
CARD16 x;
if (BYTES_LEFT (buffer) < 2)
return XSETTINGS_ACCESS;
x = *(CARD16 *)buffer->pos;
buffer->pos += 2;
if (buffer->byte_order == local_byte_order)
*result = x;
else
*result = (x << 8) | (x >> 8);
return XSETTINGS_SUCCESS;
}
static XSettingsResult
fetch_ushort (XSettingsBuffer *buffer,
unsigned short *result)
{
CARD16 x;
XSettingsResult r;
r = fetch_card16 (buffer, &x);
if (r == XSETTINGS_SUCCESS)
*result = x;
return r;
}
static XSettingsResult
fetch_card32 (XSettingsBuffer *buffer,
CARD32 *result)
{
CARD32 x;
if (BYTES_LEFT (buffer) < 4)
return XSETTINGS_ACCESS;
x = *(CARD32 *)buffer->pos;
buffer->pos += 4;
if (buffer->byte_order == local_byte_order)
*result = x;
else
*result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
return XSETTINGS_SUCCESS;
}
static XSettingsResult
fetch_card8 (XSettingsBuffer *buffer,
CARD8 *result)
{
if (BYTES_LEFT (buffer) < 1)
return XSETTINGS_ACCESS;
*result = *(CARD8 *)buffer->pos;
buffer->pos += 1;
return XSETTINGS_SUCCESS;
}
#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
static XSettingsList *
parse_settings (unsigned char *data,
size_t len)
{
XSettingsBuffer buffer;
XSettingsResult result = XSETTINGS_SUCCESS;
XSettingsList *settings = NULL;
CARD32 serial;
CARD32 n_entries;
CARD32 i;
XSettingsSetting *setting = NULL;
char buffer_byte_order = '\0';
local_byte_order = xsettings_byte_order ();
buffer.pos = buffer.data = data;
buffer.len = len;
buffer.byte_order = '\0';
result = fetch_card8 (&buffer, (unsigned char *) &buffer_byte_order);
if (buffer_byte_order != MSBFirst &&
buffer_byte_order != LSBFirst)
{
fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
result = XSETTINGS_FAILED;
goto out;
}
buffer.byte_order = buffer_byte_order;
buffer.pos += 3;
result = fetch_card32 (&buffer, &serial);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_card32 (&buffer, &n_entries);
if (result != XSETTINGS_SUCCESS)
goto out;
for (i = 0; i < n_entries; i++)
{
CARD8 type;
CARD16 name_len;
CARD32 v_int;
size_t pad_len;
result = fetch_card8 (&buffer, &type);
if (result != XSETTINGS_SUCCESS)
goto out;
buffer.pos += 1;
result = fetch_card16 (&buffer, &name_len);
if (result != XSETTINGS_SUCCESS)
goto out;
pad_len = XSETTINGS_PAD(name_len, 4);
if (BYTES_LEFT (&buffer) < pad_len)
{
result = XSETTINGS_ACCESS;
goto out;
}
setting = malloc (sizeof *setting);
if (!setting)
{
result = XSETTINGS_NO_MEM;
goto out;
}
setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
setting->name = malloc (name_len + 1);
if (!setting->name)
{
result = XSETTINGS_NO_MEM;
goto out;
}
memcpy (setting->name, buffer.pos, name_len);
setting->name[name_len] = '\0';
buffer.pos += pad_len;
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
setting->last_change_serial = v_int;
switch (type)
{
case XSETTINGS_TYPE_INT:
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
setting->data.v_int = (INT32)v_int;
break;
case XSETTINGS_TYPE_STRING:
result = fetch_card32 (&buffer, &v_int);
if (result != XSETTINGS_SUCCESS)
goto out;
pad_len = XSETTINGS_PAD (v_int, 4);
if (v_int + 1 == 0 || /* Guard against wrap-around */
BYTES_LEFT (&buffer) < pad_len)
{
result = XSETTINGS_ACCESS;
goto out;
}
setting->data.v_string = malloc (v_int + 1);
if (!setting->data.v_string)
{
result = XSETTINGS_NO_MEM;
goto out;
}
memcpy (setting->data.v_string, buffer.pos, v_int);
setting->data.v_string[v_int] = '\0';
buffer.pos += pad_len;
break;
case XSETTINGS_TYPE_COLOR:
result = fetch_ushort (&buffer, &setting->data.v_color.red);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.green);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.blue);
if (result != XSETTINGS_SUCCESS)
goto out;
result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
if (result != XSETTINGS_SUCCESS)
goto out;
break;
default:
/* Quietly ignore unknown types */
break;
}
setting->type = type;
result = xsettings_list_insert (&settings, setting);
if (result != XSETTINGS_SUCCESS)
goto out;
setting = NULL;
}
out:
if (result != XSETTINGS_SUCCESS)
{
switch (result)
{
case XSETTINGS_NO_MEM:
fprintf(stderr, "Out of memory reading XSETTINGS property\n");
break;
case XSETTINGS_ACCESS:
fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
break;
case XSETTINGS_DUPLICATE_ENTRY:
fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
SDL_FALLTHROUGH;
case XSETTINGS_FAILED:
SDL_FALLTHROUGH;
case XSETTINGS_SUCCESS:
SDL_FALLTHROUGH;
case XSETTINGS_NO_ENTRY:
break;
}
if (setting)
xsettings_setting_free (setting);
xsettings_list_free (settings);
settings = NULL;
}
return settings;
}
static void
read_settings (XSettingsClient *client)
{
Atom type;
int format;
unsigned long n_items;
unsigned long bytes_after;
unsigned char *data;
int result;
int (*old_handler) (Display *, XErrorEvent *);
XSettingsList *old_list = client->settings;
client->settings = NULL;
if (client->manager_window)
{
old_handler = X11_XSetErrorHandler (ignore_errors);
result = X11_XGetWindowProperty (client->display, client->manager_window,
client->xsettings_atom, 0, LONG_MAX,
False, client->xsettings_atom,
&type, &format, &n_items, &bytes_after, &data);
X11_XSetErrorHandler (old_handler);
if (result == Success && type != None)
{
if (type != client->xsettings_atom)
{
fprintf (stderr, "Invalid type for XSETTINGS property");
}
else if (format != 8)
{
fprintf (stderr, "Invalid format for XSETTINGS property %d", format);
}
else
client->settings = parse_settings (data, n_items);
X11_XFree (data);
}
}
notify_changes (client, old_list);
xsettings_list_free (old_list);
}
static void
add_events (Display *display,
Window window,
long mask)
{
XWindowAttributes attr;
X11_XGetWindowAttributes (display, window, &attr);
X11_XSelectInput (display, window, attr.your_event_mask | mask);
}
static void
check_manager_window (XSettingsClient *client)
{
if (client->manager_window && client->watch)
client->watch (client->manager_window, False, 0, client->cb_data);
if (client->grab)
client->grab (client->display);
else
X11_XGrabServer (client->display);
client->manager_window = X11_XGetSelectionOwner (client->display,
client->selection_atom);
if (client->manager_window)
X11_XSelectInput (client->display, client->manager_window,
PropertyChangeMask | StructureNotifyMask);
if (client->ungrab)
client->ungrab (client->display);
else
X11_XUngrabServer (client->display);
X11_XFlush (client->display);
if (client->manager_window && client->watch)
{
if (!client->watch (client->manager_window, True,
PropertyChangeMask | StructureNotifyMask,
client->cb_data))
{
/* Inability to watch the window probably means that it was destroyed
* after we ungrabbed
*/
client->manager_window = None;
return;
}
}
read_settings (client);
}
XSettingsClient *
xsettings_client_new (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data)
{
return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data,
NULL, NULL);
}
XSettingsClient *
xsettings_client_new_with_grab_funcs (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data,
XSettingsGrabFunc grab,
XSettingsGrabFunc ungrab)
{
XSettingsClient *client;
char buffer[256];
char *atom_names[3];
Atom atoms[3];
client = malloc (sizeof *client);
if (!client)
return NULL;
client->display = display;
client->screen = screen;
client->notify = notify;
client->watch = watch;
client->cb_data = cb_data;
client->grab = grab;
client->ungrab = ungrab;
client->manager_window = None;
client->settings = NULL;
sprintf(buffer, "_XSETTINGS_S%d", screen);
atom_names[0] = buffer;
atom_names[1] = "_XSETTINGS_SETTINGS";
atom_names[2] = "MANAGER";
#ifdef HAVE_XINTERNATOMS
XInternAtoms (display, atom_names, 3, False, atoms);
#else
atoms[0] = X11_XInternAtom (display, atom_names[0], False);
atoms[1] = X11_XInternAtom (display, atom_names[1], False);
atoms[2] = X11_XInternAtom (display, atom_names[2], False);
#endif
client->selection_atom = atoms[0];
client->xsettings_atom = atoms[1];
client->manager_atom = atoms[2];
/* Select on StructureNotify so we get MANAGER events
*/
add_events (display, RootWindow (display, screen), StructureNotifyMask);
if (client->watch)
client->watch (RootWindow (display, screen), True, StructureNotifyMask,
client->cb_data);
check_manager_window (client);
return client;
}
void
xsettings_client_set_grab_func (XSettingsClient *client,
XSettingsGrabFunc grab)
{
client->grab = grab;
}
void
xsettings_client_set_ungrab_func (XSettingsClient *client,
XSettingsGrabFunc ungrab)
{
client->ungrab = ungrab;
}
void
xsettings_client_destroy (XSettingsClient *client)
{
if (client->watch)
client->watch (RootWindow (client->display, client->screen),
False, 0, client->cb_data);
if (client->manager_window && client->watch)
client->watch (client->manager_window, False, 0, client->cb_data);
xsettings_list_free (client->settings);
free (client);
}
XSettingsResult
xsettings_client_get_setting (XSettingsClient *client,
const char *name,
XSettingsSetting **setting)
{
XSettingsSetting *search = xsettings_list_lookup (client->settings, name);
if (search)
{
*setting = xsettings_setting_copy (search);
return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM;
}
else
return XSETTINGS_NO_ENTRY;
}
Bool
xsettings_client_process_event (XSettingsClient *client,
const XEvent *xev)
{
/* The checks here will not unlikely cause us to reread
* the properties from the manager window a number of
* times when the manager changes from A->B. But manager changes
* are going to be pretty rare.
*/
if (xev->xany.window == RootWindow (client->display, client->screen))
{
if (xev->xany.type == ClientMessage &&
xev->xclient.message_type == client->manager_atom &&
xev->xclient.data.l[1] == client->selection_atom)
{
check_manager_window (client);
return True;
}
}
else if (xev->xany.window == client->manager_window)
{
if (xev->xany.type == DestroyNotify)
{
check_manager_window (client);
return False;
}
else if (xev->xany.type == PropertyNotify)
{
read_settings (client);
return True;
}
}
return False;
}
XSettingsSetting *
xsettings_setting_copy (XSettingsSetting *setting)
{
XSettingsSetting *result;
size_t str_len;
result = malloc (sizeof *result);
if (!result)
return NULL;
str_len = strlen (setting->name);
result->name = malloc (str_len + 1);
if (!result->name)
goto err;
memcpy (result->name, setting->name, str_len + 1);
result->type = setting->type;
switch (setting->type)
{
case XSETTINGS_TYPE_INT:
result->data.v_int = setting->data.v_int;
break;
case XSETTINGS_TYPE_COLOR:
result->data.v_color = setting->data.v_color;
break;
case XSETTINGS_TYPE_STRING:
str_len = strlen (setting->data.v_string);
result->data.v_string = malloc (str_len + 1);
if (!result->data.v_string)
goto err;
memcpy (result->data.v_string, setting->data.v_string, str_len + 1);
break;
}
result->last_change_serial = setting->last_change_serial;
return result;
err:
if (result->name)
free (result->name);
free (result);
return NULL;
}
XSettingsList *
xsettings_list_copy (XSettingsList *list)
{
XSettingsList *new = NULL;
XSettingsList *old_iter = list;
XSettingsList *new_iter = NULL;
while (old_iter)
{
XSettingsList *new_node;
new_node = malloc (sizeof *new_node);
if (!new_node)
goto error;
new_node->setting = xsettings_setting_copy (old_iter->setting);
if (!new_node->setting)
{
free (new_node);
goto error;
}
if (new_iter)
new_iter->next = new_node;
else
{
new = new_node;
new->next = NULL;
}
new_iter = new_node;
old_iter = old_iter->next;
}
return new;
error:
xsettings_list_free (new);
return NULL;
}
int
xsettings_setting_equal (XSettingsSetting *setting_a,
XSettingsSetting *setting_b)
{
if (setting_a->type != setting_b->type)
return 0;
if (strcmp (setting_a->name, setting_b->name) != 0)
return 0;
switch (setting_a->type)
{
case XSETTINGS_TYPE_INT:
return setting_a->data.v_int == setting_b->data.v_int;
case XSETTINGS_TYPE_COLOR:
return (setting_a->data.v_color.red == setting_b->data.v_color.red &&
setting_a->data.v_color.green == setting_b->data.v_color.green &&
setting_a->data.v_color.blue == setting_b->data.v_color.blue &&
setting_a->data.v_color.alpha == setting_b->data.v_color.alpha);
case XSETTINGS_TYPE_STRING:
return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0;
}
return 0;
}
void
xsettings_setting_free (XSettingsSetting *setting)
{
if (setting->type == XSETTINGS_TYPE_STRING)
free (setting->data.v_string);
if (setting->name)
free (setting->name);
free (setting);
}
void
xsettings_list_free (XSettingsList *list)
{
while (list)
{
XSettingsList *next = list->next;
xsettings_setting_free (list->setting);
free (list);
list = next;
}
}
XSettingsResult
xsettings_list_insert (XSettingsList **list,
XSettingsSetting *setting)
{
XSettingsList *node;
XSettingsList *iter;
XSettingsList *last = NULL;
node = malloc (sizeof *node);
if (!node)
return XSETTINGS_NO_MEM;
node->setting = setting;
iter = *list;
while (iter)
{
int cmp = strcmp (setting->name, iter->setting->name);
if (cmp < 0)
break;
else if (cmp == 0)
{
free (node);
return XSETTINGS_DUPLICATE_ENTRY;
}
last = iter;
iter = iter->next;
}
if (last)
last->next = node;
else
*list = node;
node->next = iter;
return XSETTINGS_SUCCESS;
}
XSettingsResult
xsettings_list_delete (XSettingsList **list,
const char *name)
{
XSettingsList *iter;
XSettingsList *last = NULL;
iter = *list;
while (iter)
{
if (strcmp (name, iter->setting->name) == 0)
{
if (last)
last->next = iter->next;
else
*list = iter->next;
xsettings_setting_free (iter->setting);
free (iter);
return XSETTINGS_SUCCESS;
}
last = iter;
iter = iter->next;
}
return XSETTINGS_FAILED;
}
XSettingsSetting *
xsettings_list_lookup (XSettingsList *list,
const char *name)
{
XSettingsList *iter;
iter = list;
while (iter)
{
if (strcmp (name, iter->setting->name) == 0)
return iter->setting;
iter = iter->next;
}
return NULL;
}
char
xsettings_byte_order (void)
{
CARD32 myint = 0x01020304;
return (*(char *)&myint == 1) ? MSBFirst : LSBFirst;
}
#endif /* SDL_VIDEO_DRIVER_X11 */

View file

@ -0,0 +1,153 @@
/*
* Copyright © 2001, 2007 Red Hat, Inc.
* Copyright 2024 Igalia S.L.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Red Hat not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Red Hat makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: Owen Taylor, Red Hat, Inc.
*/
#ifndef XSETTINGS_CLIENT_H
#define XSETTINGS_CLIENT_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct _XSettingsBuffer XSettingsBuffer;
typedef struct _XSettingsColor XSettingsColor;
typedef struct _XSettingsList XSettingsList;
typedef struct _XSettingsSetting XSettingsSetting;
/* Types of settings possible. Enum values correspond to
* protocol values.
*/
typedef enum
{
XSETTINGS_TYPE_INT = 0,
XSETTINGS_TYPE_STRING = 1,
XSETTINGS_TYPE_COLOR = 2
} XSettingsType;
typedef enum
{
XSETTINGS_SUCCESS,
XSETTINGS_NO_MEM,
XSETTINGS_ACCESS,
XSETTINGS_FAILED,
XSETTINGS_NO_ENTRY,
XSETTINGS_DUPLICATE_ENTRY
} XSettingsResult;
struct _XSettingsBuffer
{
char byte_order;
size_t len;
unsigned char *data;
unsigned char *pos;
};
struct _XSettingsColor
{
unsigned short red, green, blue, alpha;
};
struct _XSettingsList
{
XSettingsSetting *setting;
XSettingsList *next;
};
struct _XSettingsSetting
{
char *name;
XSettingsType type;
union {
int v_int;
char *v_string;
XSettingsColor v_color;
} data;
unsigned long last_change_serial;
};
XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting);
void xsettings_setting_free (XSettingsSetting *setting);
int xsettings_setting_equal (XSettingsSetting *setting_a,
XSettingsSetting *setting_b);
void xsettings_list_free (XSettingsList *list);
XSettingsList *xsettings_list_copy (XSettingsList *list);
XSettingsResult xsettings_list_insert (XSettingsList **list,
XSettingsSetting *setting);
XSettingsSetting *xsettings_list_lookup (XSettingsList *list,
const char *name);
XSettingsResult xsettings_list_delete (XSettingsList **list,
const char *name);
char xsettings_byte_order (void);
#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
typedef struct _XSettingsClient XSettingsClient;
typedef enum
{
XSETTINGS_ACTION_NEW,
XSETTINGS_ACTION_CHANGED,
XSETTINGS_ACTION_DELETED
} XSettingsAction;
typedef void (*XSettingsNotifyFunc) (const char *name,
XSettingsAction action,
XSettingsSetting *setting,
void *cb_data);
typedef Bool (*XSettingsWatchFunc) (Window window,
Bool is_start,
long mask,
void *cb_data);
typedef void (*XSettingsGrabFunc) (Display *display);
XSettingsClient *xsettings_client_new (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data);
XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display,
int screen,
XSettingsNotifyFunc notify,
XSettingsWatchFunc watch,
void *cb_data,
XSettingsGrabFunc grab,
XSettingsGrabFunc ungrab);
void xsettings_client_set_grab_func (XSettingsClient *client,
XSettingsGrabFunc grab);
void xsettings_client_set_ungrab_func (XSettingsClient *client,
XSettingsGrabFunc ungrab);
void xsettings_client_destroy (XSettingsClient *client);
Bool xsettings_client_process_event (XSettingsClient *client,
const XEvent *xev);
XSettingsResult xsettings_client_get_setting (XSettingsClient *client,
const char *name,
XSettingsSetting **setting);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* XSETTINGS_CLIENT_H */