diff --git a/src/patches/0005-Made-GLES-work-on-X11.patch b/src/patches/0005-Made-GLES-work-on-X11.patch new file mode 100644 index 0000000..7cccbc7 --- /dev/null +++ b/src/patches/0005-Made-GLES-work-on-X11.patch @@ -0,0 +1,11275 @@ +From a0005e38a1707de072fbf013dfc14118e2e22e32 Mon Sep 17 00:00:00 2001 +From: Sergey Lapin +Date: Thu, 20 Mar 2025 21:49:14 +0300 +Subject: [PATCH] Made GLES work on X11 + +--- + core/SCsub | 2 +- + .../gles2/rasterizer_canvas_base_gles2.cpp | 3 + + drivers/gles2/rasterizer_gles2.cpp | 10 + + drivers/gles2/rasterizer_storage_gles2.cpp | 3 + + platform/x11es/SCsub | 27 + + platform/x11es/context_gl_x11.cpp | 306 ++ + platform/x11es/context_gl_x11.h | 89 + + platform/x11es/crash_handler_x11.cpp | 173 + + platform/x11es/crash_handler_x11.h | 47 + + platform/x11es/detect.py | 433 ++ + platform/x11es/detect_prime.cpp | 246 + + platform/x11es/detect_prime.h | 38 + + platform/x11es/godot_x11.cpp | 83 + + platform/x11es/joypad_linux.cpp | 595 +++ + platform/x11es/joypad_linux.h | 125 + + platform/x11es/key_mapping_x11.cpp | 1958 +++++++ + platform/x11es/key_mapping_x11.h | 55 + + platform/x11es/libudev-so_wrap.c | 829 +++ + platform/x11es/libudev-so_wrap.h | 376 ++ + platform/x11es/logo.png | Bin 0 -> 1679 bytes + platform/x11es/os_x11.cpp | 4493 +++++++++++++++++ + platform/x11es/os_x11.h | 398 ++ + platform/x11es/platform_config.h | 49 + + platform/x11es/platform_x11_builders.py | 18 + + platform/x11es/power_x11.cpp | 571 +++ + platform/x11es/power_x11.h | 65 + + 26 files changed, 10991 insertions(+), 1 deletion(-) + create mode 100644 platform/x11es/SCsub + create mode 100644 platform/x11es/context_gl_x11.cpp + create mode 100644 platform/x11es/context_gl_x11.h + create mode 100644 platform/x11es/crash_handler_x11.cpp + create mode 100644 platform/x11es/crash_handler_x11.h + create mode 100644 platform/x11es/detect.py + create mode 100644 platform/x11es/detect_prime.cpp + create mode 100644 platform/x11es/detect_prime.h + create mode 100644 platform/x11es/godot_x11.cpp + create mode 100644 platform/x11es/joypad_linux.cpp + create mode 100644 platform/x11es/joypad_linux.h + create mode 100644 platform/x11es/key_mapping_x11.cpp + create mode 100644 platform/x11es/key_mapping_x11.h + create mode 100644 platform/x11es/libudev-so_wrap.c + create mode 100644 platform/x11es/libudev-so_wrap.h + create mode 100644 platform/x11es/logo.png + create mode 100644 platform/x11es/os_x11.cpp + create mode 100644 platform/x11es/os_x11.h + create mode 100644 platform/x11es/platform_config.h + create mode 100644 platform/x11es/platform_x11_builders.py + create mode 100644 platform/x11es/power_x11.cpp + create mode 100644 platform/x11es/power_x11.h + +diff --git a/core/SCsub b/core/SCsub +index d2109a8d4b..f7b62376ba 100644 +--- a/core/SCsub ++++ b/core/SCsub +@@ -133,7 +133,7 @@ if env["builtin_zstd"]: + "decompress/zstd_decompress_block.c", + "decompress/zstd_decompress.c", + ] +- if env["platform"] in ["android", "iphone", "osx", "server", "x11"]: ++ if env["platform"] in ["android", "iphone", "osx", "server", "x11", "x11es"]: + # Match platforms with ZSTD_ASM_SUPPORTED in common/portability_macros.h + thirdparty_zstd_sources.append("decompress/huf_decompress_amd64.S") + thirdparty_zstd_sources = [thirdparty_zstd_dir + file for file in thirdparty_zstd_sources] +diff --git a/drivers/gles2/rasterizer_canvas_base_gles2.cpp b/drivers/gles2/rasterizer_canvas_base_gles2.cpp +index 8615dc2e64..3abf65732b 100644 +--- a/drivers/gles2/rasterizer_canvas_base_gles2.cpp ++++ b/drivers/gles2/rasterizer_canvas_base_gles2.cpp +@@ -37,6 +37,9 @@ + #include "servers/visual/visual_server_raster.h" + + #ifndef GLES_OVER_GL ++#undef glClearDepthf ++void glClearDepthf (GLfloat d); ++ + #define glClearDepth glClearDepthf + #endif + +diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp +index c8eb0c7509..cbd11af2de 100644 +--- a/drivers/gles2/rasterizer_gles2.cpp ++++ b/drivers/gles2/rasterizer_gles2.cpp +@@ -79,6 +79,9 @@ + #include + #include + #endif ++#if !defined(OPENGL_ENABLED) && defined(X11_ENABLED) ++#include ++#endif + + #if defined(MINGW_ENABLED) || defined(_MSC_VER) + #define strcpy strcpy_s +@@ -246,10 +249,17 @@ void RasterizerGLES2::initialize() { + } + #else + if (OS::get_singleton()->is_stdout_verbose()) { ++#ifdef X11_ENABLED ++ DebugMessageCallbackARB callback = (DebugMessageCallbackARB)glXGetProcAddress((const GLubyte *)"glDebugMessageCallback"); ++ if (!callback) { ++ callback = (DebugMessageCallbackARB)glXGetProcAddress((const GLubyte *)"glDebugMessageCallbackKHR"); ++ } ++#else + DebugMessageCallbackARB callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallback"); + if (!callback) { + callback = (DebugMessageCallbackARB)eglGetProcAddress("glDebugMessageCallbackKHR"); + } ++#endif + + if (callback) { + print_line("godot: ENABLING GL DEBUG"); +diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp +index 497d8d991f..5427df69d8 100644 +--- a/drivers/gles2/rasterizer_storage_gles2.cpp ++++ b/drivers/gles2/rasterizer_storage_gles2.cpp +@@ -89,7 +89,10 @@ GLuint RasterizerStorageGLES2::system_fbo = 0; + #define _DEPTH_COMPONENT24_OES 0x81A6 + + #ifndef GLES_OVER_GL ++#include + #define glClearDepth glClearDepthf ++PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT; ++#define glRenderbufferStorageMultisample glRenderbufferStorageMultisampleEXT + + // enable extensions manually for android and ios + #ifndef UWP_ENABLED +diff --git a/platform/x11es/SCsub b/platform/x11es/SCsub +new file mode 100644 +index 0000000000..3777596c80 +--- /dev/null ++++ b/platform/x11es/SCsub +@@ -0,0 +1,27 @@ ++#!/usr/bin/env python ++ ++Import("env") ++ ++from platform_methods import run_in_subprocess ++import platform_x11_builders ++ ++common_x11 = [ ++ "context_gl_x11.cpp", ++ "crash_handler_x11.cpp", ++ "os_x11.cpp", ++ "key_mapping_x11.cpp", ++ "joypad_linux.cpp", ++ "power_x11.cpp", ++ "detect_prime.cpp", ++] ++ ++if "udev" in env and env["udev"]: ++ common_x11.append("libudev-so_wrap.c") ++ ++#if "speechd" in env and env["speechd"]: ++# common_x11.append(["speechd-so_wrap.c", "tts_linux.cpp"]) ++ ++prog = env.add_program("#bin/godot", ["godot_x11.cpp"] + common_x11) ++ ++if env["debug_symbols"] and env["separate_debug_symbols"]: ++ env.AddPostAction(prog, run_in_subprocess(platform_x11_builders.make_debug_x11)) +diff --git a/platform/x11es/context_gl_x11.cpp b/platform/x11es/context_gl_x11.cpp +new file mode 100644 +index 0000000000..ab5d655408 +--- /dev/null ++++ b/platform/x11es/context_gl_x11.cpp +@@ -0,0 +1,306 @@ ++/**************************************************************************/ ++/* context_gl_x11.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#include "context_gl_x11.h" ++ ++#ifdef X11_ENABLED ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++#include ++#include ++#include ++ ++#define GLX_GLXEXT_PROTOTYPES ++#include ++#include ++ ++#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 ++#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 ++ ++typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); ++ ++struct ContextGL_X11_Private { ++ ::GLXContext glx_context; ++ ::GLXContext glx_context_offscreen; ++}; ++ ++void ContextGL_X11::release_current() { ++ glXMakeCurrent(x11_display, None, nullptr); ++} ++ ++void ContextGL_X11::make_current() { ++ glXMakeCurrent(x11_display, x11_window, p->glx_context); ++} ++ ++bool ContextGL_X11::is_offscreen_available() const { ++ return p->glx_context_offscreen; ++} ++ ++void ContextGL_X11::make_offscreen_current() { ++ glXMakeCurrent(x11_display, x11_window, p->glx_context_offscreen); ++} ++ ++void ContextGL_X11::release_offscreen_current() { ++ glXMakeCurrent(x11_display, None, NULL); ++} ++ ++void ContextGL_X11::swap_buffers() { ++ glXSwapBuffers(x11_display, x11_window); ++} ++ ++static bool ctxErrorOccurred = false; ++static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) { ++ ctxErrorOccurred = true; ++ return 0; ++} ++ ++static void set_class_hint(Display *p_display, Window p_window) { ++ XClassHint *classHint; ++ ++ /* set the name and class hints for the window manager to use */ ++ classHint = XAllocClassHint(); ++ if (classHint) { ++ classHint->res_name = (char *)"Godot_Engine"; ++ classHint->res_class = (char *)"Godot"; ++ } ++ XSetClassHint(p_display, p_window, classHint); ++ XFree(classHint); ++} ++ ++Error ContextGL_X11::initialize() { ++ //const char *extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display)); ++ ++ GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB"); ++ ++ ERR_FAIL_COND_V(!glXCreateContextAttribsARB, ERR_UNCONFIGURED); ++ ++ static int visual_attribs[] = { ++ GLX_RENDER_TYPE, GLX_RGBA_BIT, ++ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, ++ GLX_DOUBLEBUFFER, true, ++ GLX_RED_SIZE, 1, ++ GLX_GREEN_SIZE, 1, ++ GLX_BLUE_SIZE, 1, ++ GLX_DEPTH_SIZE, 24, ++ None ++ }; ++ ++ static int visual_attribs_layered[] = { ++ GLX_RENDER_TYPE, GLX_RGBA_BIT, ++ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, ++ GLX_DOUBLEBUFFER, true, ++ GLX_RED_SIZE, 8, ++ GLX_GREEN_SIZE, 8, ++ GLX_BLUE_SIZE, 8, ++ GLX_ALPHA_SIZE, 8, ++ GLX_DEPTH_SIZE, 24, ++ None ++ }; ++ ++ int fbcount; ++ GLXFBConfig fbconfig = nullptr; ++ XVisualInfo *vi = nullptr; ++ ++ XSetWindowAttributes swa; ++ swa.event_mask = StructureNotifyMask; ++ swa.border_pixel = 0; ++ unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; ++ ++ if (OS::get_singleton()->is_layered_allowed()) { ++ GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs_layered, &fbcount); ++ ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED); ++ ++ for (int i = 0; i < fbcount; i++) { ++ vi = (XVisualInfo *)glXGetVisualFromFBConfig(x11_display, fbc[i]); ++ if (!vi) { ++ continue; ++ } ++ ++ XRenderPictFormat *pict_format = XRenderFindVisualFormat(x11_display, vi->visual); ++ if (!pict_format) { ++ XFree(vi); ++ vi = nullptr; ++ continue; ++ } ++ ++ fbconfig = fbc[i]; ++ if (pict_format->direct.alphaMask > 0) { ++ break; ++ } ++ } ++ XFree(fbc); ++ ERR_FAIL_COND_V(!fbconfig, ERR_UNCONFIGURED); ++ ++ swa.background_pixmap = None; ++ swa.background_pixel = 0; ++ swa.border_pixmap = None; ++ valuemask |= CWBackPixel; ++ ++ } else { ++ GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); ++ ERR_FAIL_COND_V(!fbc, ERR_UNCONFIGURED); ++ ++ vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); ++ ++ fbconfig = fbc[0]; ++ XFree(fbc); ++ } ++ ++ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&ctxErrorHandler); ++ ++ switch (context_type) { ++ case OLDSTYLE: { ++ p->glx_context = glXCreateContext(x11_display, vi, nullptr, GL_TRUE); ++ ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED); ++ } break; ++ case GLES_2_0_COMPATIBLE: { ++ p->glx_context = glXCreateNewContext(x11_display, fbconfig, GLX_RGBA_TYPE, nullptr, true); ++ ERR_FAIL_COND_V(!p->glx_context, ERR_UNCONFIGURED); ++ } break; ++ case GLES_3_0_COMPATIBLE: { ++ static int context_attribs[] = { ++ GLX_CONTEXT_MAJOR_VERSION_ARB, 3, ++ GLX_CONTEXT_MINOR_VERSION_ARB, 3, ++ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, ++ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*|GLX_CONTEXT_DEBUG_BIT_ARB*/, ++ None ++ }; ++ ++ p->glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs); ++ ERR_FAIL_COND_V(ctxErrorOccurred || !p->glx_context, ERR_UNCONFIGURED); ++ p->glx_context_offscreen = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs); ++ } break; ++ } ++ ++ swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); ++ x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); ++ XStoreName(x11_display, x11_window, "Godot Engine"); ++ ++ ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED); ++ set_class_hint(x11_display, x11_window); ++ ++ if (!OS::get_singleton()->is_no_window_mode_enabled()) { ++ XMapWindow(x11_display, x11_window); ++ } ++ ++ XSync(x11_display, False); ++ XSetErrorHandler(oldHandler); ++ ++ glXMakeCurrent(x11_display, x11_window, p->glx_context); ++ ++ XFree(vi); ++ ++ return OK; ++} ++ ++int ContextGL_X11::get_window_width() { ++ XWindowAttributes xwa; ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ ++ return xwa.width; ++} ++ ++int ContextGL_X11::get_window_height() { ++ XWindowAttributes xwa; ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ ++ return xwa.height; ++} ++ ++void *ContextGL_X11::get_glx_context() { ++ if (p != nullptr) { ++ return p->glx_context; ++ } else { ++ return nullptr; ++ } ++} ++ ++void ContextGL_X11::set_use_vsync(bool p_use) { ++ static bool setup = false; ++ static PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = nullptr; ++ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalMESA = nullptr; ++ static PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI = nullptr; ++ ++ if (!setup) { ++ setup = true; ++ String extensions = glXQueryExtensionsString(x11_display, DefaultScreen(x11_display)); ++ if (extensions.find("GLX_EXT_swap_control") != -1) { ++ glXSwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalEXT"); ++ } ++ if (extensions.find("GLX_MESA_swap_control") != -1) { ++ glXSwapIntervalMESA = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalMESA"); ++ } ++ if (extensions.find("GLX_SGI_swap_control") != -1) { ++ glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC)glXGetProcAddressARB((const GLubyte *)"glXSwapIntervalSGI"); ++ } ++ } ++ int val = p_use ? 1 : 0; ++ if (glXSwapIntervalMESA) { ++ glXSwapIntervalMESA(val); ++ } else if (glXSwapIntervalSGI) { ++ glXSwapIntervalSGI(val); ++ } else if (glXSwapIntervalEXT) { ++ GLXDrawable drawable = glXGetCurrentDrawable(); ++ glXSwapIntervalEXT(x11_display, drawable, val); ++ } else { ++ return; ++ } ++ use_vsync = p_use; ++} ++bool ContextGL_X11::is_using_vsync() const { ++ return use_vsync; ++} ++ ++ContextGL_X11::ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type) : ++ x11_window(p_x11_window) { ++ default_video_mode = p_default_video_mode; ++ x11_display = p_x11_display; ++ ++ context_type = p_context_type; ++ ++ double_buffer = false; ++ direct_render = false; ++ glx_minor = glx_major = 0; ++ p = memnew(ContextGL_X11_Private); ++ p->glx_context = nullptr; ++ p->glx_context_offscreen = nullptr; ++ use_vsync = false; ++} ++ ++ContextGL_X11::~ContextGL_X11() { ++ release_current(); ++ glXDestroyContext(x11_display, p->glx_context); ++ if (p->glx_context_offscreen) { ++ glXDestroyContext(x11_display, p->glx_context_offscreen); ++ } ++ memdelete(p); ++} ++ ++#endif ++#endif +diff --git a/platform/x11es/context_gl_x11.h b/platform/x11es/context_gl_x11.h +new file mode 100644 +index 0000000000..7ac2d2d97c +--- /dev/null ++++ b/platform/x11es/context_gl_x11.h +@@ -0,0 +1,89 @@ ++/**************************************************************************/ ++/* context_gl_x11.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifndef CONTEXT_GL_X11_H ++#define CONTEXT_GL_X11_H ++ ++#ifdef X11_ENABLED ++ ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ ++#include "core/os/os.h" ++#include ++#include ++ ++struct ContextGL_X11_Private; ++ ++class ContextGL_X11 { ++public: ++ enum ContextType { ++ OLDSTYLE, ++ GLES_2_0_COMPATIBLE, ++ GLES_3_0_COMPATIBLE ++ }; ++ ++private: ++ ContextGL_X11_Private *p; ++ OS::VideoMode default_video_mode; ++ //::Colormap x11_colormap; ++ ::Display *x11_display; ++ ::Window &x11_window; ++ bool double_buffer; ++ bool direct_render; ++ int glx_minor, glx_major; ++ bool use_vsync; ++ ContextType context_type; ++ ++public: ++ void release_current(); ++ void make_current(); ++ void swap_buffers(); ++ int get_window_width(); ++ int get_window_height(); ++ void *get_glx_context(); ++ ++ bool is_offscreen_available() const; ++ void make_offscreen_current(); ++ void release_offscreen_current(); ++ ++ Error initialize(); ++ ++ void set_use_vsync(bool p_use); ++ bool is_using_vsync() const; ++ ++ ContextGL_X11(::Display *p_x11_display, ::Window &p_x11_window, const OS::VideoMode &p_default_video_mode, ContextType p_context_type); ++ ~ContextGL_X11(); ++}; ++ ++#endif ++ ++#endif ++ ++#endif // CONTEXT_GL_X11_H +diff --git a/platform/x11es/crash_handler_x11.cpp b/platform/x11es/crash_handler_x11.cpp +new file mode 100644 +index 0000000000..804bd4b229 +--- /dev/null ++++ b/platform/x11es/crash_handler_x11.cpp +@@ -0,0 +1,173 @@ ++/**************************************************************************/ ++/* crash_handler_x11.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#include "crash_handler_x11.h" ++ ++#include "core/os/os.h" ++#include "core/print_string.h" ++#include "core/project_settings.h" ++#include "core/version.h" ++#include "main/main.h" ++ ++#ifdef DEBUG_ENABLED ++#define CRASH_HANDLER_ENABLED 1 ++#endif ++ ++#ifdef CRASH_HANDLER_ENABLED ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static void handle_crash(int sig) { ++ if (OS::get_singleton() == nullptr) { ++ abort(); ++ } ++ ++ void *bt_buffer[256]; ++ size_t size = backtrace(bt_buffer, 256); ++ String _execpath = OS::get_singleton()->get_executable_path(); ++ ++ String msg; ++ const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); ++ if (proj_settings) { ++ msg = proj_settings->get("debug/settings/crash_handler/message"); ++ } ++ ++ // Tell MainLoop about the crash. This can be handled by users too in Node. ++ if (OS::get_singleton()->get_main_loop()) { ++ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); ++ } ++ ++ // Dump the backtrace to stderr with a message to the user ++ print_error("\n================================================================"); ++ print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, sig)); ++ ++ // Print the engine version just before, so that people are reminded to include the version in backtrace reports. ++ if (String(VERSION_HASH).empty()) { ++ print_error(vformat("Engine version: %s", VERSION_FULL_NAME)); ++ } else { ++ print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, VERSION_HASH)); ++ } ++ print_error(vformat("Dumping the backtrace. %s", msg)); ++ char **strings = backtrace_symbols(bt_buffer, size); ++ // PIE executable relocation, zero for non-PIE executables ++#ifdef __GLIBC__ ++ // This is a glibc only thing apparently. ++ uintptr_t relocation = _r_debug.r_map->l_addr; ++#else ++ // Non glibc systems apparently don't give PIE relocation info. ++ uintptr_t relocation = 0; ++#endif //__GLIBC__ ++ if (strings) { ++ List args; ++ for (size_t i = 0; i < size; i++) { ++ char str[1024]; ++ snprintf(str, 1024, "%p", (void *)((uintptr_t)bt_buffer[i] - relocation)); ++ args.push_back(str); ++ } ++ args.push_back("-e"); ++ args.push_back(_execpath); ++ ++ // Try to get the file/line number using addr2line ++ int ret; ++ String output = ""; ++ Error err = OS::get_singleton()->execute(String("addr2line"), args, true, nullptr, &output, &ret); ++ Vector addr2line_results; ++ if (err == OK) { ++ addr2line_results = output.substr(0, output.length() - 1).split("\n", false); ++ } ++ ++ for (size_t i = 1; i < size; i++) { ++ char fname[1024]; ++ Dl_info info; ++ ++ snprintf(fname, 1024, "%s", strings[i]); ++ ++ // Try to demangle the function name to provide a more readable one ++ if (dladdr(bt_buffer[i], &info) && info.dli_sname) { ++ if (info.dli_sname[0] == '_') { ++ int status; ++ char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); ++ ++ if (status == 0 && demangled) { ++ snprintf(fname, 1024, "%s", demangled); ++ } ++ ++ if (demangled) { ++ free(demangled); ++ } ++ } ++ } ++ ++ print_error(vformat("[%d] %s (%s)", (int64_t)i, fname, err == OK ? addr2line_results[i] : "")); ++ } ++ ++ free(strings); ++ } ++ print_error("-- END OF BACKTRACE --"); ++ print_error("================================================================"); ++ ++ // Abort to pass the error to the OS ++ abort(); ++} ++#endif ++ ++CrashHandler::CrashHandler() { ++ disabled = false; ++} ++ ++CrashHandler::~CrashHandler() { ++ disable(); ++} ++ ++void CrashHandler::disable() { ++ if (disabled) { ++ return; ++ } ++ ++#ifdef CRASH_HANDLER_ENABLED ++ signal(SIGSEGV, nullptr); ++ signal(SIGFPE, nullptr); ++ signal(SIGILL, nullptr); ++#endif ++ ++ disabled = true; ++} ++ ++void CrashHandler::initialize() { ++#ifdef CRASH_HANDLER_ENABLED ++ signal(SIGSEGV, handle_crash); ++ signal(SIGFPE, handle_crash); ++ signal(SIGILL, handle_crash); ++#endif ++} +diff --git a/platform/x11es/crash_handler_x11.h b/platform/x11es/crash_handler_x11.h +new file mode 100644 +index 0000000000..8898b53b75 +--- /dev/null ++++ b/platform/x11es/crash_handler_x11.h +@@ -0,0 +1,47 @@ ++/**************************************************************************/ ++/* crash_handler_x11.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifndef CRASH_HANDLER_X11_H ++#define CRASH_HANDLER_X11_H ++ ++class CrashHandler { ++ bool disabled; ++ ++public: ++ void initialize(); ++ ++ void disable(); ++ bool is_disabled() const { return disabled; }; ++ ++ CrashHandler(); ++ ~CrashHandler(); ++}; ++ ++#endif // CRASH_HANDLER_X11_H +diff --git a/platform/x11es/detect.py b/platform/x11es/detect.py +new file mode 100644 +index 0000000000..ea55017240 +--- /dev/null ++++ b/platform/x11es/detect.py +@@ -0,0 +1,433 @@ ++import os ++import platform ++import sys ++from methods import get_compiler_version, using_gcc, using_clang ++ ++ ++def is_active(): ++ return True ++ ++ ++def get_name(): ++ return "X11" ++ ++ ++def can_build(): ++ if os.name != "posix" or sys.platform == "darwin": ++ return False ++ ++ # Check the minimal dependencies ++ x11_error = os.system("pkg-config --version > /dev/null") ++ if x11_error: ++ print("Error: pkg-config not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config x11 --modversion > /dev/null") ++ if x11_error: ++ print("Error: X11 libraries not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xcursor --modversion > /dev/null") ++ if x11_error: ++ print("Error: Xcursor library not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xinerama --modversion > /dev/null") ++ if x11_error: ++ print("Error: Xinerama library not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xext --modversion > /dev/null") ++ if x11_error: ++ print("Error: Xext library not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xrandr --modversion > /dev/null") ++ if x11_error: ++ print("Error: XrandR library not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xrender --modversion > /dev/null") ++ if x11_error: ++ print("Error: XRender library not found. Aborting.") ++ return False ++ ++ x11_error = os.system("pkg-config xi --modversion > /dev/null") ++ if x11_error: ++ print("Error: Xi library not found. Aborting.") ++ return False ++ ++ return True ++ ++ ++def get_opts(): ++ from SCons.Variables import BoolVariable, EnumVariable ++ ++ return [ ++ EnumVariable("linker", "Linker program", "default", ("default", "bfd", "gold", "lld", "mold")), ++ BoolVariable("use_llvm", "Use the LLVM compiler", False), ++ BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", True), ++ BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), ++ BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False), ++ BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False), ++ BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False), ++ BoolVariable("use_msan", "Use LLVM/GCC compiler memory sanitizer (MSAN))", False), ++ BoolVariable("pulseaudio", "Detect and use PulseAudio", True), ++# BoolVariable("speechd", "Detect and use Speech Dispatcher for Text-to-Speech support", True), ++ BoolVariable("udev", "Use udev for gamepad connection callbacks", True), ++ BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), ++ BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), ++ BoolVariable("touch", "Enable touch events", True), ++ BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False), ++ ] ++ ++ ++def get_flags(): ++ return [] ++ ++ ++def configure(env): ++ ## Build type ++ ++ if env["target"] == "release": ++ if env["optimize"] == "speed": # optimize for speed (default) ++ env.Prepend(CCFLAGS=["-O3"]) ++ elif env["optimize"] == "size": # optimize for size ++ env.Prepend(CCFLAGS=["-Os"]) ++ ++ if env["debug_symbols"]: ++ env.Prepend(CCFLAGS=["-g2"]) ++ ++ elif env["target"] == "release_debug": ++ if env["optimize"] == "speed": # optimize for speed (default) ++ env.Prepend(CCFLAGS=["-O2"]) ++ elif env["optimize"] == "size": # optimize for size ++ env.Prepend(CCFLAGS=["-Os"]) ++ ++ if env["debug_symbols"]: ++ env.Prepend(CCFLAGS=["-g2"]) ++ ++ elif env["target"] == "debug": ++ env.Prepend(CCFLAGS=["-ggdb"]) ++ env.Prepend(CCFLAGS=["-g3"]) ++ env.Append(LINKFLAGS=["-rdynamic"]) ++ ++ if env["debug_symbols"]: ++ # Adding dwarf-4 explicitly makes stacktraces work with clang builds, ++ # otherwise addr2line doesn't understand them ++ env.Append(CCFLAGS=["-gdwarf-4"]) ++ ++ ## Architecture ++ ++ # Cross-compilation ++ # TODO: Support cross-compilation on architectures other than x86. ++ host_is_64_bit = sys.maxsize > 2**32 ++ if env["bits"] == "default": ++ env["bits"] = "64" if host_is_64_bit else "32" ++ if host_is_64_bit and (env["bits"] == "32" or env["arch"] == "x86"): ++ env.Append(CCFLAGS=["-m32"]) ++ env.Append(LINKFLAGS=["-m32"]) ++ elif not host_is_64_bit and (env["bits"] == "64" or env["arch"] == "x86_64"): ++ env.Append(CCFLAGS=["-m64"]) ++ env.Append(LINKFLAGS=["-m64"]) ++ ++ machines = { ++ "riscv64": "rv64", ++ "ppc64le": "ppc64", ++ "ppc64": "ppc64", ++ "ppcle": "ppc", ++ "ppc": "ppc", ++ } ++ ++ if env["arch"] == "" and platform.machine() in machines: ++ env["arch"] = machines[platform.machine()] ++ ++ if env["arch"] == "rv64": ++ # G = General-purpose extensions, C = Compression extension (very common). ++ env.Append(CCFLAGS=["-march=rv64gc"]) ++ ++ ## Compiler configuration ++ ++ if "CXX" in env and "clang" in os.path.basename(env["CXX"]): ++ # Convenience check to enforce the use_llvm overrides when CXX is clang(++) ++ env["use_llvm"] = True ++ ++ if env["use_llvm"]: ++ if "clang++" not in os.path.basename(env["CXX"]): ++ env["CC"] = "clang" ++ env["CXX"] = "clang++" ++ env.extra_suffix = ".llvm" + env.extra_suffix ++ ++ # Linker ++ ++ if env["linker"] != "default": ++ print("Using linker program: " + env["linker"]) ++ if env["linker"] == "mold" and using_gcc(env): # GCC < 12.1 doesn't support -fuse-ld=mold. ++ cc_semver = tuple(get_compiler_version(env)) ++ if cc_semver < (12, 1): ++ found_wrapper = False ++ for path in ["/usr/libexec", "/usr/local/libexec", "/usr/lib", "/usr/local/lib"]: ++ if os.path.isfile(path + "/mold/ld"): ++ env.Append(LINKFLAGS=["-B" + path + "/mold"]) ++ found_wrapper = True ++ break ++ if not found_wrapper: ++ print("Couldn't locate mold installation path. Make sure it's installed in /usr or /usr/local.") ++ sys.exit(255) ++ else: ++ env.Append(LINKFLAGS=["-fuse-ld=mold"]) ++ else: ++ env.Append(LINKFLAGS=["-fuse-ld=%s" % env["linker"]]) ++ ++ # Sanitizers ++ if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"] or env["use_msan"]: ++ env.extra_suffix += "s" ++ ++ if env["use_ubsan"]: ++ env.Append( ++ CCFLAGS=[ ++ "-fsanitize=undefined,shift,shift-exponent,integer-divide-by-zero,unreachable,vla-bound,null,return,signed-integer-overflow,bounds,float-divide-by-zero,float-cast-overflow,nonnull-attribute,returns-nonnull-attribute,bool,enum,vptr,pointer-overflow,builtin" ++ ] ++ ) ++ ++ if env["use_llvm"]: ++ env.Append( ++ CCFLAGS=[ ++ "-fsanitize=nullability-return,nullability-arg,function,nullability-assign,implicit-integer-sign-change,implicit-signed-integer-truncation,implicit-unsigned-integer-truncation" ++ ] ++ ) ++ else: ++ env.Append(CCFLAGS=["-fsanitize=bounds-strict"]) ++ env.Append(LINKFLAGS=["-fsanitize=undefined"]) ++ ++ if env["use_asan"]: ++ env.Append(CCFLAGS=["-fsanitize=address,pointer-subtract,pointer-compare"]) ++ env.Append(LINKFLAGS=["-fsanitize=address"]) ++ ++ if env["use_lsan"]: ++ env.Append(CCFLAGS=["-fsanitize=leak"]) ++ env.Append(LINKFLAGS=["-fsanitize=leak"]) ++ ++ if env["use_tsan"]: ++ env.Append(CCFLAGS=["-fsanitize=thread"]) ++ env.Append(LINKFLAGS=["-fsanitize=thread"]) ++ ++ if env["use_msan"]: ++ env.Append(CCFLAGS=["-fsanitize=memory"]) ++ env.Append(LINKFLAGS=["-fsanitize=memory"]) ++ ++ # LTO ++ ++ if env["lto"] == "auto": # Full LTO for production. ++ env["lto"] = "full" ++ ++ if env["lto"] != "none": ++ if env["lto"] == "thin": ++ if not env["use_llvm"]: ++ print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.") ++ sys.exit(255) ++ env.Append(CCFLAGS=["-flto=thin"]) ++ env.Append(LINKFLAGS=["-flto=thin"]) ++ elif not env["use_llvm"] and env.GetOption("num_jobs") > 1: ++ env.Append(CCFLAGS=["-flto"]) ++ env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))]) ++ else: ++ env.Append(CCFLAGS=["-flto"]) ++ env.Append(LINKFLAGS=["-flto"]) ++ ++ if not env["use_llvm"]: ++ env["RANLIB"] = "gcc-ranlib" ++ env["AR"] = "gcc-ar" ++ ++ env.Append(CCFLAGS=["-pipe"]) ++ ++ # Check for gcc version >= 6 before adding -no-pie ++ version = get_compiler_version(env) or [-1, -1] ++ if using_gcc(env): ++ if version[0] >= 6: ++ env.Append(CCFLAGS=["-fpie"]) ++ env.Append(LINKFLAGS=["-no-pie"]) ++ # Do the same for clang should be fine with Clang 4 and higher ++ if using_clang(env): ++ if version[0] >= 4: ++ env.Append(CCFLAGS=["-fpie"]) ++ env.Append(LINKFLAGS=["-no-pie"]) ++ ++ ## Dependencies ++ ++ env.ParseConfig("pkg-config x11 --cflags --libs") ++ env.ParseConfig("pkg-config xcursor --cflags --libs") ++ env.ParseConfig("pkg-config xinerama --cflags --libs") ++ env.ParseConfig("pkg-config xext --cflags --libs") ++ env.ParseConfig("pkg-config xrandr --cflags --libs") ++ env.ParseConfig("pkg-config xrender --cflags --libs") ++ env.ParseConfig("pkg-config xi --cflags --libs") ++ ++ if env["touch"]: ++ env.Append(CPPDEFINES=["TOUCH_ENABLED"]) ++ ++ # FIXME: Check for existence of the libs before parsing their flags with pkg-config ++ ++ # freetype depends on libpng and zlib, so bundling one of them while keeping others ++ # as shared libraries leads to weird issues ++ if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]: ++ env["builtin_freetype"] = True ++ env["builtin_libpng"] = True ++ env["builtin_zlib"] = True ++ ++ if not env["builtin_freetype"]: ++ env.ParseConfig("pkg-config freetype2 --cflags --libs") ++ ++ if not env["builtin_libpng"]: ++ env.ParseConfig("pkg-config libpng16 --cflags --libs") ++ ++ if not env["builtin_bullet"]: ++ # We need at least version 2.90 ++ min_bullet_version = "2.90" ++ ++ import subprocess ++ ++ bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip() ++ if str(bullet_version) < min_bullet_version: ++ # Abort as system bullet was requested but too old ++ print( ++ "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format( ++ bullet_version, min_bullet_version ++ ) ++ ) ++ sys.exit(255) ++ env.ParseConfig("pkg-config bullet --cflags --libs") ++ ++ if False: # not env['builtin_assimp']: ++ # FIXME: Add min version check ++ env.ParseConfig("pkg-config assimp --cflags --libs") ++ ++ if not env["builtin_enet"]: ++ env.ParseConfig("pkg-config libenet --cflags --libs") ++ ++ if not env["builtin_squish"]: ++ env.ParseConfig("pkg-config libsquish --cflags --libs") ++ ++ if not env["builtin_zstd"]: ++ env.ParseConfig("pkg-config libzstd --cflags --libs") ++ ++ # Sound and video libraries ++ # Keep the order as it triggers chained dependencies (ogg needed by others, etc.) ++ ++ if not env["builtin_libtheora"]: ++ env["builtin_libogg"] = False # Needed to link against system libtheora ++ env["builtin_libvorbis"] = False # Needed to link against system libtheora ++ env.ParseConfig("pkg-config theora theoradec --cflags --libs") ++ else: ++ list_of_x86 = ["x86_64", "x86", "i386", "i586"] ++ if (env["arch"].startswith("x86") or env["arch"] == "") and any(platform.machine() in s for s in list_of_x86): ++ env["x86_libtheora_opt_gcc"] = True ++ ++ if not env["builtin_libvpx"]: ++ env.ParseConfig("pkg-config vpx --cflags --libs") ++ ++ if not env["builtin_libvorbis"]: ++ env["builtin_libogg"] = False # Needed to link against system libvorbis ++ env.ParseConfig("pkg-config vorbis vorbisfile --cflags --libs") ++ ++ if not env["builtin_opus"]: ++ env["builtin_libogg"] = False # Needed to link against system opus ++ env.ParseConfig("pkg-config opus opusfile --cflags --libs") ++ ++ if not env["builtin_libogg"]: ++ env.ParseConfig("pkg-config ogg --cflags --libs") ++ ++ if not env["builtin_libwebp"]: ++ env.ParseConfig("pkg-config libwebp --cflags --libs") ++ ++ if not env["builtin_mbedtls"]: ++ # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228 ++ env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"]) ++ ++ if not env["builtin_wslay"]: ++ env.ParseConfig("pkg-config libwslay --cflags --libs") ++ ++ if not env["builtin_miniupnpc"]: ++ # No pkgconfig file so far, hardcode default paths. ++ env.Prepend(CPPPATH=["/usr/include/miniupnpc"]) ++ env.Append(LIBS=["miniupnpc"]) ++ ++ # On Linux wchar_t should be 32-bits ++ # 16-bit library shouldn't be required due to compiler optimisations ++ if not env["builtin_pcre2"]: ++ env.ParseConfig("pkg-config libpcre2-32 --cflags --libs") ++ ++ # Embree is only used in tools build on x86_64 and aarch64. ++ if env["tools"] and not env["builtin_embree"] and host_is_64_bit: ++ # No pkgconfig file so far, hardcode expected lib name. ++ env.Append(LIBS=["embree3"]) ++ ++ ## Flags ++ ++ if os.system("pkg-config --exists alsa") == 0: # 0 means found ++ env["alsa"] = True ++ env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"]) ++ env.ParseConfig("pkg-config alsa --cflags") # Only cflags, we dlopen the library. ++ else: ++ print("Warning: ALSA libraries not found. Disabling the ALSA audio driver.") ++ ++ if env["pulseaudio"]: ++ if os.system("pkg-config --exists libpulse") == 0: # 0 means found ++ env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"]) ++ env.ParseConfig("pkg-config libpulse --cflags") # Only cflags, we dlopen the library. ++ else: ++ env["pulseaudio"] = False ++ print("Warning: PulseAudio development libraries not found. Disabling the PulseAudio audio driver.") ++ ++# if env["speechd"]: ++# if os.system("pkg-config --exists speech-dispatcher") == 0: # 0 means found ++# env.Append(CPPDEFINES=["SPEECHD_ENABLED"]) ++# env.ParseConfig("pkg-config speech-dispatcher --cflags") # Only cflags, we dlopen the library. ++# else: ++# env["speechd"] = False ++# print("Warning: Speech Dispatcher development libraries not found. Disabling Text-to-Speech support.") ++ ++ if platform.system() == "Linux": ++ env.Append(CPPDEFINES=["JOYDEV_ENABLED"]) ++ if env["udev"]: ++ if os.system("pkg-config --exists libudev") == 0: # 0 means found ++ env.Append(CPPDEFINES=["UDEV_ENABLED"]) ++ env.ParseConfig("pkg-config libudev --cflags") # Only cflags, we dlopen the library. ++ else: ++ env["udev"] = False ++ print("Warning: libudev development libraries not found. Disabling controller hotplugging support.") ++ else: ++ env["udev"] = False # Linux specific ++ ++ # Linkflags below this line should typically stay the last ones ++ if not env["builtin_zlib"]: ++ env.ParseConfig("pkg-config zlib --cflags --libs") ++ ++ env.Prepend(CPPPATH=["#platform/x11es"]) ++ env.Append(CPPDEFINES=["X11_ENABLED", "UNIX_ENABLED", "GLES_ENABLED", ("_FILE_OFFSET_BITS", 64)]) ++ ++ env.ParseConfig("pkg-config glesv2 --cflags --libs") ++ env.ParseConfig("pkg-config glx --cflags --libs") ++ ++ env.Append(LIBS=["pthread"]) ++ ++ if platform.system() == "Linux": ++ env.Append(LIBS=["dl"]) ++ ++ if not env["execinfo"] and platform.libc_ver()[0] != "glibc": ++ # The default crash handler depends on glibc, so if the host uses ++ # a different libc (BSD libc, musl), fall back to libexecinfo. ++ print("Note: Using `execinfo=yes` for the crash handler as required on platforms where glibc is missing.") ++ env["execinfo"] = True ++ ++ if env["execinfo"]: ++ env.Append(LIBS=["execinfo"]) ++ ++ # Link those statically for portability ++ if env["use_static_cpp"]: ++ env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) ++ if env["use_llvm"] and platform.system() != "FreeBSD": ++ env["LINKCOM"] = env["LINKCOM"] + " -l:libatomic.a" ++ else: ++ if env["use_llvm"] and platform.system() != "FreeBSD": ++ env.Append(LIBS=["atomic"]) +diff --git a/platform/x11es/detect_prime.cpp b/platform/x11es/detect_prime.cpp +new file mode 100644 +index 0000000000..49dfd207cc +--- /dev/null ++++ b/platform/x11es/detect_prime.cpp +@@ -0,0 +1,246 @@ ++/**************************************************************************/ ++/* detect_prime.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifdef X11_ENABLED ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ ++#include "detect_prime.h" ++ ++#include "core/print_string.h" ++#include "core/ustring.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 ++#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 ++ ++typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLXContext, Bool, const int *); ++ ++struct vendor { ++ const char *glxvendor; ++ int priority = 0; ++}; ++ ++vendor vendormap[] = { ++ { "Advanced Micro Devices, Inc.", 30 }, ++ { "AMD", 30 }, ++ { "NVIDIA Corporation", 30 }, ++ { "X.Org", 30 }, ++ { "Intel Open Source Technology Center", 20 }, ++ { "Intel", 20 }, ++ { "nouveau", 10 }, ++ { "Mesa Project", 0 }, ++ { nullptr, 0 } ++}; ++ ++// Runs inside a child. Exiting will not quit the engine. ++void create_context() { ++ Display *x11_display = XOpenDisplay(nullptr); ++ Window x11_window; ++ GLXContext glx_context; ++ ++ GLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (GLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB"); ++ ++ static int visual_attribs[] = { ++ GLX_RENDER_TYPE, GLX_RGBA_BIT, ++ GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, ++ GLX_DOUBLEBUFFER, true, ++ GLX_RED_SIZE, 1, ++ GLX_GREEN_SIZE, 1, ++ GLX_BLUE_SIZE, 1, ++ GLX_DEPTH_SIZE, 24, ++ None ++ }; ++ ++ int fbcount; ++ GLXFBConfig fbconfig = nullptr; ++ XVisualInfo *vi = nullptr; ++ ++ XSetWindowAttributes swa; ++ swa.event_mask = StructureNotifyMask; ++ swa.border_pixel = 0; ++ unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; ++ ++ GLXFBConfig *fbc = glXChooseFBConfig(x11_display, DefaultScreen(x11_display), visual_attribs, &fbcount); ++ if (!fbc) { ++ exit(1); ++ } ++ ++ vi = glXGetVisualFromFBConfig(x11_display, fbc[0]); ++ ++ fbconfig = fbc[0]; ++ ++ static int context_attribs[] = { ++ GLX_CONTEXT_MAJOR_VERSION_ARB, 3, ++ GLX_CONTEXT_MINOR_VERSION_ARB, 3, ++ GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, ++ GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, ++ None ++ }; ++ ++ glx_context = glXCreateContextAttribsARB(x11_display, fbconfig, nullptr, true, context_attribs); ++ ++ swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); ++ x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, 10, 10, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); ++ ++ if (!x11_window) { ++ exit(1); ++ } ++ ++ glXMakeCurrent(x11_display, x11_window, glx_context); ++ XFree(vi); ++} ++ ++int detect_prime() { ++ pid_t p; ++ int priorities[2] = {}; ++ String vendors[2]; ++ String renderers[2]; ++ ++ vendors[0] = "Unknown"; ++ vendors[1] = "Unknown"; ++ renderers[0] = "Unknown"; ++ renderers[1] = "Unknown"; ++ ++ for (int i = 0; i < 2; ++i) { ++ int fdset[2]; ++ ++ if (pipe(fdset) == -1) { ++ print_verbose("Failed to pipe(), using default GPU"); ++ return 0; ++ } ++ ++ // Fork so the driver initialization can crash without taking down the engine. ++ p = fork(); ++ ++ if (p > 0) { ++ // Main thread ++ ++ int stat_loc = 0; ++ char string[201]; ++ string[200] = '\0'; ++ ++ close(fdset[1]); ++ ++ waitpid(p, &stat_loc, 0); ++ ++ if (!stat_loc) { ++ // No need to do anything complicated here. Anything less than ++ // PIPE_BUF will be delivered in one read() call. ++ // Leave it 'Unknown' otherwise. ++ if (read(fdset[0], string, sizeof(string) - 1) > 0) { ++ vendors[i] = string; ++ renderers[i] = string + strlen(string) + 1; ++ } ++ } ++ ++ close(fdset[0]); ++ ++ } else { ++ // In child, exit() here will not quit the engine. ++ ++ char string[201]; ++ ++ close(fdset[0]); ++ ++ if (i) { ++ setenv("DRI_PRIME", "1", 1); ++ } ++ create_context(); ++ ++ const char *vendor = (const char *)glGetString(GL_VENDOR); ++ const char *renderer = (const char *)glGetString(GL_RENDERER); ++ ++ unsigned int vendor_len = strlen(vendor) + 1; ++ unsigned int renderer_len = strlen(renderer) + 1; ++ ++ if (vendor_len + renderer_len >= sizeof(string)) { ++ renderer_len = 200 - vendor_len; ++ } ++ ++ memcpy(&string, vendor, vendor_len); ++ memcpy(&string[vendor_len], renderer, renderer_len); ++ ++ if (write(fdset[1], string, vendor_len + renderer_len) == -1) { ++ print_verbose("Couldn't write vendor/renderer string."); ++ } ++ close(fdset[1]); ++ ++ // The function quick_exit() is used because exit() will call destructors on static objects copied by fork(). ++ // These objects will be freed anyway when the process finishes execution. ++ quick_exit(0); ++ } ++ } ++ ++ int preferred = 0; ++ int priority = 0; ++ ++ if (vendors[0] == vendors[1]) { ++ print_verbose("Only one GPU found, using default."); ++ return 0; ++ } ++ ++ for (int i = 1; i >= 0; --i) { ++ vendor *v = vendormap; ++ while (v->glxvendor) { ++ if (v->glxvendor == vendors[i]) { ++ priorities[i] = v->priority; ++ ++ if (v->priority >= priority) { ++ priority = v->priority; ++ preferred = i; ++ } ++ } ++ ++v; ++ } ++ } ++ ++ print_verbose("Found renderers:"); ++ for (int i = 0; i < 2; ++i) { ++ print_verbose("Renderer " + itos(i) + ": " + renderers[i] + " with priority: " + itos(priorities[i])); ++ } ++ ++ print_verbose("Using renderer: " + renderers[preferred]); ++ return preferred; ++} ++ ++#endif ++#endif +diff --git a/platform/x11es/detect_prime.h b/platform/x11es/detect_prime.h +new file mode 100644 +index 0000000000..931d1d7d24 +--- /dev/null ++++ b/platform/x11es/detect_prime.h +@@ -0,0 +1,38 @@ ++/**************************************************************************/ ++/* detect_prime.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifdef X11_ENABLED ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ ++int detect_prime(); ++ ++#endif ++ ++#endif // DETECT_PRIME_H +diff --git a/platform/x11es/godot_x11.cpp b/platform/x11es/godot_x11.cpp +new file mode 100644 +index 0000000000..62cffd0a03 +--- /dev/null ++++ b/platform/x11es/godot_x11.cpp +@@ -0,0 +1,83 @@ ++/**************************************************************************/ ++/* godot_x11.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#include ++#include ++#include ++#include ++ ++#include "main/main.h" ++#include "os_x11.h" ++ ++// For export templates, add a section; the exporter will patch it to enclose ++// the data appended to the executable (bundled PCK). ++#if !defined(TOOLS_ENABLED) && defined(__GNUC__) ++static const char dummy[8] __attribute__((section("pck"), used)) = { 0 }; ++ ++// Dummy function to prevent LTO from discarding "pck" section. ++extern "C" const char *pck_section_dummy_call() __attribute__((used)); ++extern "C" const char *pck_section_dummy_call() { ++ return &dummy[0]; ++} ++#endif ++ ++int main(int argc, char *argv[]) { ++ OS_X11 os; ++ ++ setlocale(LC_CTYPE, ""); ++ ++ char *cwd = (char *)malloc(PATH_MAX); ++ ERR_FAIL_COND_V(!cwd, ERR_OUT_OF_MEMORY); ++ char *ret = getcwd(cwd, PATH_MAX); ++ ++ Error err = Main::setup(argv[0], argc - 1, &argv[1]); ++ if (err != OK) { ++ free(cwd); ++ ++ if (err == ERR_HELP) { // Returned by --help and --version, so success. ++ return 0; ++ } ++ return 255; ++ } ++ ++ if (Main::start()) { ++ os.run(); // it is actually the OS that decides how to run ++ } ++ Main::cleanup(); ++ ++ if (ret) { // Previous getcwd was successful ++ if (chdir(cwd) != 0) { ++ ERR_PRINT("Couldn't return to previous working directory."); ++ } ++ } ++ free(cwd); ++ ++ return os.get_exit_code(); ++} +diff --git a/platform/x11es/joypad_linux.cpp b/platform/x11es/joypad_linux.cpp +new file mode 100644 +index 0000000000..9f4750a935 +--- /dev/null ++++ b/platform/x11es/joypad_linux.cpp +@@ -0,0 +1,595 @@ ++/**************************************************************************/ ++/* joypad_linux.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifdef JOYDEV_ENABLED ++ ++#include "joypad_linux.h" ++ ++#include "core/os/os.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef UDEV_ENABLED ++#include "libudev-so_wrap.h" ++#endif ++ ++#define LONG_BITS (sizeof(long) * 8) ++#define test_bit(nr, addr) (((1UL << ((nr) % LONG_BITS)) & ((addr)[(nr) / LONG_BITS])) != 0) ++#define NBITS(x) ((((x)-1) / LONG_BITS) + 1) ++ ++#ifdef UDEV_ENABLED ++static const char *ignore_str = "/dev/input/js"; ++#endif ++ ++JoypadLinux::Joypad::Joypad() { ++ fd = -1; ++ dpad = 0; ++ devpath = ""; ++ for (int i = 0; i < MAX_ABS; i++) { ++ abs_info[i] = nullptr; ++ } ++} ++ ++JoypadLinux::Joypad::~Joypad() { ++ for (int i = 0; i < MAX_ABS; i++) { ++ if (abs_info[i]) { ++ memdelete(abs_info[i]); ++ } ++ } ++} ++ ++void JoypadLinux::Joypad::reset() { ++ dpad = 0; ++ fd = -1; ++ for (int i = 0; i < MAX_ABS; i++) { ++ abs_map[i] = -1; ++ curr_axis[i] = 0; ++ } ++ events.clear(); ++} ++ ++#ifdef UDEV_ENABLED ++// This function is derived from SDL: ++// https://github.com/libsdl-org/SDL/blob/main/src/core/linux/SDL_sandbox.c#L28-L45 ++static bool detect_sandbox() { ++ if (access("/.flatpak-info", F_OK) == 0) { ++ return true; ++ } ++ ++ // For Snap, we check multiple variables because they might be set for ++ // unrelated reasons. This is the same thing WebKitGTK does. ++ if (OS::get_singleton()->has_environment("SNAP") && OS::get_singleton()->has_environment("SNAP_NAME") && OS::get_singleton()->has_environment("SNAP_REVISION")) { ++ return true; ++ } ++ ++ if (access("/run/host/container-manager", F_OK) == 0) { ++ return true; ++ } ++ ++ return false; ++} ++#endif // UDEV_ENABLED ++ ++JoypadLinux::JoypadLinux(InputDefault *in) { ++#ifdef UDEV_ENABLED ++ if (detect_sandbox()) { ++ // Linux binaries in sandboxes / containers need special handling because ++ // libudev doesn't work there. So we need to fallback to manual parsing ++ // of /dev/input in such case. ++ use_udev = false; ++ print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads."); ++ } else { ++ use_udev = initialize_libudev() == 0; ++ if (use_udev) { ++ print_verbose("JoypadLinux: udev enabled and loaded successfully."); ++ } else { ++ print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads."); ++ } ++ } ++#else ++ print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads."); ++#endif ++ input = in; ++ monitor_joypads_thread.start(monitor_joypads_thread_func, this); ++ joypad_events_thread.start(joypad_events_thread_func, this); ++} ++ ++JoypadLinux::~JoypadLinux() { ++ monitor_joypads_exit.set(); ++ joypad_events_exit.set(); ++ monitor_joypads_thread.wait_to_finish(); ++ joypad_events_thread.wait_to_finish(); ++ close_joypads(); ++} ++ ++void JoypadLinux::monitor_joypads_thread_func(void *p_user) { ++ if (p_user) { ++ JoypadLinux *joy = (JoypadLinux *)p_user; ++ joy->monitor_joypads_thread_run(); ++ } ++} ++ ++void JoypadLinux::monitor_joypads_thread_run() { ++#ifdef UDEV_ENABLED ++ if (use_udev) { ++ udev *_udev = udev_new(); ++ if (!_udev) { ++ use_udev = false; ++ ERR_PRINT("Failed getting an udev context, falling back to parsing /dev/input."); ++ monitor_joypads(); ++ } else { ++ enumerate_joypads(_udev); ++ monitor_joypads(_udev); ++ udev_unref(_udev); ++ } ++ } else { ++ monitor_joypads(); ++ } ++#else ++ monitor_joypads(); ++#endif ++} ++ ++#ifdef UDEV_ENABLED ++void JoypadLinux::enumerate_joypads(udev *p_udev) { ++ udev_enumerate *enumerate; ++ udev_list_entry *devices, *dev_list_entry; ++ udev_device *dev; ++ ++ enumerate = udev_enumerate_new(p_udev); ++ udev_enumerate_add_match_subsystem(enumerate, "input"); ++ ++ udev_enumerate_scan_devices(enumerate); ++ devices = udev_enumerate_get_list_entry(enumerate); ++ udev_list_entry_foreach(dev_list_entry, devices) { ++ const char *path = udev_list_entry_get_name(dev_list_entry); ++ dev = udev_device_new_from_syspath(p_udev, path); ++ const char *devnode = udev_device_get_devnode(dev); ++ ++ if (devnode) { ++ String devnode_str = devnode; ++ if (devnode_str.find(ignore_str) == -1) { ++ open_joypad(devnode); ++ } ++ } ++ udev_device_unref(dev); ++ } ++ udev_enumerate_unref(enumerate); ++} ++ ++void JoypadLinux::monitor_joypads(udev *p_udev) { ++ udev_device *dev = nullptr; ++ udev_monitor *mon = udev_monitor_new_from_netlink(p_udev, "udev"); ++ udev_monitor_filter_add_match_subsystem_devtype(mon, "input", nullptr); ++ udev_monitor_enable_receiving(mon); ++ int fd = udev_monitor_get_fd(mon); ++ ++ while (!monitor_joypads_exit.is_set()) { ++ fd_set fds; ++ struct timeval tv; ++ int ret; ++ ++ FD_ZERO(&fds); ++ FD_SET(fd, &fds); ++ tv.tv_sec = 0; ++ tv.tv_usec = 0; ++ ++ ret = select(fd + 1, &fds, nullptr, nullptr, &tv); ++ ++ /* Check if our file descriptor has received data. */ ++ if (ret > 0 && FD_ISSET(fd, &fds)) { ++ /* Make the call to receive the device. ++ select() ensured that this will not block. */ ++ dev = udev_monitor_receive_device(mon); ++ ++ if (dev && udev_device_get_devnode(dev) != nullptr) { ++ String action = udev_device_get_action(dev); ++ const char *devnode = udev_device_get_devnode(dev); ++ if (devnode) { ++ String devnode_str = devnode; ++ if (devnode_str.find(ignore_str) == -1) { ++ if (action == "add") { ++ open_joypad(devnode); ++ } else if (String(action) == "remove") { ++ close_joypad(devnode); ++ } ++ } ++ } ++ udev_device_unref(dev); ++ } ++ } ++ usleep(50000); ++ } ++ udev_monitor_unref(mon); ++} ++#endif ++ ++void JoypadLinux::monitor_joypads() { ++ while (!monitor_joypads_exit.is_set()) { ++ DIR *input_directory; ++ input_directory = opendir("/dev/input"); ++ if (input_directory) { ++ struct dirent *current; ++ char fname[64]; ++ ++ while ((current = readdir(input_directory)) != nullptr) { ++ if (strncmp(current->d_name, "event", 5) != 0) { ++ continue; ++ } ++ sprintf(fname, "/dev/input/%.*s", 16, current->d_name); ++ if (attached_devices.find(fname) == -1) { ++ open_joypad(fname); ++ } ++ } ++ } ++ closedir(input_directory); ++ usleep(1000000); // 1s ++ } ++} ++ ++void JoypadLinux::close_joypads() { ++ for (int i = 0; i < JOYPADS_MAX; i++) { ++ MutexLock lock(joypads_mutex[i]); ++ Joypad &joypad = joypads[i]; ++ close_joypad(joypad, i); ++ } ++} ++ ++void JoypadLinux::close_joypad(const char *p_devpath) { ++ for (int i = 0; i < JOYPADS_MAX; i++) { ++ MutexLock lock(joypads_mutex[i]); ++ Joypad &joypad = joypads[i]; ++ if (joypads[i].devpath == p_devpath) { ++ close_joypad(joypad, i); ++ } ++ } ++} ++ ++void JoypadLinux::close_joypad(Joypad &p_joypad, int p_id) { ++ if (p_joypad.fd != -1) { ++ close(p_joypad.fd); ++ p_joypad.fd = -1; ++ attached_devices.erase(p_joypad.devpath); ++ input->joy_connection_changed(p_id, false, ""); ++ }; ++ p_joypad.events.clear(); ++} ++ ++static String _hex_str(uint8_t p_byte) { ++ static const char *dict = "0123456789abcdef"; ++ char ret[3]; ++ ret[2] = 0; ++ ++ ret[0] = dict[p_byte >> 4]; ++ ret[1] = dict[p_byte & 0xF]; ++ ++ return ret; ++} ++ ++void JoypadLinux::setup_joypad_properties(Joypad &p_joypad) { ++ unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; ++ unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; ++ ++ int num_buttons = 0; ++ int num_axes = 0; ++ ++ if ((ioctl(p_joypad.fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || ++ (ioctl(p_joypad.fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) { ++ return; ++ } ++ for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { ++ if (test_bit(i, keybit)) { ++ p_joypad.key_map[i] = num_buttons++; ++ } ++ } ++ for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) { ++ if (test_bit(i, keybit)) { ++ p_joypad.key_map[i] = num_buttons++; ++ } ++ } ++ for (int i = 0; i < ABS_MISC; ++i) { ++ /* Skip hats */ ++ if (i == ABS_HAT0X) { ++ i = ABS_HAT3Y; ++ continue; ++ } ++ if (test_bit(i, absbit)) { ++ p_joypad.abs_map[i] = num_axes++; ++ p_joypad.abs_info[i] = memnew(input_absinfo); ++ if (ioctl(p_joypad.fd, EVIOCGABS(i), p_joypad.abs_info[i]) < 0) { ++ memdelete(p_joypad.abs_info[i]); ++ p_joypad.abs_info[i] = nullptr; ++ } ++ } ++ } ++ ++ p_joypad.force_feedback = false; ++ p_joypad.ff_effect_timestamp = 0; ++ unsigned long ffbit[NBITS(FF_CNT)]; ++ if (ioctl(p_joypad.fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) { ++ if (test_bit(FF_RUMBLE, ffbit)) { ++ p_joypad.force_feedback = true; ++ } ++ } ++} ++ ++void JoypadLinux::open_joypad(const char *p_path) { ++ int joy_num = input->get_unused_joy_id(); ++ int fd = open(p_path, O_RDWR | O_NONBLOCK); ++ if (fd != -1 && joy_num != -1) { ++ unsigned long evbit[NBITS(EV_MAX)] = { 0 }; ++ unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; ++ unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; ++ ++ // add to attached devices so we don't try to open it again ++ attached_devices.push_back(String(p_path)); ++ ++ if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || ++ (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || ++ (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) { ++ close(fd); ++ return; ++ } ++ ++ //check if the device supports basic gamepad events, prevents certain keyboards from ++ //being detected as joypads ++ if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && ++ (test_bit(ABS_X, absbit) || test_bit(ABS_Y, absbit) || test_bit(ABS_HAT0X, absbit) || ++ test_bit(ABS_GAS, absbit) || test_bit(ABS_RUDDER, absbit)) && ++ (test_bit(BTN_A, keybit) || test_bit(BTN_THUMBL, keybit) || ++ test_bit(BTN_TRIGGER, keybit) || test_bit(BTN_1, keybit))) && ++ !(test_bit(EV_ABS, evbit) && ++ test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit) && ++ test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit))) { ++ close(fd); ++ return; ++ } ++ ++ char uid[128]; ++ char namebuf[128]; ++ String name = ""; ++ input_id inpid; ++ if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) >= 0) { ++ name = namebuf; ++ } ++ ++ if (ioctl(fd, EVIOCGID, &inpid) < 0) { ++ close(fd); ++ return; ++ } ++ ++ uint16_t vendor = BSWAP16(inpid.vendor); ++ uint16_t product = BSWAP16(inpid.product); ++ uint16_t version = BSWAP16(inpid.version); ++ ++ if (input->should_ignore_device(vendor, product)) { ++ // This can be true in cases where Steam is passing information into the game to ignore ++ // original gamepads when using virtual rebindings (See SteamInput). ++ return; ++ } ++ ++ MutexLock lock(joypads_mutex[joy_num]); ++ Joypad &joypad = joypads[joy_num]; ++ joypad.reset(); ++ joypad.fd = fd; ++ joypad.devpath = String(p_path); ++ setup_joypad_properties(joypad); ++ sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0); ++ if (inpid.vendor && inpid.product && inpid.version) { ++ sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0); ++ input->joy_connection_changed(joy_num, true, name, uid); ++ } else { ++ String uidname = uid; ++ int uidlen = MIN(name.length(), 11); ++ for (int i = 0; i < uidlen; i++) { ++ uidname = uidname + _hex_str(name[i]); ++ } ++ uidname += "00"; ++ input->joy_connection_changed(joy_num, true, name, uidname); ++ } ++ } ++} ++ ++void JoypadLinux::joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) { ++ if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { ++ return; ++ } ++ if (p_joypad.ff_effect_id != -1) { ++ joypad_vibration_stop(p_joypad, p_timestamp); ++ } ++ ++ struct ff_effect effect; ++ effect.type = FF_RUMBLE; ++ effect.id = -1; ++ effect.u.rumble.weak_magnitude = floor(p_weak_magnitude * (float)0xffff); ++ effect.u.rumble.strong_magnitude = floor(p_strong_magnitude * (float)0xffff); ++ effect.replay.length = floor(p_duration * 1000); ++ effect.replay.delay = 0; ++ ++ if (ioctl(p_joypad.fd, EVIOCSFF, &effect) < 0) { ++ return; ++ } ++ ++ struct input_event play; ++ play.type = EV_FF; ++ play.code = effect.id; ++ play.value = 1; ++ if (write(p_joypad.fd, (const void *)&play, sizeof(play)) == -1) { ++ print_verbose("Couldn't write to Joypad device."); ++ } ++ ++ p_joypad.ff_effect_id = effect.id; ++ p_joypad.ff_effect_timestamp = p_timestamp; ++} ++ ++void JoypadLinux::joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp) { ++ if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_joypad.ff_effect_id == -1) { ++ return; ++ } ++ ++ if (ioctl(p_joypad.fd, EVIOCRMFF, p_joypad.ff_effect_id) < 0) { ++ return; ++ } ++ ++ p_joypad.ff_effect_id = -1; ++ p_joypad.ff_effect_timestamp = p_timestamp; ++} ++ ++float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { ++ int min = p_abs->minimum; ++ int max = p_abs->maximum; ++ // Convert to a value between -1.0f and 1.0f. ++ return 2.0f * (p_value - min) / (max - min) - 1.0f; ++} ++ ++void JoypadLinux::joypad_events_thread_func(void *p_user) { ++ if (p_user) { ++ JoypadLinux *joy = (JoypadLinux *)p_user; ++ joy->joypad_events_thread_run(); ++ } ++} ++ ++void JoypadLinux::joypad_events_thread_run() { ++ while (!joypad_events_exit.is_set()) { ++ bool no_events = true; ++ for (int i = 0; i < JOYPADS_MAX; i++) { ++ MutexLock lock(joypads_mutex[i]); ++ Joypad &joypad = joypads[i]; ++ if (joypad.fd == -1) { ++ continue; ++ } ++ input_event event; ++ while (read(joypad.fd, &event, sizeof(event)) > 0) { ++ JoypadEvent joypad_event; ++ joypad_event.type = event.type; ++ joypad_event.code = event.code; ++ joypad_event.value = event.value; ++ joypad.events.push_back(joypad_event); ++ } ++ if (errno != EAGAIN) { ++ close_joypad(joypad, i); ++ } ++ } ++ if (no_events) { ++ usleep(10000); // 10ms ++ } ++ } ++} ++ ++void JoypadLinux::process_joypads() { ++ for (int i = 0; i < JOYPADS_MAX; i++) { ++ MutexLock lock(joypads_mutex[i]); ++ Joypad &joypad = joypads[i]; ++ if (joypad.fd == -1) { ++ continue; ++ } ++ for (uint32_t j = 0; j < joypad.events.size(); j++) { ++ const JoypadEvent &joypad_event = joypad.events[j]; ++ // joypad_event may be tainted and out of MAX_KEY range, which will cause ++ // joypad.key_map[joypad_event.code] to crash ++ if (joypad_event.code >= MAX_KEY) { ++ return; ++ } ++ ++ switch (joypad_event.type) { ++ case EV_KEY: ++ input->joy_button(i, joypad.key_map[joypad_event.code], joypad_event.value); ++ break; ++ ++ case EV_ABS: ++ switch (joypad_event.code) { ++ case ABS_HAT0X: ++ if (joypad_event.value != 0) { ++ if (joypad_event.value < 0) { ++ joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_LEFT) & ~InputDefault::HAT_MASK_RIGHT; ++ } else { ++ joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_RIGHT) & ~InputDefault::HAT_MASK_LEFT; ++ } ++ } else { ++ joypad.dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT); ++ } ++ input->joy_hat(i, joypad.dpad); ++ break; ++ ++ case ABS_HAT0Y: ++ if (joypad_event.value != 0) { ++ if (joypad_event.value < 0) { ++ joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_UP) & ~InputDefault::HAT_MASK_DOWN; ++ } else { ++ joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_DOWN) & ~InputDefault::HAT_MASK_UP; ++ } ++ } else { ++ joypad.dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN); ++ } ++ input->joy_hat(i, joypad.dpad); ++ break; ++ ++ default: ++ if (joypad_event.code >= MAX_ABS) { ++ return; ++ } ++ if (joypad.abs_map[joypad_event.code] != -1 && joypad.abs_info[joypad_event.code]) { ++ float value = axis_correct(joypad.abs_info[joypad_event.code], joypad_event.value); ++ joypad.curr_axis[joypad.abs_map[joypad_event.code]] = value; ++ } ++ break; ++ } ++ break; ++ } ++ } ++ joypad.events.clear(); ++ ++ for (int j = 0; j < MAX_ABS; j++) { ++ int index = joypad.abs_map[j]; ++ if (index != -1) { ++ input->joy_axis(i, index, joypad.curr_axis[index]); ++ } ++ } ++ ++ if (joypad.force_feedback) { ++ uint64_t timestamp = input->get_joy_vibration_timestamp(i); ++ if (timestamp > joypad.ff_effect_timestamp) { ++ Vector2 strength = input->get_joy_vibration_strength(i); ++ float duration = input->get_joy_vibration_duration(i); ++ if (strength.x == 0 && strength.y == 0) { ++ joypad_vibration_stop(joypad, timestamp); ++ } else { ++ joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp); ++ } ++ } ++ } ++ } ++} ++ ++#endif // JOYDEV_ENABLED +diff --git a/platform/x11es/joypad_linux.h b/platform/x11es/joypad_linux.h +new file mode 100644 +index 0000000000..acf709684f +--- /dev/null ++++ b/platform/x11es/joypad_linux.h +@@ -0,0 +1,125 @@ ++/**************************************************************************/ ++/* joypad_linux.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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: Andreas Haas ++ ++#ifndef JOYPAD_LINUX_H ++#define JOYPAD_LINUX_H ++ ++#ifdef JOYDEV_ENABLED ++#include "core/os/mutex.h" ++#include "core/os/thread.h" ++#include "main/input_default.h" ++ ++struct input_absinfo; ++ ++class JoypadLinux { ++public: ++ JoypadLinux(InputDefault *in); ++ ~JoypadLinux(); ++ void process_joypads(); ++ ++private: ++ enum { ++ JOYPADS_MAX = 16, ++ MAX_ABS = 63, ++ MAX_KEY = 767, // Hack because can't be included here ++ }; ++ ++ struct JoypadEvent { ++ uint16_t type; ++ uint16_t code; ++ int32_t value; ++ }; ++ ++ struct Joypad { ++ float curr_axis[MAX_ABS]; ++ int key_map[MAX_KEY]; ++ int abs_map[MAX_ABS]; ++ int dpad; ++ int fd; ++ ++ String devpath; ++ input_absinfo *abs_info[MAX_ABS]; ++ ++ bool force_feedback; ++ int ff_effect_id; ++ uint64_t ff_effect_timestamp; ++ ++ LocalVector events; ++ ++ Joypad(); ++ ~Joypad(); ++ void reset(); ++ }; ++ ++#ifdef UDEV_ENABLED ++ bool use_udev; ++#endif ++ InputDefault *input; ++ ++ SafeFlag monitor_joypads_exit; ++ SafeFlag joypad_events_exit; ++ Thread monitor_joypads_thread; ++ Thread joypad_events_thread; ++ ++ Joypad joypads[JOYPADS_MAX]; ++ Mutex joypads_mutex[JOYPADS_MAX]; ++ ++ Vector attached_devices; ++ ++ static void monitor_joypads_thread_func(void *p_user); ++ void monitor_joypads_thread_run(); ++ ++ void open_joypad(const char *p_path); ++ void setup_joypad_properties(Joypad &p_joypad); ++ ++ void close_joypads(); ++ void close_joypad(const char *p_devpath); ++ void close_joypad(Joypad &p_joypad, int p_id); ++ ++#ifdef UDEV_ENABLED ++ void enumerate_joypads(struct udev *p_udev); ++ void monitor_joypads(struct udev *p_udev); ++#endif ++ void monitor_joypads(); ++ ++ void joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); ++ void joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp); ++ ++ static void joypad_events_thread_func(void *p_user); ++ void joypad_events_thread_run(); ++ ++ float axis_correct(const input_absinfo *p_abs, int p_value) const; ++}; ++ ++#endif ++ ++#endif // JOYPAD_LINUX_H +diff --git a/platform/x11es/key_mapping_x11.cpp b/platform/x11es/key_mapping_x11.cpp +new file mode 100644 +index 0000000000..7cb5218580 +--- /dev/null ++++ b/platform/x11es/key_mapping_x11.cpp +@@ -0,0 +1,1958 @@ ++/**************************************************************************/ ++/* key_mapping_x11.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#include "key_mapping_x11.h" ++ ++/***** SCAN CODE CONVERSION ******/ ++ ++struct _XTranslatePair { ++ KeySym keysym; ++ unsigned int keycode; ++}; ++ ++static _XTranslatePair _xkeysym_to_keycode[] = { ++ // misc keys ++ ++ { XK_Escape, KEY_ESCAPE }, ++ { XK_Tab, KEY_TAB }, ++ { XK_ISO_Left_Tab, KEY_BACKTAB }, ++ { XK_BackSpace, KEY_BACKSPACE }, ++ { XK_Return, KEY_ENTER }, ++ { XK_Insert, KEY_INSERT }, ++ { XK_Delete, KEY_DELETE }, ++ { XK_Clear, KEY_DELETE }, ++ { XK_Pause, KEY_PAUSE }, ++ { XK_Print, KEY_PRINT }, ++ { XK_Home, KEY_HOME }, ++ { XK_End, KEY_END }, ++ { XK_Left, KEY_LEFT }, ++ { XK_Up, KEY_UP }, ++ { XK_Right, KEY_RIGHT }, ++ { XK_Down, KEY_DOWN }, ++ { XK_Prior, KEY_PAGEUP }, ++ { XK_Next, KEY_PAGEDOWN }, ++ { XK_Shift_L, KEY_SHIFT }, ++ { XK_Shift_R, KEY_SHIFT }, ++ { XK_Shift_Lock, KEY_SHIFT }, ++ { XK_Control_L, KEY_CONTROL }, ++ { XK_Control_R, KEY_CONTROL }, ++ { XK_Meta_L, KEY_META }, ++ { XK_Meta_R, KEY_META }, ++ { XK_Alt_L, KEY_ALT }, ++ { XK_Alt_R, KEY_ALT }, ++ { XK_Caps_Lock, KEY_CAPSLOCK }, ++ { XK_Num_Lock, KEY_NUMLOCK }, ++ { XK_Scroll_Lock, KEY_SCROLLLOCK }, ++ { XK_Super_L, KEY_SUPER_L }, ++ { XK_Super_R, KEY_SUPER_R }, ++ { XK_Menu, KEY_MENU }, ++ { XK_Hyper_L, KEY_HYPER_L }, ++ { XK_Hyper_R, KEY_HYPER_R }, ++ { XK_Help, KEY_HELP }, ++ { XK_KP_Space, KEY_SPACE }, ++ { XK_KP_Tab, KEY_TAB }, ++ { XK_KP_Enter, KEY_KP_ENTER }, ++ { XK_Home, KEY_HOME }, ++ { XK_Left, KEY_LEFT }, ++ { XK_Up, KEY_UP }, ++ { XK_Right, KEY_RIGHT }, ++ { XK_Down, KEY_DOWN }, ++ { XK_Prior, KEY_PAGEUP }, ++ { XK_Next, KEY_PAGEDOWN }, ++ { XK_End, KEY_END }, ++ { XK_Begin, KEY_CLEAR }, ++ { XK_Insert, KEY_INSERT }, ++ { XK_Delete, KEY_DELETE }, ++ //{ XK_KP_Equal, KEY_EQUAL }, ++ //{ XK_KP_Separator, KEY_COMMA }, ++ { XK_KP_Decimal, KEY_KP_PERIOD }, ++ { XK_KP_Delete, KEY_KP_PERIOD }, ++ { XK_KP_Multiply, KEY_KP_MULTIPLY }, ++ { XK_KP_Divide, KEY_KP_DIVIDE }, ++ { XK_KP_Subtract, KEY_KP_SUBTRACT }, ++ { XK_KP_Add, KEY_KP_ADD }, ++ { XK_KP_0, KEY_KP_0 }, ++ { XK_KP_1, KEY_KP_1 }, ++ { XK_KP_2, KEY_KP_2 }, ++ { XK_KP_3, KEY_KP_3 }, ++ { XK_KP_4, KEY_KP_4 }, ++ { XK_KP_5, KEY_KP_5 }, ++ { XK_KP_6, KEY_KP_6 }, ++ { XK_KP_7, KEY_KP_7 }, ++ { XK_KP_8, KEY_KP_8 }, ++ { XK_KP_9, KEY_KP_9 }, ++ // same but with numlock ++ { XK_KP_Insert, KEY_KP_0 }, ++ { XK_KP_End, KEY_KP_1 }, ++ { XK_KP_Down, KEY_KP_2 }, ++ { XK_KP_Page_Down, KEY_KP_3 }, ++ { XK_KP_Left, KEY_KP_4 }, ++ { XK_KP_Begin, KEY_KP_5 }, ++ { XK_KP_Right, KEY_KP_6 }, ++ { XK_KP_Home, KEY_KP_7 }, ++ { XK_KP_Up, KEY_KP_8 }, ++ { XK_KP_Page_Up, KEY_KP_9 }, ++ { XK_F1, KEY_F1 }, ++ { XK_F2, KEY_F2 }, ++ { XK_F3, KEY_F3 }, ++ { XK_F4, KEY_F4 }, ++ { XK_F5, KEY_F5 }, ++ { XK_F6, KEY_F6 }, ++ { XK_F7, KEY_F7 }, ++ { XK_F8, KEY_F8 }, ++ { XK_F9, KEY_F9 }, ++ { XK_F10, KEY_F10 }, ++ { XK_F11, KEY_F11 }, ++ { XK_F12, KEY_F12 }, ++ { XK_F13, KEY_F13 }, ++ { XK_F14, KEY_F14 }, ++ { XK_F15, KEY_F15 }, ++ { XK_F16, KEY_F16 }, ++ ++ // media keys ++ { XF86XK_Back, KEY_BACK }, ++ { XF86XK_Forward, KEY_FORWARD }, ++ { XF86XK_Stop, KEY_STOP }, ++ { XF86XK_Refresh, KEY_REFRESH }, ++ { XF86XK_Favorites, KEY_FAVORITES }, ++ { XF86XK_AudioMedia, KEY_LAUNCHMEDIA }, ++ { XF86XK_OpenURL, KEY_OPENURL }, ++ { XF86XK_HomePage, KEY_HOMEPAGE }, ++ { XF86XK_Search, KEY_SEARCH }, ++ { XF86XK_AudioLowerVolume, KEY_VOLUMEDOWN }, ++ { XF86XK_AudioMute, KEY_VOLUMEMUTE }, ++ { XF86XK_AudioRaiseVolume, KEY_VOLUMEUP }, ++ { XF86XK_AudioPlay, KEY_MEDIAPLAY }, ++ { XF86XK_AudioStop, KEY_MEDIASTOP }, ++ { XF86XK_AudioPrev, KEY_MEDIAPREVIOUS }, ++ { XF86XK_AudioNext, KEY_MEDIANEXT }, ++ { XF86XK_AudioRecord, KEY_MEDIARECORD }, ++ ++ // launch keys ++ { XF86XK_Mail, KEY_LAUNCHMAIL }, ++ { XF86XK_MyComputer, KEY_LAUNCH0 }, ++ { XF86XK_Calculator, KEY_LAUNCH1 }, ++ { XF86XK_Standby, KEY_STANDBY }, ++ ++ { XF86XK_Launch0, KEY_LAUNCH2 }, ++ { XF86XK_Launch1, KEY_LAUNCH3 }, ++ { XF86XK_Launch2, KEY_LAUNCH4 }, ++ { XF86XK_Launch3, KEY_LAUNCH5 }, ++ { XF86XK_Launch4, KEY_LAUNCH6 }, ++ { XF86XK_Launch5, KEY_LAUNCH7 }, ++ { XF86XK_Launch6, KEY_LAUNCH8 }, ++ { XF86XK_Launch7, KEY_LAUNCH9 }, ++ { XF86XK_Launch8, KEY_LAUNCHA }, ++ { XF86XK_Launch9, KEY_LAUNCHB }, ++ { XF86XK_LaunchA, KEY_LAUNCHC }, ++ { XF86XK_LaunchB, KEY_LAUNCHD }, ++ { XF86XK_LaunchC, KEY_LAUNCHE }, ++ { XF86XK_LaunchD, KEY_LAUNCHF }, ++ ++ { 0, 0 } ++}; ++ ++struct _TranslatePair { ++ unsigned int keysym; ++ unsigned int keycode; ++}; ++ ++static _TranslatePair _scancode_to_keycode[] = { ++ ++ { KEY_ESCAPE, 0x09 }, ++ { KEY_1, 0x0A }, ++ { KEY_2, 0x0B }, ++ { KEY_3, 0x0C }, ++ { KEY_4, 0x0D }, ++ { KEY_5, 0x0E }, ++ { KEY_6, 0x0F }, ++ { KEY_7, 0x10 }, ++ { KEY_8, 0x11 }, ++ { KEY_9, 0x12 }, ++ { KEY_0, 0x13 }, ++ { KEY_MINUS, 0x14 }, ++ { KEY_EQUAL, 0x15 }, ++ { KEY_BACKSPACE, 0x16 }, ++ { KEY_TAB, 0x17 }, ++ { KEY_Q, 0x18 }, ++ { KEY_W, 0x19 }, ++ { KEY_E, 0x1A }, ++ { KEY_R, 0x1B }, ++ { KEY_T, 0x1C }, ++ { KEY_Y, 0x1D }, ++ { KEY_U, 0x1E }, ++ { KEY_I, 0x1F }, ++ { KEY_O, 0x20 }, ++ { KEY_P, 0x21 }, ++ { KEY_BRACELEFT, 0x22 }, ++ { KEY_BRACERIGHT, 0x23 }, ++ { KEY_ENTER, 0x24 }, ++ { KEY_CONTROL, 0x25 }, ++ { KEY_A, 0x26 }, ++ { KEY_S, 0x27 }, ++ { KEY_D, 0x28 }, ++ { KEY_F, 0x29 }, ++ { KEY_G, 0x2A }, ++ { KEY_H, 0x2B }, ++ { KEY_J, 0x2C }, ++ { KEY_K, 0x2D }, ++ { KEY_L, 0x2E }, ++ { KEY_SEMICOLON, 0x2F }, ++ { KEY_APOSTROPHE, 0x30 }, ++ { KEY_QUOTELEFT, 0x31 }, ++ { KEY_SHIFT, 0x32 }, ++ { KEY_BACKSLASH, 0x33 }, ++ { KEY_Z, 0x34 }, ++ { KEY_X, 0x35 }, ++ { KEY_C, 0x36 }, ++ { KEY_V, 0x37 }, ++ { KEY_B, 0x38 }, ++ { KEY_N, 0x39 }, ++ { KEY_M, 0x3A }, ++ { KEY_COMMA, 0x3B }, ++ { KEY_PERIOD, 0x3C }, ++ { KEY_SLASH, 0x3D }, ++ { KEY_SHIFT, 0x3E }, ++ { KEY_KP_MULTIPLY, 0x3F }, ++ { KEY_ALT, 0x40 }, ++ { KEY_SPACE, 0x41 }, ++ { KEY_CAPSLOCK, 0x42 }, ++ { KEY_F1, 0x43 }, ++ { KEY_F2, 0x44 }, ++ { KEY_F3, 0x45 }, ++ { KEY_F4, 0x46 }, ++ { KEY_F5, 0x47 }, ++ { KEY_F6, 0x48 }, ++ { KEY_F7, 0x49 }, ++ { KEY_F8, 0x4A }, ++ { KEY_F9, 0x4B }, ++ { KEY_F10, 0x4C }, ++ { KEY_NUMLOCK, 0x4D }, ++ { KEY_SCROLLLOCK, 0x4E }, ++ { KEY_KP_7, 0x4F }, ++ { KEY_KP_8, 0x50 }, ++ { KEY_KP_9, 0x51 }, ++ { KEY_KP_SUBTRACT, 0x52 }, ++ { KEY_KP_4, 0x53 }, ++ { KEY_KP_5, 0x54 }, ++ { KEY_KP_6, 0x55 }, ++ { KEY_KP_ADD, 0x56 }, ++ { KEY_KP_1, 0x57 }, ++ { KEY_KP_2, 0x58 }, ++ { KEY_KP_3, 0x59 }, ++ { KEY_KP_0, 0x5A }, ++ { KEY_KP_PERIOD, 0x5B }, ++ //{ KEY_???, 0x5E }, //NON US BACKSLASH ++ { KEY_F11, 0x5F }, ++ { KEY_F12, 0x60 }, ++ { KEY_KP_ENTER, 0x68 }, ++ { KEY_CONTROL, 0x69 }, ++ { KEY_KP_DIVIDE, 0x6A }, ++ { KEY_PRINT, 0x6B }, ++ { KEY_ALT, 0x6C }, ++ { KEY_ENTER, 0x6D }, ++ { KEY_HOME, 0x6E }, ++ { KEY_UP, 0x6F }, ++ { KEY_PAGEUP, 0x70 }, ++ { KEY_LEFT, 0x71 }, ++ { KEY_RIGHT, 0x72 }, ++ { KEY_END, 0x73 }, ++ { KEY_DOWN, 0x74 }, ++ { KEY_PAGEDOWN, 0x75 }, ++ { KEY_INSERT, 0x76 }, ++ { KEY_DELETE, 0x77 }, ++ { KEY_VOLUMEMUTE, 0x79 }, ++ { KEY_VOLUMEDOWN, 0x7A }, ++ { KEY_VOLUMEUP, 0x7B }, ++ { KEY_PAUSE, 0x7F }, ++ { KEY_SUPER_L, 0x85 }, ++ { KEY_SUPER_R, 0x86 }, ++ { KEY_MENU, 0x87 }, ++ { KEY_UNKNOWN, 0 } ++}; ++ ++unsigned int KeyMappingX11::get_scancode(unsigned int p_code) { ++ unsigned int keycode = KEY_UNKNOWN; ++ for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) { ++ if (_scancode_to_keycode[i].keycode == p_code) { ++ keycode = _scancode_to_keycode[i].keysym; ++ break; ++ } ++ } ++ ++ return keycode; ++} ++ ++unsigned int KeyMappingX11::get_xlibcode(unsigned int p_keysym) { ++ unsigned int code = 0; ++ for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) { ++ if (_scancode_to_keycode[i].keysym == p_keysym) { ++ code = _scancode_to_keycode[i].keycode; ++ break; ++ } ++ } ++ ++ return code; ++} ++ ++unsigned int KeyMappingX11::get_keycode(KeySym p_keysym) { ++ // kinda bruteforce.. could optimize. ++ ++ if (p_keysym < 0x100) { // Latin 1, maps 1-1 ++ return p_keysym; ++ } ++ ++ // look for special key ++ for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) { ++ if (_xkeysym_to_keycode[idx].keysym == p_keysym) { ++ return _xkeysym_to_keycode[idx].keycode; ++ } ++ } ++ ++ return 0; ++} ++ ++KeySym KeyMappingX11::get_keysym(unsigned int p_code) { ++ // kinda bruteforce.. could optimize. ++ ++ if (p_code < 0x100) { // Latin 1, maps 1-1 ++ return p_code; ++ } ++ ++ // look for special key ++ for (int idx = 0; _xkeysym_to_keycode[idx].keysym != 0; idx++) { ++ if (_xkeysym_to_keycode[idx].keycode == p_code) { ++ return _xkeysym_to_keycode[idx].keysym; ++ } ++ } ++ ++ return 0; ++} ++ ++/***** UNICODE CONVERSION ******/ ++ ++// Tables taken from FOX toolkit ++ ++struct _XTranslateUnicodePair { ++ KeySym keysym; ++ unsigned int unicode; ++}; ++ ++enum { ++ ++ _KEYSYM_MAX = 759 ++}; ++ ++static _XTranslateUnicodePair _xkeysym_to_unicode[_KEYSYM_MAX] = { ++ { 0x01A1, 0x0104 }, ++ { 0x01A2, 0x02D8 }, ++ { 0x01A3, 0x0141 }, ++ { 0x01A5, 0x013D }, ++ { 0x01A6, 0x015A }, ++ { 0x01A9, 0x0160 }, ++ { 0x01AA, 0x015E }, ++ { 0x01AB, 0x0164 }, ++ { 0x01AC, 0x0179 }, ++ { 0x01AE, 0x017D }, ++ { 0x01AF, 0x017B }, ++ { 0x01B1, 0x0105 }, ++ { 0x01B2, 0x02DB }, ++ { 0x01B3, 0x0142 }, ++ { 0x01B5, 0x013E }, ++ { 0x01B6, 0x015B }, ++ { 0x01B7, 0x02C7 }, ++ { 0x01B9, 0x0161 }, ++ { 0x01BA, 0x015F }, ++ { 0x01BB, 0x0165 }, ++ { 0x01BC, 0x017A }, ++ { 0x01BD, 0x02DD }, ++ { 0x01BE, 0x017E }, ++ { 0x01BF, 0x017C }, ++ { 0x01C0, 0x0154 }, ++ { 0x01C3, 0x0102 }, ++ { 0x01C5, 0x0139 }, ++ { 0x01C6, 0x0106 }, ++ { 0x01C8, 0x010C }, ++ { 0x01CA, 0x0118 }, ++ { 0x01CC, 0x011A }, ++ { 0x01CF, 0x010E }, ++ { 0x01D0, 0x0110 }, ++ { 0x01D1, 0x0143 }, ++ { 0x01D2, 0x0147 }, ++ { 0x01D5, 0x0150 }, ++ { 0x01D8, 0x0158 }, ++ { 0x01D9, 0x016E }, ++ { 0x01DB, 0x0170 }, ++ { 0x01DE, 0x0162 }, ++ { 0x01E0, 0x0155 }, ++ { 0x01E3, 0x0103 }, ++ { 0x01E5, 0x013A }, ++ { 0x01E6, 0x0107 }, ++ { 0x01E8, 0x010D }, ++ { 0x01EA, 0x0119 }, ++ { 0x01EC, 0x011B }, ++ { 0x01EF, 0x010F }, ++ { 0x01F0, 0x0111 }, ++ { 0x01F1, 0x0144 }, ++ { 0x01F2, 0x0148 }, ++ { 0x01F5, 0x0151 }, ++ { 0x01F8, 0x0159 }, ++ { 0x01F9, 0x016F }, ++ { 0x01FB, 0x0171 }, ++ { 0x01FE, 0x0163 }, ++ { 0x01FF, 0x02D9 }, ++ { 0x02A1, 0x0126 }, ++ { 0x02A6, 0x0124 }, ++ { 0x02A9, 0x0130 }, ++ { 0x02AB, 0x011E }, ++ { 0x02AC, 0x0134 }, ++ { 0x02B1, 0x0127 }, ++ { 0x02B6, 0x0125 }, ++ { 0x02B9, 0x0131 }, ++ { 0x02BB, 0x011F }, ++ { 0x02BC, 0x0135 }, ++ { 0x02C5, 0x010A }, ++ { 0x02C6, 0x0108 }, ++ { 0x02D5, 0x0120 }, ++ { 0x02D8, 0x011C }, ++ { 0x02DD, 0x016C }, ++ { 0x02DE, 0x015C }, ++ { 0x02E5, 0x010B }, ++ { 0x02E6, 0x0109 }, ++ { 0x02F5, 0x0121 }, ++ { 0x02F8, 0x011D }, ++ { 0x02FD, 0x016D }, ++ { 0x02FE, 0x015D }, ++ { 0x03A2, 0x0138 }, ++ { 0x03A3, 0x0156 }, ++ { 0x03A5, 0x0128 }, ++ { 0x03A6, 0x013B }, ++ { 0x03AA, 0x0112 }, ++ { 0x03AB, 0x0122 }, ++ { 0x03AC, 0x0166 }, ++ { 0x03B3, 0x0157 }, ++ { 0x03B5, 0x0129 }, ++ { 0x03B6, 0x013C }, ++ { 0x03BA, 0x0113 }, ++ { 0x03BB, 0x0123 }, ++ { 0x03BC, 0x0167 }, ++ { 0x03BD, 0x014A }, ++ { 0x03BF, 0x014B }, ++ { 0x03C0, 0x0100 }, ++ { 0x03C7, 0x012E }, ++ { 0x03CC, 0x0116 }, ++ { 0x03CF, 0x012A }, ++ { 0x03D1, 0x0145 }, ++ { 0x03D2, 0x014C }, ++ { 0x03D3, 0x0136 }, ++ { 0x03D9, 0x0172 }, ++ { 0x03DD, 0x0168 }, ++ { 0x03DE, 0x016A }, ++ { 0x03E0, 0x0101 }, ++ { 0x03E7, 0x012F }, ++ { 0x03EC, 0x0117 }, ++ { 0x03EF, 0x012B }, ++ { 0x03F1, 0x0146 }, ++ { 0x03F2, 0x014D }, ++ { 0x03F3, 0x0137 }, ++ { 0x03F9, 0x0173 }, ++ { 0x03FD, 0x0169 }, ++ { 0x03FE, 0x016B }, ++ { 0x047E, 0x203E }, ++ { 0x04A1, 0x3002 }, ++ { 0x04A2, 0x300C }, ++ { 0x04A3, 0x300D }, ++ { 0x04A4, 0x3001 }, ++ { 0x04A5, 0x30FB }, ++ { 0x04A6, 0x30F2 }, ++ { 0x04A7, 0x30A1 }, ++ { 0x04A8, 0x30A3 }, ++ { 0x04A9, 0x30A5 }, ++ { 0x04AA, 0x30A7 }, ++ { 0x04AB, 0x30A9 }, ++ { 0x04AC, 0x30E3 }, ++ { 0x04AD, 0x30E5 }, ++ { 0x04AE, 0x30E7 }, ++ { 0x04AF, 0x30C3 }, ++ { 0x04B0, 0x30FC }, ++ { 0x04B1, 0x30A2 }, ++ { 0x04B2, 0x30A4 }, ++ { 0x04B3, 0x30A6 }, ++ { 0x04B4, 0x30A8 }, ++ { 0x04B5, 0x30AA }, ++ { 0x04B6, 0x30AB }, ++ { 0x04B7, 0x30AD }, ++ { 0x04B8, 0x30AF }, ++ { 0x04B9, 0x30B1 }, ++ { 0x04BA, 0x30B3 }, ++ { 0x04BB, 0x30B5 }, ++ { 0x04BC, 0x30B7 }, ++ { 0x04BD, 0x30B9 }, ++ { 0x04BE, 0x30BB }, ++ { 0x04BF, 0x30BD }, ++ { 0x04C0, 0x30BF }, ++ { 0x04C1, 0x30C1 }, ++ { 0x04C2, 0x30C4 }, ++ { 0x04C3, 0x30C6 }, ++ { 0x04C4, 0x30C8 }, ++ { 0x04C5, 0x30CA }, ++ { 0x04C6, 0x30CB }, ++ { 0x04C7, 0x30CC }, ++ { 0x04C8, 0x30CD }, ++ { 0x04C9, 0x30CE }, ++ { 0x04CA, 0x30CF }, ++ { 0x04CB, 0x30D2 }, ++ { 0x04CC, 0x30D5 }, ++ { 0x04CD, 0x30D8 }, ++ { 0x04CE, 0x30DB }, ++ { 0x04CF, 0x30DE }, ++ { 0x04D0, 0x30DF }, ++ { 0x04D1, 0x30E0 }, ++ { 0x04D2, 0x30E1 }, ++ { 0x04D3, 0x30E2 }, ++ { 0x04D4, 0x30E4 }, ++ { 0x04D5, 0x30E6 }, ++ { 0x04D6, 0x30E8 }, ++ { 0x04D7, 0x30E9 }, ++ { 0x04D8, 0x30EA }, ++ { 0x04D9, 0x30EB }, ++ { 0x04DA, 0x30EC }, ++ { 0x04DB, 0x30ED }, ++ { 0x04DC, 0x30EF }, ++ { 0x04DD, 0x30F3 }, ++ { 0x04DE, 0x309B }, ++ { 0x04DF, 0x309C }, ++ { 0x05AC, 0x060C }, ++ { 0x05BB, 0x061B }, ++ { 0x05BF, 0x061F }, ++ { 0x05C1, 0x0621 }, ++ { 0x05C2, 0x0622 }, ++ { 0x05C3, 0x0623 }, ++ { 0x05C4, 0x0624 }, ++ { 0x05C5, 0x0625 }, ++ { 0x05C6, 0x0626 }, ++ { 0x05C7, 0x0627 }, ++ { 0x05C8, 0x0628 }, ++ { 0x05C9, 0x0629 }, ++ { 0x05CA, 0x062A }, ++ { 0x05CB, 0x062B }, ++ { 0x05CC, 0x062C }, ++ { 0x05CD, 0x062D }, ++ { 0x05CE, 0x062E }, ++ { 0x05CF, 0x062F }, ++ { 0x05D0, 0x0630 }, ++ { 0x05D1, 0x0631 }, ++ { 0x05D2, 0x0632 }, ++ { 0x05D3, 0x0633 }, ++ { 0x05D4, 0x0634 }, ++ { 0x05D5, 0x0635 }, ++ { 0x05D6, 0x0636 }, ++ { 0x05D7, 0x0637 }, ++ { 0x05D8, 0x0638 }, ++ { 0x05D9, 0x0639 }, ++ { 0x05DA, 0x063A }, ++ { 0x05E0, 0x0640 }, ++ { 0x05E1, 0x0641 }, ++ { 0x05E2, 0x0642 }, ++ { 0x05E3, 0x0643 }, ++ { 0x05E4, 0x0644 }, ++ { 0x05E5, 0x0645 }, ++ { 0x05E6, 0x0646 }, ++ { 0x05E7, 0x0647 }, ++ { 0x05E8, 0x0648 }, ++ { 0x05E9, 0x0649 }, ++ { 0x05EA, 0x064A }, ++ { 0x05EB, 0x064B }, ++ { 0x05EC, 0x064C }, ++ { 0x05ED, 0x064D }, ++ { 0x05EE, 0x064E }, ++ { 0x05EF, 0x064F }, ++ { 0x05F0, 0x0650 }, ++ { 0x05F1, 0x0651 }, ++ { 0x05F2, 0x0652 }, ++ { 0x06A1, 0x0452 }, ++ { 0x06A2, 0x0453 }, ++ { 0x06A3, 0x0451 }, ++ { 0x06A4, 0x0454 }, ++ { 0x06A5, 0x0455 }, ++ { 0x06A6, 0x0456 }, ++ { 0x06A7, 0x0457 }, ++ { 0x06A8, 0x0458 }, ++ { 0x06A9, 0x0459 }, ++ { 0x06AA, 0x045A }, ++ { 0x06AB, 0x045B }, ++ { 0x06AC, 0x045C }, ++ { 0x06AE, 0x045E }, ++ { 0x06AF, 0x045F }, ++ { 0x06B0, 0x2116 }, ++ { 0x06B1, 0x0402 }, ++ { 0x06B2, 0x0403 }, ++ { 0x06B3, 0x0401 }, ++ { 0x06B4, 0x0404 }, ++ { 0x06B5, 0x0405 }, ++ { 0x06B6, 0x0406 }, ++ { 0x06B7, 0x0407 }, ++ { 0x06B8, 0x0408 }, ++ { 0x06B9, 0x0409 }, ++ { 0x06BA, 0x040A }, ++ { 0x06BB, 0x040B }, ++ { 0x06BC, 0x040C }, ++ { 0x06BE, 0x040E }, ++ { 0x06BF, 0x040F }, ++ { 0x06C0, 0x044E }, ++ { 0x06C1, 0x0430 }, ++ { 0x06C2, 0x0431 }, ++ { 0x06C3, 0x0446 }, ++ { 0x06C4, 0x0434 }, ++ { 0x06C5, 0x0435 }, ++ { 0x06C6, 0x0444 }, ++ { 0x06C7, 0x0433 }, ++ { 0x06C8, 0x0445 }, ++ { 0x06C9, 0x0438 }, ++ { 0x06CA, 0x0439 }, ++ { 0x06CB, 0x043A }, ++ { 0x06CC, 0x043B }, ++ { 0x06CD, 0x043C }, ++ { 0x06CE, 0x043D }, ++ { 0x06CF, 0x043E }, ++ { 0x06D0, 0x043F }, ++ { 0x06D1, 0x044F }, ++ { 0x06D2, 0x0440 }, ++ { 0x06D3, 0x0441 }, ++ { 0x06D4, 0x0442 }, ++ { 0x06D5, 0x0443 }, ++ { 0x06D6, 0x0436 }, ++ { 0x06D7, 0x0432 }, ++ { 0x06D8, 0x044C }, ++ { 0x06D9, 0x044B }, ++ { 0x06DA, 0x0437 }, ++ { 0x06DB, 0x0448 }, ++ { 0x06DC, 0x044D }, ++ { 0x06DD, 0x0449 }, ++ { 0x06DE, 0x0447 }, ++ { 0x06DF, 0x044A }, ++ { 0x06E0, 0x042E }, ++ { 0x06E1, 0x0410 }, ++ { 0x06E2, 0x0411 }, ++ { 0x06E3, 0x0426 }, ++ { 0x06E4, 0x0414 }, ++ { 0x06E5, 0x0415 }, ++ { 0x06E6, 0x0424 }, ++ { 0x06E7, 0x0413 }, ++ { 0x06E8, 0x0425 }, ++ { 0x06E9, 0x0418 }, ++ { 0x06EA, 0x0419 }, ++ { 0x06EB, 0x041A }, ++ { 0x06EC, 0x041B }, ++ { 0x06ED, 0x041C }, ++ { 0x06EE, 0x041D }, ++ { 0x06EF, 0x041E }, ++ { 0x06F0, 0x041F }, ++ { 0x06F1, 0x042F }, ++ { 0x06F2, 0x0420 }, ++ { 0x06F3, 0x0421 }, ++ { 0x06F4, 0x0422 }, ++ { 0x06F5, 0x0423 }, ++ { 0x06F6, 0x0416 }, ++ { 0x06F7, 0x0412 }, ++ { 0x06F8, 0x042C }, ++ { 0x06F9, 0x042B }, ++ { 0x06FA, 0x0417 }, ++ { 0x06FB, 0x0428 }, ++ { 0x06FC, 0x042D }, ++ { 0x06FD, 0x0429 }, ++ { 0x06FE, 0x0427 }, ++ { 0x06FF, 0x042A }, ++ { 0x07A1, 0x0386 }, ++ { 0x07A2, 0x0388 }, ++ { 0x07A3, 0x0389 }, ++ { 0x07A4, 0x038A }, ++ { 0x07A5, 0x03AA }, ++ { 0x07A7, 0x038C }, ++ { 0x07A8, 0x038E }, ++ { 0x07A9, 0x03AB }, ++ { 0x07AB, 0x038F }, ++ { 0x07AE, 0x0385 }, ++ { 0x07AF, 0x2015 }, ++ { 0x07B1, 0x03AC }, ++ { 0x07B2, 0x03AD }, ++ { 0x07B3, 0x03AE }, ++ { 0x07B4, 0x03AF }, ++ { 0x07B5, 0x03CA }, ++ { 0x07B6, 0x0390 }, ++ { 0x07B7, 0x03CC }, ++ { 0x07B8, 0x03CD }, ++ { 0x07B9, 0x03CB }, ++ { 0x07BA, 0x03B0 }, ++ { 0x07BB, 0x03CE }, ++ { 0x07C1, 0x0391 }, ++ { 0x07C2, 0x0392 }, ++ { 0x07C3, 0x0393 }, ++ { 0x07C4, 0x0394 }, ++ { 0x07C5, 0x0395 }, ++ { 0x07C6, 0x0396 }, ++ { 0x07C7, 0x0397 }, ++ { 0x07C8, 0x0398 }, ++ { 0x07C9, 0x0399 }, ++ { 0x07CA, 0x039A }, ++ { 0x07CB, 0x039B }, ++ { 0x07CC, 0x039C }, ++ { 0x07CD, 0x039D }, ++ { 0x07CE, 0x039E }, ++ { 0x07CF, 0x039F }, ++ { 0x07D0, 0x03A0 }, ++ { 0x07D1, 0x03A1 }, ++ { 0x07D2, 0x03A3 }, ++ { 0x07D4, 0x03A4 }, ++ { 0x07D5, 0x03A5 }, ++ { 0x07D6, 0x03A6 }, ++ { 0x07D7, 0x03A7 }, ++ { 0x07D8, 0x03A8 }, ++ { 0x07D9, 0x03A9 }, ++ { 0x07E1, 0x03B1 }, ++ { 0x07E2, 0x03B2 }, ++ { 0x07E3, 0x03B3 }, ++ { 0x07E4, 0x03B4 }, ++ { 0x07E5, 0x03B5 }, ++ { 0x07E6, 0x03B6 }, ++ { 0x07E7, 0x03B7 }, ++ { 0x07E8, 0x03B8 }, ++ { 0x07E9, 0x03B9 }, ++ { 0x07EA, 0x03BA }, ++ { 0x07EB, 0x03BB }, ++ { 0x07EC, 0x03BC }, ++ { 0x07ED, 0x03BD }, ++ { 0x07EE, 0x03BE }, ++ { 0x07EF, 0x03BF }, ++ { 0x07F0, 0x03C0 }, ++ { 0x07F1, 0x03C1 }, ++ { 0x07F2, 0x03C3 }, ++ { 0x07F3, 0x03C2 }, ++ { 0x07F4, 0x03C4 }, ++ { 0x07F5, 0x03C5 }, ++ { 0x07F6, 0x03C6 }, ++ { 0x07F7, 0x03C7 }, ++ { 0x07F8, 0x03C8 }, ++ { 0x07F9, 0x03C9 }, ++ { 0x08A1, 0x23B7 }, ++ { 0x08A2, 0x250C }, ++ { 0x08A3, 0x2500 }, ++ { 0x08A4, 0x2320 }, ++ { 0x08A5, 0x2321 }, ++ { 0x08A6, 0x2502 }, ++ { 0x08A7, 0x23A1 }, ++ { 0x08A8, 0x23A3 }, ++ { 0x08A9, 0x23A4 }, ++ { 0x08AA, 0x23A6 }, ++ { 0x08AB, 0x239B }, ++ { 0x08AC, 0x239D }, ++ { 0x08AD, 0x239E }, ++ { 0x08AE, 0x23A0 }, ++ { 0x08AF, 0x23A8 }, ++ { 0x08B0, 0x23AC }, ++ { 0x08BC, 0x2264 }, ++ { 0x08BD, 0x2260 }, ++ { 0x08BE, 0x2265 }, ++ { 0x08BF, 0x222B }, ++ { 0x08C0, 0x2234 }, ++ { 0x08C1, 0x221D }, ++ { 0x08C2, 0x221E }, ++ { 0x08C5, 0x2207 }, ++ { 0x08C8, 0x223C }, ++ { 0x08C9, 0x2243 }, ++ { 0x08CD, 0x21D4 }, ++ { 0x08CE, 0x21D2 }, ++ { 0x08CF, 0x2261 }, ++ { 0x08D6, 0x221A }, ++ { 0x08DA, 0x2282 }, ++ { 0x08DB, 0x2283 }, ++ { 0x08DC, 0x2229 }, ++ { 0x08DD, 0x222A }, ++ { 0x08DE, 0x2227 }, ++ { 0x08DF, 0x2228 }, ++ { 0x08EF, 0x2202 }, ++ { 0x08F6, 0x0192 }, ++ { 0x08FB, 0x2190 }, ++ { 0x08FC, 0x2191 }, ++ { 0x08FD, 0x2192 }, ++ { 0x08FE, 0x2193 }, ++ { 0x09E0, 0x25C6 }, ++ { 0x09E1, 0x2592 }, ++ { 0x09E2, 0x2409 }, ++ { 0x09E3, 0x240C }, ++ { 0x09E4, 0x240D }, ++ { 0x09E5, 0x240A }, ++ { 0x09E8, 0x2424 }, ++ { 0x09E9, 0x240B }, ++ { 0x09EA, 0x2518 }, ++ { 0x09EB, 0x2510 }, ++ { 0x09EC, 0x250C }, ++ { 0x09ED, 0x2514 }, ++ { 0x09EE, 0x253C }, ++ { 0x09EF, 0x23BA }, ++ { 0x09F0, 0x23BB }, ++ { 0x09F1, 0x2500 }, ++ { 0x09F2, 0x23BC }, ++ { 0x09F3, 0x23BD }, ++ { 0x09F4, 0x251C }, ++ { 0x09F5, 0x2524 }, ++ { 0x09F6, 0x2534 }, ++ { 0x09F7, 0x252C }, ++ { 0x09F8, 0x2502 }, ++ { 0x0AA1, 0x2003 }, ++ { 0x0AA2, 0x2002 }, ++ { 0x0AA3, 0x2004 }, ++ { 0x0AA4, 0x2005 }, ++ { 0x0AA5, 0x2007 }, ++ { 0x0AA6, 0x2008 }, ++ { 0x0AA7, 0x2009 }, ++ { 0x0AA8, 0x200A }, ++ { 0x0AA9, 0x2014 }, ++ { 0x0AAA, 0x2013 }, ++ { 0x0AAE, 0x2026 }, ++ { 0x0AAF, 0x2025 }, ++ { 0x0AB0, 0x2153 }, ++ { 0x0AB1, 0x2154 }, ++ { 0x0AB2, 0x2155 }, ++ { 0x0AB3, 0x2156 }, ++ { 0x0AB4, 0x2157 }, ++ { 0x0AB5, 0x2158 }, ++ { 0x0AB6, 0x2159 }, ++ { 0x0AB7, 0x215A }, ++ { 0x0AB8, 0x2105 }, ++ { 0x0ABB, 0x2012 }, ++ { 0x0ABC, 0x2329 }, ++ { 0x0ABE, 0x232A }, ++ { 0x0AC3, 0x215B }, ++ { 0x0AC4, 0x215C }, ++ { 0x0AC5, 0x215D }, ++ { 0x0AC6, 0x215E }, ++ { 0x0AC9, 0x2122 }, ++ { 0x0ACA, 0x2613 }, ++ { 0x0ACC, 0x25C1 }, ++ { 0x0ACD, 0x25B7 }, ++ { 0x0ACE, 0x25CB }, ++ { 0x0ACF, 0x25AF }, ++ { 0x0AD0, 0x2018 }, ++ { 0x0AD1, 0x2019 }, ++ { 0x0AD2, 0x201C }, ++ { 0x0AD3, 0x201D }, ++ { 0x0AD4, 0x211E }, ++ { 0x0AD6, 0x2032 }, ++ { 0x0AD7, 0x2033 }, ++ { 0x0AD9, 0x271D }, ++ { 0x0ADB, 0x25AC }, ++ { 0x0ADC, 0x25C0 }, ++ { 0x0ADD, 0x25B6 }, ++ { 0x0ADE, 0x25CF }, ++ { 0x0ADF, 0x25AE }, ++ { 0x0AE0, 0x25E6 }, ++ { 0x0AE1, 0x25AB }, ++ { 0x0AE2, 0x25AD }, ++ { 0x0AE3, 0x25B3 }, ++ { 0x0AE4, 0x25BD }, ++ { 0x0AE5, 0x2606 }, ++ { 0x0AE6, 0x2022 }, ++ { 0x0AE7, 0x25AA }, ++ { 0x0AE8, 0x25B2 }, ++ { 0x0AE9, 0x25BC }, ++ { 0x0AEA, 0x261C }, ++ { 0x0AEB, 0x261E }, ++ { 0x0AEC, 0x2663 }, ++ { 0x0AED, 0x2666 }, ++ { 0x0AEE, 0x2665 }, ++ { 0x0AF0, 0x2720 }, ++ { 0x0AF1, 0x2020 }, ++ { 0x0AF2, 0x2021 }, ++ { 0x0AF3, 0x2713 }, ++ { 0x0AF4, 0x2717 }, ++ { 0x0AF5, 0x266F }, ++ { 0x0AF6, 0x266D }, ++ { 0x0AF7, 0x2642 }, ++ { 0x0AF8, 0x2640 }, ++ { 0x0AF9, 0x260E }, ++ { 0x0AFA, 0x2315 }, ++ { 0x0AFB, 0x2117 }, ++ { 0x0AFC, 0x2038 }, ++ { 0x0AFD, 0x201A }, ++ { 0x0AFE, 0x201E }, ++ { 0x0BA3, 0x003C }, ++ { 0x0BA6, 0x003E }, ++ { 0x0BA8, 0x2228 }, ++ { 0x0BA9, 0x2227 }, ++ { 0x0BC0, 0x00AF }, ++ { 0x0BC2, 0x22A5 }, ++ { 0x0BC3, 0x2229 }, ++ { 0x0BC4, 0x230A }, ++ { 0x0BC6, 0x005F }, ++ { 0x0BCA, 0x2218 }, ++ { 0x0BCC, 0x2395 }, ++ { 0x0BCE, 0x22A4 }, ++ { 0x0BCF, 0x25CB }, ++ { 0x0BD3, 0x2308 }, ++ { 0x0BD6, 0x222A }, ++ { 0x0BD8, 0x2283 }, ++ { 0x0BDA, 0x2282 }, ++ { 0x0BDC, 0x22A2 }, ++ { 0x0BFC, 0x22A3 }, ++ { 0x0CDF, 0x2017 }, ++ { 0x0CE0, 0x05D0 }, ++ { 0x0CE1, 0x05D1 }, ++ { 0x0CE2, 0x05D2 }, ++ { 0x0CE3, 0x05D3 }, ++ { 0x0CE4, 0x05D4 }, ++ { 0x0CE5, 0x05D5 }, ++ { 0x0CE6, 0x05D6 }, ++ { 0x0CE7, 0x05D7 }, ++ { 0x0CE8, 0x05D8 }, ++ { 0x0CE9, 0x05D9 }, ++ { 0x0CEA, 0x05DA }, ++ { 0x0CEB, 0x05DB }, ++ { 0x0CEC, 0x05DC }, ++ { 0x0CED, 0x05DD }, ++ { 0x0CEE, 0x05DE }, ++ { 0x0CEF, 0x05DF }, ++ { 0x0CF0, 0x05E0 }, ++ { 0x0CF1, 0x05E1 }, ++ { 0x0CF2, 0x05E2 }, ++ { 0x0CF3, 0x05E3 }, ++ { 0x0CF4, 0x05E4 }, ++ { 0x0CF5, 0x05E5 }, ++ { 0x0CF6, 0x05E6 }, ++ { 0x0CF7, 0x05E7 }, ++ { 0x0CF8, 0x05E8 }, ++ { 0x0CF9, 0x05E9 }, ++ { 0x0CFA, 0x05EA }, ++ { 0x0DA1, 0x0E01 }, ++ { 0x0DA2, 0x0E02 }, ++ { 0x0DA3, 0x0E03 }, ++ { 0x0DA4, 0x0E04 }, ++ { 0x0DA5, 0x0E05 }, ++ { 0x0DA6, 0x0E06 }, ++ { 0x0DA7, 0x0E07 }, ++ { 0x0DA8, 0x0E08 }, ++ { 0x0DA9, 0x0E09 }, ++ { 0x0DAA, 0x0E0A }, ++ { 0x0DAB, 0x0E0B }, ++ { 0x0DAC, 0x0E0C }, ++ { 0x0DAD, 0x0E0D }, ++ { 0x0DAE, 0x0E0E }, ++ { 0x0DAF, 0x0E0F }, ++ { 0x0DB0, 0x0E10 }, ++ { 0x0DB1, 0x0E11 }, ++ { 0x0DB2, 0x0E12 }, ++ { 0x0DB3, 0x0E13 }, ++ { 0x0DB4, 0x0E14 }, ++ { 0x0DB5, 0x0E15 }, ++ { 0x0DB6, 0x0E16 }, ++ { 0x0DB7, 0x0E17 }, ++ { 0x0DB8, 0x0E18 }, ++ { 0x0DB9, 0x0E19 }, ++ { 0x0DBA, 0x0E1A }, ++ { 0x0DBB, 0x0E1B }, ++ { 0x0DBC, 0x0E1C }, ++ { 0x0DBD, 0x0E1D }, ++ { 0x0DBE, 0x0E1E }, ++ { 0x0DBF, 0x0E1F }, ++ { 0x0DC0, 0x0E20 }, ++ { 0x0DC1, 0x0E21 }, ++ { 0x0DC2, 0x0E22 }, ++ { 0x0DC3, 0x0E23 }, ++ { 0x0DC4, 0x0E24 }, ++ { 0x0DC5, 0x0E25 }, ++ { 0x0DC6, 0x0E26 }, ++ { 0x0DC7, 0x0E27 }, ++ { 0x0DC8, 0x0E28 }, ++ { 0x0DC9, 0x0E29 }, ++ { 0x0DCA, 0x0E2A }, ++ { 0x0DCB, 0x0E2B }, ++ { 0x0DCC, 0x0E2C }, ++ { 0x0DCD, 0x0E2D }, ++ { 0x0DCE, 0x0E2E }, ++ { 0x0DCF, 0x0E2F }, ++ { 0x0DD0, 0x0E30 }, ++ { 0x0DD1, 0x0E31 }, ++ { 0x0DD2, 0x0E32 }, ++ { 0x0DD3, 0x0E33 }, ++ { 0x0DD4, 0x0E34 }, ++ { 0x0DD5, 0x0E35 }, ++ { 0x0DD6, 0x0E36 }, ++ { 0x0DD7, 0x0E37 }, ++ { 0x0DD8, 0x0E38 }, ++ { 0x0DD9, 0x0E39 }, ++ { 0x0DDA, 0x0E3A }, ++ { 0x0DDF, 0x0E3F }, ++ { 0x0DE0, 0x0E40 }, ++ { 0x0DE1, 0x0E41 }, ++ { 0x0DE2, 0x0E42 }, ++ { 0x0DE3, 0x0E43 }, ++ { 0x0DE4, 0x0E44 }, ++ { 0x0DE5, 0x0E45 }, ++ { 0x0DE6, 0x0E46 }, ++ { 0x0DE7, 0x0E47 }, ++ { 0x0DE8, 0x0E48 }, ++ { 0x0DE9, 0x0E49 }, ++ { 0x0DEA, 0x0E4A }, ++ { 0x0DEB, 0x0E4B }, ++ { 0x0DEC, 0x0E4C }, ++ { 0x0DED, 0x0E4D }, ++ { 0x0DF0, 0x0E50 }, ++ { 0x0DF1, 0x0E51 }, ++ { 0x0DF2, 0x0E52 }, ++ { 0x0DF3, 0x0E53 }, ++ { 0x0DF4, 0x0E54 }, ++ { 0x0DF5, 0x0E55 }, ++ { 0x0DF6, 0x0E56 }, ++ { 0x0DF7, 0x0E57 }, ++ { 0x0DF8, 0x0E58 }, ++ { 0x0DF9, 0x0E59 }, ++ { 0x0EA1, 0x3131 }, ++ { 0x0EA2, 0x3132 }, ++ { 0x0EA3, 0x3133 }, ++ { 0x0EA4, 0x3134 }, ++ { 0x0EA5, 0x3135 }, ++ { 0x0EA6, 0x3136 }, ++ { 0x0EA7, 0x3137 }, ++ { 0x0EA8, 0x3138 }, ++ { 0x0EA9, 0x3139 }, ++ { 0x0EAA, 0x313A }, ++ { 0x0EAB, 0x313B }, ++ { 0x0EAC, 0x313C }, ++ { 0x0EAD, 0x313D }, ++ { 0x0EAE, 0x313E }, ++ { 0x0EAF, 0x313F }, ++ { 0x0EB0, 0x3140 }, ++ { 0x0EB1, 0x3141 }, ++ { 0x0EB2, 0x3142 }, ++ { 0x0EB3, 0x3143 }, ++ { 0x0EB4, 0x3144 }, ++ { 0x0EB5, 0x3145 }, ++ { 0x0EB6, 0x3146 }, ++ { 0x0EB7, 0x3147 }, ++ { 0x0EB8, 0x3148 }, ++ { 0x0EB9, 0x3149 }, ++ { 0x0EBA, 0x314A }, ++ { 0x0EBB, 0x314B }, ++ { 0x0EBC, 0x314C }, ++ { 0x0EBD, 0x314D }, ++ { 0x0EBE, 0x314E }, ++ { 0x0EBF, 0x314F }, ++ { 0x0EC0, 0x3150 }, ++ { 0x0EC1, 0x3151 }, ++ { 0x0EC2, 0x3152 }, ++ { 0x0EC3, 0x3153 }, ++ { 0x0EC4, 0x3154 }, ++ { 0x0EC5, 0x3155 }, ++ { 0x0EC6, 0x3156 }, ++ { 0x0EC7, 0x3157 }, ++ { 0x0EC8, 0x3158 }, ++ { 0x0EC9, 0x3159 }, ++ { 0x0ECA, 0x315A }, ++ { 0x0ECB, 0x315B }, ++ { 0x0ECC, 0x315C }, ++ { 0x0ECD, 0x315D }, ++ { 0x0ECE, 0x315E }, ++ { 0x0ECF, 0x315F }, ++ { 0x0ED0, 0x3160 }, ++ { 0x0ED1, 0x3161 }, ++ { 0x0ED2, 0x3162 }, ++ { 0x0ED3, 0x3163 }, ++ { 0x0ED4, 0x11A8 }, ++ { 0x0ED5, 0x11A9 }, ++ { 0x0ED6, 0x11AA }, ++ { 0x0ED7, 0x11AB }, ++ { 0x0ED8, 0x11AC }, ++ { 0x0ED9, 0x11AD }, ++ { 0x0EDA, 0x11AE }, ++ { 0x0EDB, 0x11AF }, ++ { 0x0EDC, 0x11B0 }, ++ { 0x0EDD, 0x11B1 }, ++ { 0x0EDE, 0x11B2 }, ++ { 0x0EDF, 0x11B3 }, ++ { 0x0EE0, 0x11B4 }, ++ { 0x0EE1, 0x11B5 }, ++ { 0x0EE2, 0x11B6 }, ++ { 0x0EE3, 0x11B7 }, ++ { 0x0EE4, 0x11B8 }, ++ { 0x0EE5, 0x11B9 }, ++ { 0x0EE6, 0x11BA }, ++ { 0x0EE7, 0x11BB }, ++ { 0x0EE8, 0x11BC }, ++ { 0x0EE9, 0x11BD }, ++ { 0x0EEA, 0x11BE }, ++ { 0x0EEB, 0x11BF }, ++ { 0x0EEC, 0x11C0 }, ++ { 0x0EED, 0x11C1 }, ++ { 0x0EEE, 0x11C2 }, ++ { 0x0EEF, 0x316D }, ++ { 0x0EF0, 0x3171 }, ++ { 0x0EF1, 0x3178 }, ++ { 0x0EF2, 0x317F }, ++ { 0x0EF3, 0x3181 }, ++ { 0x0EF4, 0x3184 }, ++ { 0x0EF5, 0x3186 }, ++ { 0x0EF6, 0x318D }, ++ { 0x0EF7, 0x318E }, ++ { 0x0EF8, 0x11EB }, ++ { 0x0EF9, 0x11F0 }, ++ { 0x0EFA, 0x11F9 }, ++ { 0x0EFF, 0x20A9 }, ++ { 0x13A4, 0x20AC }, ++ { 0x13BC, 0x0152 }, ++ { 0x13BD, 0x0153 }, ++ { 0x13BE, 0x0178 }, ++ { 0x20AC, 0x20AC }, ++}; ++ ++unsigned int KeyMappingX11::get_unicode_from_keysym(KeySym p_keysym) { ++ /* Latin-1 */ ++ if (p_keysym >= 0x20 && p_keysym <= 0x7e) { ++ return p_keysym; ++ } ++ if (p_keysym >= 0xa0 && p_keysym <= 0xff) { ++ return p_keysym; ++ } ++ // keypad to latin1 is easy ++ if (p_keysym >= 0xffaa && p_keysym <= 0xffb9) { ++ return p_keysym - 0xff80; ++ } ++ ++ /* Unicode (may be present)*/ ++ ++ if ((p_keysym & 0xff000000) == 0x01000000) { ++ return p_keysym & 0x00ffffff; ++ } ++ ++ int middle, low = 0, high = _KEYSYM_MAX - 1; ++ do { ++ middle = (high + low) / 2; ++ if (_xkeysym_to_unicode[middle].keysym == p_keysym) { ++ return _xkeysym_to_unicode[middle].unicode; ++ } ++ if (_xkeysym_to_unicode[middle].keysym <= p_keysym) { ++ low = middle + 1; ++ } else { ++ high = middle - 1; ++ } ++ } while (high >= low); ++ ++ return 0; ++} ++ ++struct _XTranslateUnicodePairReverse { ++ unsigned int unicode; ++ KeySym keysym; ++}; ++ ++enum { ++ ++ _UNICODE_MAX = 750 ++}; ++ ++static _XTranslateUnicodePairReverse _unicode_to_xkeysym[_UNICODE_MAX] = { ++ { 0x0ABD, 0x002E }, ++ { 0x0BA3, 0x003C }, ++ { 0x0BA6, 0x003E }, ++ { 0x0BC6, 0x005F }, ++ { 0x0BC0, 0x00AF }, ++ { 0x03C0, 0x0100 }, ++ { 0x03E0, 0x0101 }, ++ { 0x01C3, 0x0102 }, ++ { 0x01E3, 0x0103 }, ++ { 0x01A1, 0x0104 }, ++ { 0x01B1, 0x0105 }, ++ { 0x01C6, 0x0106 }, ++ { 0x01E6, 0x0107 }, ++ { 0x02C6, 0x0108 }, ++ { 0x02E6, 0x0109 }, ++ { 0x02C5, 0x010A }, ++ { 0x02E5, 0x010B }, ++ { 0x01C8, 0x010C }, ++ { 0x01E8, 0x010D }, ++ { 0x01CF, 0x010E }, ++ { 0x01EF, 0x010F }, ++ { 0x01D0, 0x0110 }, ++ { 0x01F0, 0x0111 }, ++ { 0x03AA, 0x0112 }, ++ { 0x03BA, 0x0113 }, ++ { 0x03CC, 0x0116 }, ++ { 0x03EC, 0x0117 }, ++ { 0x01CA, 0x0118 }, ++ { 0x01EA, 0x0119 }, ++ { 0x01CC, 0x011A }, ++ { 0x01EC, 0x011B }, ++ { 0x02D8, 0x011C }, ++ { 0x02F8, 0x011D }, ++ { 0x02AB, 0x011E }, ++ { 0x02BB, 0x011F }, ++ { 0x02D5, 0x0120 }, ++ { 0x02F5, 0x0121 }, ++ { 0x03AB, 0x0122 }, ++ { 0x03BB, 0x0123 }, ++ { 0x02A6, 0x0124 }, ++ { 0x02B6, 0x0125 }, ++ { 0x02A1, 0x0126 }, ++ { 0x02B1, 0x0127 }, ++ { 0x03A5, 0x0128 }, ++ { 0x03B5, 0x0129 }, ++ { 0x03CF, 0x012A }, ++ { 0x03EF, 0x012B }, ++ { 0x03C7, 0x012E }, ++ { 0x03E7, 0x012F }, ++ { 0x02A9, 0x0130 }, ++ { 0x02B9, 0x0131 }, ++ { 0x02AC, 0x0134 }, ++ { 0x02BC, 0x0135 }, ++ { 0x03D3, 0x0136 }, ++ { 0x03F3, 0x0137 }, ++ { 0x03A2, 0x0138 }, ++ { 0x01C5, 0x0139 }, ++ { 0x01E5, 0x013A }, ++ { 0x03A6, 0x013B }, ++ { 0x03B6, 0x013C }, ++ { 0x01A5, 0x013D }, ++ { 0x01B5, 0x013E }, ++ { 0x01A3, 0x0141 }, ++ { 0x01B3, 0x0142 }, ++ { 0x01D1, 0x0143 }, ++ { 0x01F1, 0x0144 }, ++ { 0x03D1, 0x0145 }, ++ { 0x03F1, 0x0146 }, ++ { 0x01D2, 0x0147 }, ++ { 0x01F2, 0x0148 }, ++ { 0x03BD, 0x014A }, ++ { 0x03BF, 0x014B }, ++ { 0x03D2, 0x014C }, ++ { 0x03F2, 0x014D }, ++ { 0x01D5, 0x0150 }, ++ { 0x01F5, 0x0151 }, ++ { 0x13BC, 0x0152 }, ++ { 0x13BD, 0x0153 }, ++ { 0x01C0, 0x0154 }, ++ { 0x01E0, 0x0155 }, ++ { 0x03A3, 0x0156 }, ++ { 0x03B3, 0x0157 }, ++ { 0x01D8, 0x0158 }, ++ { 0x01F8, 0x0159 }, ++ { 0x01A6, 0x015A }, ++ { 0x01B6, 0x015B }, ++ { 0x02DE, 0x015C }, ++ { 0x02FE, 0x015D }, ++ { 0x01AA, 0x015E }, ++ { 0x01BA, 0x015F }, ++ { 0x01A9, 0x0160 }, ++ { 0x01B9, 0x0161 }, ++ { 0x01DE, 0x0162 }, ++ { 0x01FE, 0x0163 }, ++ { 0x01AB, 0x0164 }, ++ { 0x01BB, 0x0165 }, ++ { 0x03AC, 0x0166 }, ++ { 0x03BC, 0x0167 }, ++ { 0x03DD, 0x0168 }, ++ { 0x03FD, 0x0169 }, ++ { 0x03DE, 0x016A }, ++ { 0x03FE, 0x016B }, ++ { 0x02DD, 0x016C }, ++ { 0x02FD, 0x016D }, ++ { 0x01D9, 0x016E }, ++ { 0x01F9, 0x016F }, ++ { 0x01DB, 0x0170 }, ++ { 0x01FB, 0x0171 }, ++ { 0x03D9, 0x0172 }, ++ { 0x03F9, 0x0173 }, ++ { 0x13BE, 0x0178 }, ++ { 0x01AC, 0x0179 }, ++ { 0x01BC, 0x017A }, ++ { 0x01AF, 0x017B }, ++ { 0x01BF, 0x017C }, ++ { 0x01AE, 0x017D }, ++ { 0x01BE, 0x017E }, ++ { 0x08F6, 0x0192 }, ++ { 0x01B7, 0x02C7 }, ++ { 0x01A2, 0x02D8 }, ++ { 0x01FF, 0x02D9 }, ++ { 0x01B2, 0x02DB }, ++ { 0x01BD, 0x02DD }, ++ { 0x07AE, 0x0385 }, ++ { 0x07A1, 0x0386 }, ++ { 0x07A2, 0x0388 }, ++ { 0x07A3, 0x0389 }, ++ { 0x07A4, 0x038A }, ++ { 0x07A7, 0x038C }, ++ { 0x07A8, 0x038E }, ++ { 0x07AB, 0x038F }, ++ { 0x07B6, 0x0390 }, ++ { 0x07C1, 0x0391 }, ++ { 0x07C2, 0x0392 }, ++ { 0x07C3, 0x0393 }, ++ { 0x07C4, 0x0394 }, ++ { 0x07C5, 0x0395 }, ++ { 0x07C6, 0x0396 }, ++ { 0x07C7, 0x0397 }, ++ { 0x07C8, 0x0398 }, ++ { 0x07C9, 0x0399 }, ++ { 0x07CA, 0x039A }, ++ { 0x07CB, 0x039B }, ++ { 0x07CC, 0x039C }, ++ { 0x07CD, 0x039D }, ++ { 0x07CE, 0x039E }, ++ { 0x07CF, 0x039F }, ++ { 0x07D0, 0x03A0 }, ++ { 0x07D1, 0x03A1 }, ++ { 0x07D2, 0x03A3 }, ++ { 0x07D4, 0x03A4 }, ++ { 0x07D5, 0x03A5 }, ++ { 0x07D6, 0x03A6 }, ++ { 0x07D7, 0x03A7 }, ++ { 0x07D8, 0x03A8 }, ++ { 0x07D9, 0x03A9 }, ++ { 0x07A5, 0x03AA }, ++ { 0x07A9, 0x03AB }, ++ { 0x07B1, 0x03AC }, ++ { 0x07B2, 0x03AD }, ++ { 0x07B3, 0x03AE }, ++ { 0x07B4, 0x03AF }, ++ { 0x07BA, 0x03B0 }, ++ { 0x07E1, 0x03B1 }, ++ { 0x07E2, 0x03B2 }, ++ { 0x07E3, 0x03B3 }, ++ { 0x07E4, 0x03B4 }, ++ { 0x07E5, 0x03B5 }, ++ { 0x07E6, 0x03B6 }, ++ { 0x07E7, 0x03B7 }, ++ { 0x07E8, 0x03B8 }, ++ { 0x07E9, 0x03B9 }, ++ { 0x07EA, 0x03BA }, ++ { 0x07EB, 0x03BB }, ++ { 0x07EC, 0x03BC }, ++ { 0x07ED, 0x03BD }, ++ { 0x07EE, 0x03BE }, ++ { 0x07EF, 0x03BF }, ++ { 0x07F0, 0x03C0 }, ++ { 0x07F1, 0x03C1 }, ++ { 0x07F3, 0x03C2 }, ++ { 0x07F2, 0x03C3 }, ++ { 0x07F4, 0x03C4 }, ++ { 0x07F5, 0x03C5 }, ++ { 0x07F6, 0x03C6 }, ++ { 0x07F7, 0x03C7 }, ++ { 0x07F8, 0x03C8 }, ++ { 0x07F9, 0x03C9 }, ++ { 0x07B5, 0x03CA }, ++ { 0x07B9, 0x03CB }, ++ { 0x07B7, 0x03CC }, ++ { 0x07B8, 0x03CD }, ++ { 0x07BB, 0x03CE }, ++ { 0x06B3, 0x0401 }, ++ { 0x06B1, 0x0402 }, ++ { 0x06B2, 0x0403 }, ++ { 0x06B4, 0x0404 }, ++ { 0x06B5, 0x0405 }, ++ { 0x06B6, 0x0406 }, ++ { 0x06B7, 0x0407 }, ++ { 0x06B8, 0x0408 }, ++ { 0x06B9, 0x0409 }, ++ { 0x06BA, 0x040A }, ++ { 0x06BB, 0x040B }, ++ { 0x06BC, 0x040C }, ++ { 0x06BE, 0x040E }, ++ { 0x06BF, 0x040F }, ++ { 0x06E1, 0x0410 }, ++ { 0x06E2, 0x0411 }, ++ { 0x06F7, 0x0412 }, ++ { 0x06E7, 0x0413 }, ++ { 0x06E4, 0x0414 }, ++ { 0x06E5, 0x0415 }, ++ { 0x06F6, 0x0416 }, ++ { 0x06FA, 0x0417 }, ++ { 0x06E9, 0x0418 }, ++ { 0x06EA, 0x0419 }, ++ { 0x06EB, 0x041A }, ++ { 0x06EC, 0x041B }, ++ { 0x06ED, 0x041C }, ++ { 0x06EE, 0x041D }, ++ { 0x06EF, 0x041E }, ++ { 0x06F0, 0x041F }, ++ { 0x06F2, 0x0420 }, ++ { 0x06F3, 0x0421 }, ++ { 0x06F4, 0x0422 }, ++ { 0x06F5, 0x0423 }, ++ { 0x06E6, 0x0424 }, ++ { 0x06E8, 0x0425 }, ++ { 0x06E3, 0x0426 }, ++ { 0x06FE, 0x0427 }, ++ { 0x06FB, 0x0428 }, ++ { 0x06FD, 0x0429 }, ++ { 0x06FF, 0x042A }, ++ { 0x06F9, 0x042B }, ++ { 0x06F8, 0x042C }, ++ { 0x06FC, 0x042D }, ++ { 0x06E0, 0x042E }, ++ { 0x06F1, 0x042F }, ++ { 0x06C1, 0x0430 }, ++ { 0x06C2, 0x0431 }, ++ { 0x06D7, 0x0432 }, ++ { 0x06C7, 0x0433 }, ++ { 0x06C4, 0x0434 }, ++ { 0x06C5, 0x0435 }, ++ { 0x06D6, 0x0436 }, ++ { 0x06DA, 0x0437 }, ++ { 0x06C9, 0x0438 }, ++ { 0x06CA, 0x0439 }, ++ { 0x06CB, 0x043A }, ++ { 0x06CC, 0x043B }, ++ { 0x06CD, 0x043C }, ++ { 0x06CE, 0x043D }, ++ { 0x06CF, 0x043E }, ++ { 0x06D0, 0x043F }, ++ { 0x06D2, 0x0440 }, ++ { 0x06D3, 0x0441 }, ++ { 0x06D4, 0x0442 }, ++ { 0x06D5, 0x0443 }, ++ { 0x06C6, 0x0444 }, ++ { 0x06C8, 0x0445 }, ++ { 0x06C3, 0x0446 }, ++ { 0x06DE, 0x0447 }, ++ { 0x06DB, 0x0448 }, ++ { 0x06DD, 0x0449 }, ++ { 0x06DF, 0x044A }, ++ { 0x06D9, 0x044B }, ++ { 0x06D8, 0x044C }, ++ { 0x06DC, 0x044D }, ++ { 0x06C0, 0x044E }, ++ { 0x06D1, 0x044F }, ++ { 0x06A3, 0x0451 }, ++ { 0x06A1, 0x0452 }, ++ { 0x06A2, 0x0453 }, ++ { 0x06A4, 0x0454 }, ++ { 0x06A5, 0x0455 }, ++ { 0x06A6, 0x0456 }, ++ { 0x06A7, 0x0457 }, ++ { 0x06A8, 0x0458 }, ++ { 0x06A9, 0x0459 }, ++ { 0x06AA, 0x045A }, ++ { 0x06AB, 0x045B }, ++ { 0x06AC, 0x045C }, ++ { 0x06AE, 0x045E }, ++ { 0x06AF, 0x045F }, ++ { 0x0CE0, 0x05D0 }, ++ { 0x0CE1, 0x05D1 }, ++ { 0x0CE2, 0x05D2 }, ++ { 0x0CE3, 0x05D3 }, ++ { 0x0CE4, 0x05D4 }, ++ { 0x0CE5, 0x05D5 }, ++ { 0x0CE6, 0x05D6 }, ++ { 0x0CE7, 0x05D7 }, ++ { 0x0CE8, 0x05D8 }, ++ { 0x0CE9, 0x05D9 }, ++ { 0x0CEA, 0x05DA }, ++ { 0x0CEB, 0x05DB }, ++ { 0x0CEC, 0x05DC }, ++ { 0x0CED, 0x05DD }, ++ { 0x0CEE, 0x05DE }, ++ { 0x0CEF, 0x05DF }, ++ { 0x0CF0, 0x05E0 }, ++ { 0x0CF1, 0x05E1 }, ++ { 0x0CF2, 0x05E2 }, ++ { 0x0CF3, 0x05E3 }, ++ { 0x0CF4, 0x05E4 }, ++ { 0x0CF5, 0x05E5 }, ++ { 0x0CF6, 0x05E6 }, ++ { 0x0CF7, 0x05E7 }, ++ { 0x0CF8, 0x05E8 }, ++ { 0x0CF9, 0x05E9 }, ++ { 0x0CFA, 0x05EA }, ++ { 0x05AC, 0x060C }, ++ { 0x05BB, 0x061B }, ++ { 0x05BF, 0x061F }, ++ { 0x05C1, 0x0621 }, ++ { 0x05C2, 0x0622 }, ++ { 0x05C3, 0x0623 }, ++ { 0x05C4, 0x0624 }, ++ { 0x05C5, 0x0625 }, ++ { 0x05C6, 0x0626 }, ++ { 0x05C7, 0x0627 }, ++ { 0x05C8, 0x0628 }, ++ { 0x05C9, 0x0629 }, ++ { 0x05CA, 0x062A }, ++ { 0x05CB, 0x062B }, ++ { 0x05CC, 0x062C }, ++ { 0x05CD, 0x062D }, ++ { 0x05CE, 0x062E }, ++ { 0x05CF, 0x062F }, ++ { 0x05D0, 0x0630 }, ++ { 0x05D1, 0x0631 }, ++ { 0x05D2, 0x0632 }, ++ { 0x05D3, 0x0633 }, ++ { 0x05D4, 0x0634 }, ++ { 0x05D5, 0x0635 }, ++ { 0x05D6, 0x0636 }, ++ { 0x05D7, 0x0637 }, ++ { 0x05D8, 0x0638 }, ++ { 0x05D9, 0x0639 }, ++ { 0x05DA, 0x063A }, ++ { 0x05E0, 0x0640 }, ++ { 0x05E1, 0x0641 }, ++ { 0x05E2, 0x0642 }, ++ { 0x05E3, 0x0643 }, ++ { 0x05E4, 0x0644 }, ++ { 0x05E5, 0x0645 }, ++ { 0x05E6, 0x0646 }, ++ { 0x05E7, 0x0647 }, ++ { 0x05E8, 0x0648 }, ++ { 0x05E9, 0x0649 }, ++ { 0x05EA, 0x064A }, ++ { 0x05EB, 0x064B }, ++ { 0x05EC, 0x064C }, ++ { 0x05ED, 0x064D }, ++ { 0x05EE, 0x064E }, ++ { 0x05EF, 0x064F }, ++ { 0x05F0, 0x0650 }, ++ { 0x05F1, 0x0651 }, ++ { 0x05F2, 0x0652 }, ++ { 0x0DA1, 0x0E01 }, ++ { 0x0DA2, 0x0E02 }, ++ { 0x0DA3, 0x0E03 }, ++ { 0x0DA4, 0x0E04 }, ++ { 0x0DA5, 0x0E05 }, ++ { 0x0DA6, 0x0E06 }, ++ { 0x0DA7, 0x0E07 }, ++ { 0x0DA8, 0x0E08 }, ++ { 0x0DA9, 0x0E09 }, ++ { 0x0DAA, 0x0E0A }, ++ { 0x0DAB, 0x0E0B }, ++ { 0x0DAC, 0x0E0C }, ++ { 0x0DAD, 0x0E0D }, ++ { 0x0DAE, 0x0E0E }, ++ { 0x0DAF, 0x0E0F }, ++ { 0x0DB0, 0x0E10 }, ++ { 0x0DB1, 0x0E11 }, ++ { 0x0DB2, 0x0E12 }, ++ { 0x0DB3, 0x0E13 }, ++ { 0x0DB4, 0x0E14 }, ++ { 0x0DB5, 0x0E15 }, ++ { 0x0DB6, 0x0E16 }, ++ { 0x0DB7, 0x0E17 }, ++ { 0x0DB8, 0x0E18 }, ++ { 0x0DB9, 0x0E19 }, ++ { 0x0DBA, 0x0E1A }, ++ { 0x0DBB, 0x0E1B }, ++ { 0x0DBC, 0x0E1C }, ++ { 0x0DBD, 0x0E1D }, ++ { 0x0DBE, 0x0E1E }, ++ { 0x0DBF, 0x0E1F }, ++ { 0x0DC0, 0x0E20 }, ++ { 0x0DC1, 0x0E21 }, ++ { 0x0DC2, 0x0E22 }, ++ { 0x0DC3, 0x0E23 }, ++ { 0x0DC4, 0x0E24 }, ++ { 0x0DC5, 0x0E25 }, ++ { 0x0DC6, 0x0E26 }, ++ { 0x0DC7, 0x0E27 }, ++ { 0x0DC8, 0x0E28 }, ++ { 0x0DC9, 0x0E29 }, ++ { 0x0DCA, 0x0E2A }, ++ { 0x0DCB, 0x0E2B }, ++ { 0x0DCC, 0x0E2C }, ++ { 0x0DCD, 0x0E2D }, ++ { 0x0DCE, 0x0E2E }, ++ { 0x0DCF, 0x0E2F }, ++ { 0x0DD0, 0x0E30 }, ++ { 0x0DD1, 0x0E31 }, ++ { 0x0DD2, 0x0E32 }, ++ { 0x0DD3, 0x0E33 }, ++ { 0x0DD4, 0x0E34 }, ++ { 0x0DD5, 0x0E35 }, ++ { 0x0DD6, 0x0E36 }, ++ { 0x0DD7, 0x0E37 }, ++ { 0x0DD8, 0x0E38 }, ++ { 0x0DD9, 0x0E39 }, ++ { 0x0DDA, 0x0E3A }, ++ { 0x0DDF, 0x0E3F }, ++ { 0x0DE0, 0x0E40 }, ++ { 0x0DE1, 0x0E41 }, ++ { 0x0DE2, 0x0E42 }, ++ { 0x0DE3, 0x0E43 }, ++ { 0x0DE4, 0x0E44 }, ++ { 0x0DE5, 0x0E45 }, ++ { 0x0DE6, 0x0E46 }, ++ { 0x0DE7, 0x0E47 }, ++ { 0x0DE8, 0x0E48 }, ++ { 0x0DE9, 0x0E49 }, ++ { 0x0DEA, 0x0E4A }, ++ { 0x0DEB, 0x0E4B }, ++ { 0x0DEC, 0x0E4C }, ++ { 0x0DED, 0x0E4D }, ++ { 0x0DF0, 0x0E50 }, ++ { 0x0DF1, 0x0E51 }, ++ { 0x0DF2, 0x0E52 }, ++ { 0x0DF3, 0x0E53 }, ++ { 0x0DF4, 0x0E54 }, ++ { 0x0DF5, 0x0E55 }, ++ { 0x0DF6, 0x0E56 }, ++ { 0x0DF7, 0x0E57 }, ++ { 0x0DF8, 0x0E58 }, ++ { 0x0DF9, 0x0E59 }, ++ { 0x0ED4, 0x11A8 }, ++ { 0x0ED5, 0x11A9 }, ++ { 0x0ED6, 0x11AA }, ++ { 0x0ED7, 0x11AB }, ++ { 0x0ED8, 0x11AC }, ++ { 0x0ED9, 0x11AD }, ++ { 0x0EDA, 0x11AE }, ++ { 0x0EDB, 0x11AF }, ++ { 0x0EDC, 0x11B0 }, ++ { 0x0EDD, 0x11B1 }, ++ { 0x0EDE, 0x11B2 }, ++ { 0x0EDF, 0x11B3 }, ++ { 0x0EE0, 0x11B4 }, ++ { 0x0EE1, 0x11B5 }, ++ { 0x0EE2, 0x11B6 }, ++ { 0x0EE3, 0x11B7 }, ++ { 0x0EE4, 0x11B8 }, ++ { 0x0EE5, 0x11B9 }, ++ { 0x0EE6, 0x11BA }, ++ { 0x0EE7, 0x11BB }, ++ { 0x0EE8, 0x11BC }, ++ { 0x0EE9, 0x11BD }, ++ { 0x0EEA, 0x11BE }, ++ { 0x0EEB, 0x11BF }, ++ { 0x0EEC, 0x11C0 }, ++ { 0x0EED, 0x11C1 }, ++ { 0x0EEE, 0x11C2 }, ++ { 0x0EF8, 0x11EB }, ++ { 0x0EFA, 0x11F9 }, ++ { 0x0AA2, 0x2002 }, ++ { 0x0AA1, 0x2003 }, ++ { 0x0AA3, 0x2004 }, ++ { 0x0AA4, 0x2005 }, ++ { 0x0AA5, 0x2007 }, ++ { 0x0AA6, 0x2008 }, ++ { 0x0AA7, 0x2009 }, ++ { 0x0AA8, 0x200A }, ++ { 0x0ABB, 0x2012 }, ++ { 0x0AAA, 0x2013 }, ++ { 0x0AA9, 0x2014 }, ++ { 0x07AF, 0x2015 }, ++ { 0x0CDF, 0x2017 }, ++ { 0x0AD0, 0x2018 }, ++ { 0x0AD1, 0x2019 }, ++ { 0x0AFD, 0x201A }, ++ { 0x0AD2, 0x201C }, ++ { 0x0AD3, 0x201D }, ++ { 0x0AFE, 0x201E }, ++ { 0x0AF1, 0x2020 }, ++ { 0x0AF2, 0x2021 }, ++ { 0x0AE6, 0x2022 }, ++ { 0x0AAE, 0x2026 }, ++ { 0x0AD6, 0x2032 }, ++ { 0x0AD7, 0x2033 }, ++ { 0x0AFC, 0x2038 }, ++ { 0x047E, 0x203E }, ++ { 0x20A0, 0x20A0 }, ++ { 0x20A1, 0x20A1 }, ++ { 0x20A2, 0x20A2 }, ++ { 0x20A3, 0x20A3 }, ++ { 0x20A4, 0x20A4 }, ++ { 0x20A5, 0x20A5 }, ++ { 0x20A6, 0x20A6 }, ++ { 0x20A7, 0x20A7 }, ++ { 0x20A8, 0x20A8 }, ++ { 0x0EFF, 0x20A9 }, ++ { 0x20A9, 0x20A9 }, ++ { 0x20AA, 0x20AA }, ++ { 0x20AB, 0x20AB }, ++ { 0x20AC, 0x20AC }, ++ { 0x0AB8, 0x2105 }, ++ { 0x06B0, 0x2116 }, ++ { 0x0AFB, 0x2117 }, ++ { 0x0AD4, 0x211E }, ++ { 0x0AC9, 0x2122 }, ++ { 0x0AB0, 0x2153 }, ++ { 0x0AB1, 0x2154 }, ++ { 0x0AB2, 0x2155 }, ++ { 0x0AB3, 0x2156 }, ++ { 0x0AB4, 0x2157 }, ++ { 0x0AB5, 0x2158 }, ++ { 0x0AB6, 0x2159 }, ++ { 0x0AB7, 0x215A }, ++ { 0x0AC3, 0x215B }, ++ { 0x0AC4, 0x215C }, ++ { 0x0AC5, 0x215D }, ++ { 0x0AC6, 0x215E }, ++ { 0x08FB, 0x2190 }, ++ { 0x08FC, 0x2191 }, ++ { 0x08FD, 0x2192 }, ++ { 0x08FE, 0x2193 }, ++ { 0x08CE, 0x21D2 }, ++ { 0x08CD, 0x21D4 }, ++ { 0x08EF, 0x2202 }, ++ { 0x08C5, 0x2207 }, ++ { 0x0BCA, 0x2218 }, ++ { 0x08D6, 0x221A }, ++ { 0x08C1, 0x221D }, ++ { 0x08C2, 0x221E }, ++ { 0x08DE, 0x2227 }, ++ { 0x0BA9, 0x2227 }, ++ { 0x08DF, 0x2228 }, ++ { 0x0BA8, 0x2228 }, ++ { 0x08DC, 0x2229 }, ++ { 0x0BC3, 0x2229 }, ++ { 0x08DD, 0x222A }, ++ { 0x0BD6, 0x222A }, ++ { 0x08BF, 0x222B }, ++ { 0x08C0, 0x2234 }, ++ { 0x08C8, 0x2245 }, ++ { 0x08BD, 0x2260 }, ++ { 0x08CF, 0x2261 }, ++ { 0x08BC, 0x2264 }, ++ { 0x08BE, 0x2265 }, ++ { 0x08DA, 0x2282 }, ++ { 0x0BDA, 0x2282 }, ++ { 0x08DB, 0x2283 }, ++ { 0x0BD8, 0x2283 }, ++ { 0x0BFC, 0x22A2 }, ++ { 0x0BDC, 0x22A3 }, ++ { 0x0BC2, 0x22A4 }, ++ { 0x0BCE, 0x22A5 }, ++ { 0x0BD3, 0x2308 }, ++ { 0x0BC4, 0x230A }, ++ { 0x0AFA, 0x2315 }, ++ { 0x08A4, 0x2320 }, ++ { 0x08A5, 0x2321 }, ++ { 0x0ABC, 0x2329 }, ++ { 0x0ABE, 0x232A }, ++ { 0x0BCC, 0x2395 }, ++ { 0x09E2, 0x2409 }, ++ { 0x09E5, 0x240A }, ++ { 0x09E9, 0x240B }, ++ { 0x09E3, 0x240C }, ++ { 0x09E4, 0x240D }, ++ { 0x09DF, 0x2422 }, ++ { 0x09E8, 0x2424 }, ++ { 0x09F1, 0x2500 }, ++ { 0x08A6, 0x2502 }, ++ { 0x09F8, 0x2502 }, ++ { 0x09EC, 0x250C }, ++ { 0x09EB, 0x2510 }, ++ { 0x09ED, 0x2514 }, ++ { 0x09EA, 0x2518 }, ++ { 0x09F4, 0x251C }, ++ { 0x09F5, 0x2524 }, ++ { 0x09F7, 0x252C }, ++ { 0x09F6, 0x2534 }, ++ { 0x09EE, 0x253C }, ++ { 0x09E1, 0x2592 }, ++ { 0x0ADF, 0x25A0 }, ++ { 0x0ACF, 0x25A1 }, ++ { 0x0AE7, 0x25AA }, ++ { 0x0AE1, 0x25AB }, ++ { 0x0ADB, 0x25AC }, ++ { 0x0AE2, 0x25AD }, ++ { 0x0AE8, 0x25B2 }, ++ { 0x0AE3, 0x25B3 }, ++ { 0x0ADD, 0x25B6 }, ++ { 0x0ACD, 0x25B7 }, ++ { 0x0AE9, 0x25BC }, ++ { 0x0AE4, 0x25BD }, ++ { 0x0ADC, 0x25C0 }, ++ { 0x0ACC, 0x25C1 }, ++ { 0x09E0, 0x25C6 }, ++ { 0x0ACE, 0x25CB }, ++ { 0x0BCF, 0x25CB }, ++ { 0x0ADE, 0x25CF }, ++ { 0x0AE0, 0x25E6 }, ++ { 0x0AE5, 0x2606 }, ++ { 0x0AF9, 0x260E }, ++ { 0x0ACA, 0x2613 }, ++ { 0x0AEA, 0x261C }, ++ { 0x0AEB, 0x261E }, ++ { 0x0AF8, 0x2640 }, ++ { 0x0AF7, 0x2642 }, ++ { 0x0AEC, 0x2663 }, ++ { 0x0AEE, 0x2665 }, ++ { 0x0AED, 0x2666 }, ++ { 0x0AF6, 0x266D }, ++ { 0x0AF5, 0x266F }, ++ { 0x0AF3, 0x2713 }, ++ { 0x0AF4, 0x2717 }, ++ { 0x0AD9, 0x271D }, ++ { 0x0AF0, 0x2720 }, ++ { 0x04A4, 0x3001 }, ++ { 0x04A1, 0x3002 }, ++ { 0x04A2, 0x300C }, ++ { 0x04A3, 0x300D }, ++ { 0x04DE, 0x309B }, ++ { 0x04DF, 0x309C }, ++ { 0x04A7, 0x30A1 }, ++ { 0x04B1, 0x30A2 }, ++ { 0x04A8, 0x30A3 }, ++ { 0x04B2, 0x30A4 }, ++ { 0x04A9, 0x30A5 }, ++ { 0x04B3, 0x30A6 }, ++ { 0x04AA, 0x30A7 }, ++ { 0x04B4, 0x30A8 }, ++ { 0x04AB, 0x30A9 }, ++ { 0x04B5, 0x30AA }, ++ { 0x04B6, 0x30AB }, ++ { 0x04B7, 0x30AD }, ++ { 0x04B8, 0x30AF }, ++ { 0x04B9, 0x30B1 }, ++ { 0x04BA, 0x30B3 }, ++ { 0x04BB, 0x30B5 }, ++ { 0x04BC, 0x30B7 }, ++ { 0x04BD, 0x30B9 }, ++ { 0x04BE, 0x30BB }, ++ { 0x04BF, 0x30BD }, ++ { 0x04C0, 0x30BF }, ++ { 0x04C1, 0x30C1 }, ++ { 0x04AF, 0x30C3 }, ++ { 0x04C2, 0x30C4 }, ++ { 0x04C3, 0x30C6 }, ++ { 0x04C4, 0x30C8 }, ++ { 0x04C5, 0x30CA }, ++ { 0x04C6, 0x30CB }, ++ { 0x04C7, 0x30CC }, ++ { 0x04C8, 0x30CD }, ++ { 0x04C9, 0x30CE }, ++ { 0x04CA, 0x30CF }, ++ { 0x04CB, 0x30D2 }, ++ { 0x04CC, 0x30D5 }, ++ { 0x04CD, 0x30D8 }, ++ { 0x04CE, 0x30DB }, ++ { 0x04CF, 0x30DE }, ++ { 0x04D0, 0x30DF }, ++ { 0x04D1, 0x30E0 }, ++ { 0x04D2, 0x30E1 }, ++ { 0x04D3, 0x30E2 }, ++ { 0x04AC, 0x30E3 }, ++ { 0x04D4, 0x30E4 }, ++ { 0x04AD, 0x30E5 }, ++ { 0x04D5, 0x30E6 }, ++ { 0x04AE, 0x30E7 }, ++ { 0x04D6, 0x30E8 }, ++ { 0x04D7, 0x30E9 }, ++ { 0x04D8, 0x30EA }, ++ { 0x04D9, 0x30EB }, ++ { 0x04DA, 0x30EC }, ++ { 0x04DB, 0x30ED }, ++ { 0x04DC, 0x30EF }, ++ { 0x04A6, 0x30F2 }, ++ { 0x04DD, 0x30F3 }, ++ { 0x04A5, 0x30FB }, ++ { 0x04B0, 0x30FC }, ++ { 0x0EA1, 0x3131 }, ++ { 0x0EA2, 0x3132 }, ++ { 0x0EA3, 0x3133 }, ++ { 0x0EA4, 0x3134 }, ++ { 0x0EA5, 0x3135 }, ++ { 0x0EA6, 0x3136 }, ++ { 0x0EA7, 0x3137 }, ++ { 0x0EA8, 0x3138 }, ++ { 0x0EA9, 0x3139 }, ++ { 0x0EAA, 0x313A }, ++ { 0x0EAB, 0x313B }, ++ { 0x0EAC, 0x313C }, ++ { 0x0EAD, 0x313D }, ++ { 0x0EAE, 0x313E }, ++ { 0x0EAF, 0x313F }, ++ { 0x0EB0, 0x3140 }, ++ { 0x0EB1, 0x3141 }, ++ { 0x0EB2, 0x3142 }, ++ { 0x0EB3, 0x3143 }, ++ { 0x0EB4, 0x3144 }, ++ { 0x0EB5, 0x3145 }, ++ { 0x0EB6, 0x3146 }, ++ { 0x0EB7, 0x3147 }, ++ { 0x0EB8, 0x3148 }, ++ { 0x0EB9, 0x3149 }, ++ { 0x0EBA, 0x314A }, ++ { 0x0EBB, 0x314B }, ++ { 0x0EBC, 0x314C }, ++ { 0x0EBD, 0x314D }, ++ { 0x0EBE, 0x314E }, ++ { 0x0EBF, 0x314F }, ++ { 0x0EC0, 0x3150 }, ++ { 0x0EC1, 0x3151 }, ++ { 0x0EC2, 0x3152 }, ++ { 0x0EC3, 0x3153 }, ++ { 0x0EC4, 0x3154 }, ++ { 0x0EC5, 0x3155 }, ++ { 0x0EC6, 0x3156 }, ++ { 0x0EC7, 0x3157 }, ++ { 0x0EC8, 0x3158 }, ++ { 0x0EC9, 0x3159 }, ++ { 0x0ECA, 0x315A }, ++ { 0x0ECB, 0x315B }, ++ { 0x0ECC, 0x315C }, ++ { 0x0ECD, 0x315D }, ++ { 0x0ECE, 0x315E }, ++ { 0x0ECF, 0x315F }, ++ { 0x0ED0, 0x3160 }, ++ { 0x0ED1, 0x3161 }, ++ { 0x0ED2, 0x3162 }, ++ { 0x0ED3, 0x3163 }, ++ { 0x0EEF, 0x316D }, ++ { 0x0EF0, 0x3171 }, ++ { 0x0EF1, 0x3178 }, ++ { 0x0EF2, 0x317F }, ++ { 0x0EF4, 0x3184 }, ++ { 0x0EF5, 0x3186 }, ++ { 0x0EF6, 0x318D }, ++ { 0x0EF7, 0x318E } ++}; ++ ++KeySym KeyMappingX11::get_keysym_from_unicode(unsigned int p_unicode) { ++ /* Latin 1 */ ++ ++ if (p_unicode >= 0x20 && p_unicode <= 0x7e) { ++ return p_unicode; ++ } ++ ++ if (p_unicode >= 0xa0 && p_unicode <= 0xff) { ++ return p_unicode; ++ } ++ ++ int middle, low = 0, high = _UNICODE_MAX - 1; ++ do { ++ middle = (high + low) / 2; ++ if (_unicode_to_xkeysym[middle].keysym == p_unicode) { ++ return _unicode_to_xkeysym[middle].keysym; ++ } ++ if (_unicode_to_xkeysym[middle].keysym <= p_unicode) { ++ low = middle + 1; ++ } else { ++ high = middle - 1; ++ } ++ } while (high >= low); ++ ++ // if not found, let's hope X understands it as unicode ++ return p_unicode | 0x01000000; ++} +diff --git a/platform/x11es/key_mapping_x11.h b/platform/x11es/key_mapping_x11.h +new file mode 100644 +index 0000000000..8f0b07f541 +--- /dev/null ++++ b/platform/x11es/key_mapping_x11.h +@@ -0,0 +1,55 @@ ++/**************************************************************************/ ++/* key_mapping_x11.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifndef KEY_MAPPING_X11_H ++#define KEY_MAPPING_X11_H ++ ++#include ++#include ++#define XK_MISCELLANY ++#define XK_LATIN1 ++#define XK_XKB_KEYS ++#include ++ ++#include "core/os/keyboard.h" ++ ++class KeyMappingX11 { ++ KeyMappingX11(){}; ++ ++public: ++ static unsigned int get_keycode(KeySym p_keysym); ++ static unsigned int get_xlibcode(unsigned int p_keysym); ++ static unsigned int get_scancode(unsigned int p_code); ++ static KeySym get_keysym(unsigned int p_code); ++ static unsigned int get_unicode_from_keysym(KeySym p_keysym); ++ static KeySym get_keysym_from_unicode(unsigned int p_unicode); ++}; ++ ++#endif // KEY_MAPPING_X11_H +diff --git a/platform/x11es/libudev-so_wrap.c b/platform/x11es/libudev-so_wrap.c +new file mode 100644 +index 0000000000..f33f4bf502 +--- /dev/null ++++ b/platform/x11es/libudev-so_wrap.c +@@ -0,0 +1,829 @@ ++// This file is generated. Do not edit! ++// see https://github.com/hpvb/dynload-wrapper for details ++// generated by ./generate-wrapper.py 0.2 on 2021-02-18 10:17:54 ++// flags: ./generate-wrapper.py --include /usr/include/libudev.h --sys-include --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header libudev-so_wrap.h --output-implementation libudev-so_wrap.c ++// ++#define udev_ref udev_ref_dylibloader_orig_libudev ++#define udev_unref udev_unref_dylibloader_orig_libudev ++#define udev_new udev_new_dylibloader_orig_libudev ++#define udev_set_log_fn udev_set_log_fn_dylibloader_orig_libudev ++#define udev_get_log_priority udev_get_log_priority_dylibloader_orig_libudev ++#define udev_set_log_priority udev_set_log_priority_dylibloader_orig_libudev ++#define udev_get_userdata udev_get_userdata_dylibloader_orig_libudev ++#define udev_set_userdata udev_set_userdata_dylibloader_orig_libudev ++#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_orig_libudev ++#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_orig_libudev ++#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_orig_libudev ++#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_orig_libudev ++#define udev_device_ref udev_device_ref_dylibloader_orig_libudev ++#define udev_device_unref udev_device_unref_dylibloader_orig_libudev ++#define udev_device_get_udev udev_device_get_udev_dylibloader_orig_libudev ++#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_orig_libudev ++#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_orig_libudev ++#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_orig_libudev ++#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_orig_libudev ++#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_orig_libudev ++#define udev_device_get_parent udev_device_get_parent_dylibloader_orig_libudev ++#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_orig_libudev ++#define udev_device_get_devpath udev_device_get_devpath_dylibloader_orig_libudev ++#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_orig_libudev ++#define udev_device_get_devtype udev_device_get_devtype_dylibloader_orig_libudev ++#define udev_device_get_syspath udev_device_get_syspath_dylibloader_orig_libudev ++#define udev_device_get_sysname udev_device_get_sysname_dylibloader_orig_libudev ++#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_orig_libudev ++#define udev_device_get_devnode udev_device_get_devnode_dylibloader_orig_libudev ++#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_orig_libudev ++#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_orig_libudev ++#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_orig_libudev ++#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_orig_libudev ++#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_orig_libudev ++#define udev_device_get_property_value udev_device_get_property_value_dylibloader_orig_libudev ++#define udev_device_get_driver udev_device_get_driver_dylibloader_orig_libudev ++#define udev_device_get_devnum udev_device_get_devnum_dylibloader_orig_libudev ++#define udev_device_get_action udev_device_get_action_dylibloader_orig_libudev ++#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_orig_libudev ++#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_orig_libudev ++#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_orig_libudev ++#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_orig_libudev ++#define udev_device_has_tag udev_device_has_tag_dylibloader_orig_libudev ++#define udev_monitor_ref udev_monitor_ref_dylibloader_orig_libudev ++#define udev_monitor_unref udev_monitor_unref_dylibloader_orig_libudev ++#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_orig_libudev ++#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_orig_libudev ++#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_orig_libudev ++#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_orig_libudev ++#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_orig_libudev ++#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_orig_libudev ++#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_orig_libudev ++#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_orig_libudev ++#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_orig_libudev ++#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_orig_libudev ++#define udev_enumerate_ref udev_enumerate_ref_dylibloader_orig_libudev ++#define udev_enumerate_unref udev_enumerate_unref_dylibloader_orig_libudev ++#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_orig_libudev ++#define udev_enumerate_new udev_enumerate_new_dylibloader_orig_libudev ++#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_orig_libudev ++#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_orig_libudev ++#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_orig_libudev ++#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_orig_libudev ++#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_orig_libudev ++#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_orig_libudev ++#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_orig_libudev ++#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_orig_libudev ++#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_orig_libudev ++#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_orig_libudev ++#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_orig_libudev ++#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_orig_libudev ++#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_orig_libudev ++#define udev_queue_ref udev_queue_ref_dylibloader_orig_libudev ++#define udev_queue_unref udev_queue_unref_dylibloader_orig_libudev ++#define udev_queue_get_udev udev_queue_get_udev_dylibloader_orig_libudev ++#define udev_queue_new udev_queue_new_dylibloader_orig_libudev ++#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_orig_libudev ++#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_orig_libudev ++#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_orig_libudev ++#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_orig_libudev ++#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_orig_libudev ++#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_orig_libudev ++#define udev_queue_get_fd udev_queue_get_fd_dylibloader_orig_libudev ++#define udev_queue_flush udev_queue_flush_dylibloader_orig_libudev ++#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_orig_libudev ++#define udev_hwdb_new udev_hwdb_new_dylibloader_orig_libudev ++#define udev_hwdb_ref udev_hwdb_ref_dylibloader_orig_libudev ++#define udev_hwdb_unref udev_hwdb_unref_dylibloader_orig_libudev ++#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_orig_libudev ++#define udev_util_encode_string udev_util_encode_string_dylibloader_orig_libudev ++#include ++#undef udev_ref ++#undef udev_unref ++#undef udev_new ++#undef udev_set_log_fn ++#undef udev_get_log_priority ++#undef udev_set_log_priority ++#undef udev_get_userdata ++#undef udev_set_userdata ++#undef udev_list_entry_get_next ++#undef udev_list_entry_get_by_name ++#undef udev_list_entry_get_name ++#undef udev_list_entry_get_value ++#undef udev_device_ref ++#undef udev_device_unref ++#undef udev_device_get_udev ++#undef udev_device_new_from_syspath ++#undef udev_device_new_from_devnum ++#undef udev_device_new_from_subsystem_sysname ++#undef udev_device_new_from_device_id ++#undef udev_device_new_from_environment ++#undef udev_device_get_parent ++#undef udev_device_get_parent_with_subsystem_devtype ++#undef udev_device_get_devpath ++#undef udev_device_get_subsystem ++#undef udev_device_get_devtype ++#undef udev_device_get_syspath ++#undef udev_device_get_sysname ++#undef udev_device_get_sysnum ++#undef udev_device_get_devnode ++#undef udev_device_get_is_initialized ++#undef udev_device_get_devlinks_list_entry ++#undef udev_device_get_properties_list_entry ++#undef udev_device_get_tags_list_entry ++#undef udev_device_get_sysattr_list_entry ++#undef udev_device_get_property_value ++#undef udev_device_get_driver ++#undef udev_device_get_devnum ++#undef udev_device_get_action ++#undef udev_device_get_seqnum ++#undef udev_device_get_usec_since_initialized ++#undef udev_device_get_sysattr_value ++#undef udev_device_set_sysattr_value ++#undef udev_device_has_tag ++#undef udev_monitor_ref ++#undef udev_monitor_unref ++#undef udev_monitor_get_udev ++#undef udev_monitor_new_from_netlink ++#undef udev_monitor_enable_receiving ++#undef udev_monitor_set_receive_buffer_size ++#undef udev_monitor_get_fd ++#undef udev_monitor_receive_device ++#undef udev_monitor_filter_add_match_subsystem_devtype ++#undef udev_monitor_filter_add_match_tag ++#undef udev_monitor_filter_update ++#undef udev_monitor_filter_remove ++#undef udev_enumerate_ref ++#undef udev_enumerate_unref ++#undef udev_enumerate_get_udev ++#undef udev_enumerate_new ++#undef udev_enumerate_add_match_subsystem ++#undef udev_enumerate_add_nomatch_subsystem ++#undef udev_enumerate_add_match_sysattr ++#undef udev_enumerate_add_nomatch_sysattr ++#undef udev_enumerate_add_match_property ++#undef udev_enumerate_add_match_sysname ++#undef udev_enumerate_add_match_tag ++#undef udev_enumerate_add_match_parent ++#undef udev_enumerate_add_match_is_initialized ++#undef udev_enumerate_add_syspath ++#undef udev_enumerate_scan_devices ++#undef udev_enumerate_scan_subsystems ++#undef udev_enumerate_get_list_entry ++#undef udev_queue_ref ++#undef udev_queue_unref ++#undef udev_queue_get_udev ++#undef udev_queue_new ++#undef udev_queue_get_kernel_seqnum ++#undef udev_queue_get_udev_seqnum ++#undef udev_queue_get_udev_is_active ++#undef udev_queue_get_queue_is_empty ++#undef udev_queue_get_seqnum_is_finished ++#undef udev_queue_get_seqnum_sequence_is_finished ++#undef udev_queue_get_fd ++#undef udev_queue_flush ++#undef udev_queue_get_queued_list_entry ++#undef udev_hwdb_new ++#undef udev_hwdb_ref ++#undef udev_hwdb_unref ++#undef udev_hwdb_get_properties_list_entry ++#undef udev_util_encode_string ++#include ++#include ++struct udev* (*udev_ref_dylibloader_wrapper_libudev)(struct udev*); ++struct udev* (*udev_unref_dylibloader_wrapper_libudev)(struct udev*); ++struct udev* (*udev_new_dylibloader_wrapper_libudev)( void); ++void (*udev_set_log_fn_dylibloader_wrapper_libudev)(struct udev*, void*); ++int (*udev_get_log_priority_dylibloader_wrapper_libudev)(struct udev*); ++void (*udev_set_log_priority_dylibloader_wrapper_libudev)(struct udev*, int); ++void* (*udev_get_userdata_dylibloader_wrapper_libudev)(struct udev*); ++void (*udev_set_userdata_dylibloader_wrapper_libudev)(struct udev*, void*); ++struct udev_list_entry* (*udev_list_entry_get_next_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++struct udev_list_entry* (*udev_list_entry_get_by_name_dylibloader_wrapper_libudev)(struct udev_list_entry*,const char*); ++const char* (*udev_list_entry_get_name_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++const char* (*udev_list_entry_get_value_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++struct udev_device* (*udev_device_ref_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_device* (*udev_device_unref_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev* (*udev_device_get_udev_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_device* (*udev_device_new_from_syspath_dylibloader_wrapper_libudev)(struct udev*,const char*); ++struct udev_device* (*udev_device_new_from_devnum_dylibloader_wrapper_libudev)(struct udev*, char, dev_t); ++struct udev_device* (*udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev)(struct udev*,const char*,const char*); ++struct udev_device* (*udev_device_new_from_device_id_dylibloader_wrapper_libudev)(struct udev*,const char*); ++struct udev_device* (*udev_device_new_from_environment_dylibloader_wrapper_libudev)(struct udev*); ++struct udev_device* (*udev_device_get_parent_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_device* (*udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*); ++const char* (*udev_device_get_devpath_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_subsystem_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_devtype_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_syspath_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_sysname_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_sysnum_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_devnode_dylibloader_wrapper_libudev)(struct udev_device*); ++int (*udev_device_get_is_initialized_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_list_entry* (*udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_list_entry* (*udev_device_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_list_entry* (*udev_device_get_tags_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++struct udev_list_entry* (*udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_property_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++const char* (*udev_device_get_driver_dylibloader_wrapper_libudev)(struct udev_device*); ++dev_t (*udev_device_get_devnum_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_action_dylibloader_wrapper_libudev)(struct udev_device*); ++unsigned long long int (*udev_device_get_seqnum_dylibloader_wrapper_libudev)(struct udev_device*); ++unsigned long long int (*udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev)(struct udev_device*); ++const char* (*udev_device_get_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++int (*udev_device_set_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*); ++int (*udev_device_has_tag_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++struct udev_monitor* (*udev_monitor_ref_dylibloader_wrapper_libudev)(struct udev_monitor*); ++struct udev_monitor* (*udev_monitor_unref_dylibloader_wrapper_libudev)(struct udev_monitor*); ++struct udev* (*udev_monitor_get_udev_dylibloader_wrapper_libudev)(struct udev_monitor*); ++struct udev_monitor* (*udev_monitor_new_from_netlink_dylibloader_wrapper_libudev)(struct udev*,const char*); ++int (*udev_monitor_enable_receiving_dylibloader_wrapper_libudev)(struct udev_monitor*); ++int (*udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev)(struct udev_monitor*, int); ++int (*udev_monitor_get_fd_dylibloader_wrapper_libudev)(struct udev_monitor*); ++struct udev_device* (*udev_monitor_receive_device_dylibloader_wrapper_libudev)(struct udev_monitor*); ++int (*udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*,const char*); ++int (*udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*); ++int (*udev_monitor_filter_update_dylibloader_wrapper_libudev)(struct udev_monitor*); ++int (*udev_monitor_filter_remove_dylibloader_wrapper_libudev)(struct udev_monitor*); ++struct udev_enumerate* (*udev_enumerate_ref_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++struct udev_enumerate* (*udev_enumerate_unref_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++struct udev* (*udev_enumerate_get_udev_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++struct udev_enumerate* (*udev_enumerate_new_dylibloader_wrapper_libudev)(struct udev*); ++int (*udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++int (*udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++int (*udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++int (*udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++int (*udev_enumerate_add_match_property_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++int (*udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++int (*udev_enumerate_add_match_tag_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++int (*udev_enumerate_add_match_parent_dylibloader_wrapper_libudev)(struct udev_enumerate*,struct udev_device*); ++int (*udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++int (*udev_enumerate_add_syspath_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++int (*udev_enumerate_scan_devices_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++int (*udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++struct udev_list_entry* (*udev_enumerate_get_list_entry_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++struct udev_queue* (*udev_queue_ref_dylibloader_wrapper_libudev)(struct udev_queue*); ++struct udev_queue* (*udev_queue_unref_dylibloader_wrapper_libudev)(struct udev_queue*); ++struct udev* (*udev_queue_get_udev_dylibloader_wrapper_libudev)(struct udev_queue*); ++struct udev_queue* (*udev_queue_new_dylibloader_wrapper_libudev)(struct udev*); ++unsigned long long int (*udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*); ++unsigned long long int (*udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*); ++int (*udev_queue_get_udev_is_active_dylibloader_wrapper_libudev)(struct udev_queue*); ++int (*udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev)(struct udev_queue*); ++int (*udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int); ++int (*udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int, unsigned long long int); ++int (*udev_queue_get_fd_dylibloader_wrapper_libudev)(struct udev_queue*); ++int (*udev_queue_flush_dylibloader_wrapper_libudev)(struct udev_queue*); ++struct udev_list_entry* (*udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev)(struct udev_queue*); ++struct udev_hwdb* (*udev_hwdb_new_dylibloader_wrapper_libudev)(struct udev*); ++struct udev_hwdb* (*udev_hwdb_ref_dylibloader_wrapper_libudev)(struct udev_hwdb*); ++struct udev_hwdb* (*udev_hwdb_unref_dylibloader_wrapper_libudev)(struct udev_hwdb*); ++struct udev_list_entry* (*udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_hwdb*,const char*, unsigned); ++int (*udev_util_encode_string_dylibloader_wrapper_libudev)(const char*, char*, size_t); ++int initialize_libudev() { ++ void *handle; ++ char *error; ++ handle = dlopen("libudev.so.1", RTLD_LAZY); ++ if (!handle) { ++ fprintf(stderr, "%s\n", dlerror()); ++ return(1); ++ } ++ dlerror(); ++// udev_ref ++ *(void **) (&udev_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_unref ++ *(void **) (&udev_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_new ++ *(void **) (&udev_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_new"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_set_log_fn ++ *(void **) (&udev_set_log_fn_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_log_fn"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_get_log_priority ++ *(void **) (&udev_get_log_priority_dylibloader_wrapper_libudev) = dlsym(handle, "udev_get_log_priority"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_set_log_priority ++ *(void **) (&udev_set_log_priority_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_log_priority"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_get_userdata ++ *(void **) (&udev_get_userdata_dylibloader_wrapper_libudev) = dlsym(handle, "udev_get_userdata"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_set_userdata ++ *(void **) (&udev_set_userdata_dylibloader_wrapper_libudev) = dlsym(handle, "udev_set_userdata"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_list_entry_get_next ++ *(void **) (&udev_list_entry_get_next_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_next"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_list_entry_get_by_name ++ *(void **) (&udev_list_entry_get_by_name_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_by_name"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_list_entry_get_name ++ *(void **) (&udev_list_entry_get_name_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_name"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_list_entry_get_value ++ *(void **) (&udev_list_entry_get_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_list_entry_get_value"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_ref ++ *(void **) (&udev_device_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_unref ++ *(void **) (&udev_device_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_udev ++ *(void **) (&udev_device_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_udev"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_new_from_syspath ++ *(void **) (&udev_device_new_from_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_syspath"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_new_from_devnum ++ *(void **) (&udev_device_new_from_devnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_devnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_new_from_subsystem_sysname ++ *(void **) (&udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_subsystem_sysname"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_new_from_device_id ++ *(void **) (&udev_device_new_from_device_id_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_device_id"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_new_from_environment ++ *(void **) (&udev_device_new_from_environment_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_new_from_environment"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_parent ++ *(void **) (&udev_device_get_parent_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_parent"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_parent_with_subsystem_devtype ++ *(void **) (&udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_parent_with_subsystem_devtype"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_devpath ++ *(void **) (&udev_device_get_devpath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devpath"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_subsystem ++ *(void **) (&udev_device_get_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_subsystem"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_devtype ++ *(void **) (&udev_device_get_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devtype"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_syspath ++ *(void **) (&udev_device_get_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_syspath"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_sysname ++ *(void **) (&udev_device_get_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysname"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_sysnum ++ *(void **) (&udev_device_get_sysnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_devnode ++ *(void **) (&udev_device_get_devnode_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devnode"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_is_initialized ++ *(void **) (&udev_device_get_is_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_is_initialized"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_devlinks_list_entry ++ *(void **) (&udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devlinks_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_properties_list_entry ++ *(void **) (&udev_device_get_properties_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_properties_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_tags_list_entry ++ *(void **) (&udev_device_get_tags_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_tags_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_sysattr_list_entry ++ *(void **) (&udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysattr_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_property_value ++ *(void **) (&udev_device_get_property_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_property_value"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_driver ++ *(void **) (&udev_device_get_driver_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_driver"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_devnum ++ *(void **) (&udev_device_get_devnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_devnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_action ++ *(void **) (&udev_device_get_action_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_action"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_seqnum ++ *(void **) (&udev_device_get_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_seqnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_usec_since_initialized ++ *(void **) (&udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_usec_since_initialized"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_get_sysattr_value ++ *(void **) (&udev_device_get_sysattr_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_get_sysattr_value"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_set_sysattr_value ++ *(void **) (&udev_device_set_sysattr_value_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_set_sysattr_value"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_device_has_tag ++ *(void **) (&udev_device_has_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_device_has_tag"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_ref ++ *(void **) (&udev_monitor_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_unref ++ *(void **) (&udev_monitor_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_get_udev ++ *(void **) (&udev_monitor_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_get_udev"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_new_from_netlink ++ *(void **) (&udev_monitor_new_from_netlink_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_new_from_netlink"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_enable_receiving ++ *(void **) (&udev_monitor_enable_receiving_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_enable_receiving"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_set_receive_buffer_size ++ *(void **) (&udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_set_receive_buffer_size"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_get_fd ++ *(void **) (&udev_monitor_get_fd_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_get_fd"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_receive_device ++ *(void **) (&udev_monitor_receive_device_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_receive_device"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_filter_add_match_subsystem_devtype ++ *(void **) (&udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_add_match_subsystem_devtype"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_filter_add_match_tag ++ *(void **) (&udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_add_match_tag"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_filter_update ++ *(void **) (&udev_monitor_filter_update_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_update"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_monitor_filter_remove ++ *(void **) (&udev_monitor_filter_remove_dylibloader_wrapper_libudev) = dlsym(handle, "udev_monitor_filter_remove"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_ref ++ *(void **) (&udev_enumerate_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_unref ++ *(void **) (&udev_enumerate_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_get_udev ++ *(void **) (&udev_enumerate_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_get_udev"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_new ++ *(void **) (&udev_enumerate_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_new"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_subsystem ++ *(void **) (&udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_subsystem"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_nomatch_subsystem ++ *(void **) (&udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_nomatch_subsystem"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_sysattr ++ *(void **) (&udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_sysattr"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_nomatch_sysattr ++ *(void **) (&udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_nomatch_sysattr"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_property ++ *(void **) (&udev_enumerate_add_match_property_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_property"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_sysname ++ *(void **) (&udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_sysname"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_tag ++ *(void **) (&udev_enumerate_add_match_tag_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_tag"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_parent ++ *(void **) (&udev_enumerate_add_match_parent_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_parent"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_match_is_initialized ++ *(void **) (&udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_match_is_initialized"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_add_syspath ++ *(void **) (&udev_enumerate_add_syspath_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_add_syspath"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_scan_devices ++ *(void **) (&udev_enumerate_scan_devices_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_scan_devices"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_scan_subsystems ++ *(void **) (&udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_scan_subsystems"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_enumerate_get_list_entry ++ *(void **) (&udev_enumerate_get_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_enumerate_get_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_ref ++ *(void **) (&udev_queue_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_unref ++ *(void **) (&udev_queue_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_udev ++ *(void **) (&udev_queue_get_udev_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_new ++ *(void **) (&udev_queue_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_new"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_kernel_seqnum ++ *(void **) (&udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_kernel_seqnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_udev_seqnum ++ *(void **) (&udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev_seqnum"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_udev_is_active ++ *(void **) (&udev_queue_get_udev_is_active_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_udev_is_active"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_queue_is_empty ++ *(void **) (&udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_queue_is_empty"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_seqnum_is_finished ++ *(void **) (&udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_seqnum_is_finished"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_seqnum_sequence_is_finished ++ *(void **) (&udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_seqnum_sequence_is_finished"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_fd ++ *(void **) (&udev_queue_get_fd_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_fd"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_flush ++ *(void **) (&udev_queue_flush_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_flush"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_queue_get_queued_list_entry ++ *(void **) (&udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_queue_get_queued_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_hwdb_new ++ *(void **) (&udev_hwdb_new_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_new"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_hwdb_ref ++ *(void **) (&udev_hwdb_ref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_ref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_hwdb_unref ++ *(void **) (&udev_hwdb_unref_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_unref"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_hwdb_get_properties_list_entry ++ *(void **) (&udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev) = dlsym(handle, "udev_hwdb_get_properties_list_entry"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++// udev_util_encode_string ++ *(void **) (&udev_util_encode_string_dylibloader_wrapper_libudev) = dlsym(handle, "udev_util_encode_string"); ++ error = dlerror(); ++ if (error != NULL) { ++ fprintf(stderr, "%s\n", error); ++ } ++return 0; ++} +diff --git a/platform/x11es/libudev-so_wrap.h b/platform/x11es/libudev-so_wrap.h +new file mode 100644 +index 0000000000..ad5f7743ef +--- /dev/null ++++ b/platform/x11es/libudev-so_wrap.h +@@ -0,0 +1,376 @@ ++#ifndef DYLIBLOAD_WRAPPER_LIBUDEV ++#define DYLIBLOAD_WRAPPER_LIBUDEV ++// This file is generated. Do not edit! ++// see https://github.com/hpvb/dynload-wrapper for details ++// generated by ./generate-wrapper.py 0.2 on 2021-02-18 10:17:54 ++// flags: ./generate-wrapper.py --include /usr/include/libudev.h --sys-include --soname libudev.so.1 --init-name libudev --omit-prefix gnu_ --output-header libudev-so_wrap.h --output-implementation libudev-so_wrap.c ++// ++#define udev_ref udev_ref_dylibloader_orig_libudev ++#define udev_unref udev_unref_dylibloader_orig_libudev ++#define udev_new udev_new_dylibloader_orig_libudev ++#define udev_set_log_fn udev_set_log_fn_dylibloader_orig_libudev ++#define udev_get_log_priority udev_get_log_priority_dylibloader_orig_libudev ++#define udev_set_log_priority udev_set_log_priority_dylibloader_orig_libudev ++#define udev_get_userdata udev_get_userdata_dylibloader_orig_libudev ++#define udev_set_userdata udev_set_userdata_dylibloader_orig_libudev ++#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_orig_libudev ++#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_orig_libudev ++#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_orig_libudev ++#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_orig_libudev ++#define udev_device_ref udev_device_ref_dylibloader_orig_libudev ++#define udev_device_unref udev_device_unref_dylibloader_orig_libudev ++#define udev_device_get_udev udev_device_get_udev_dylibloader_orig_libudev ++#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_orig_libudev ++#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_orig_libudev ++#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_orig_libudev ++#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_orig_libudev ++#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_orig_libudev ++#define udev_device_get_parent udev_device_get_parent_dylibloader_orig_libudev ++#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_orig_libudev ++#define udev_device_get_devpath udev_device_get_devpath_dylibloader_orig_libudev ++#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_orig_libudev ++#define udev_device_get_devtype udev_device_get_devtype_dylibloader_orig_libudev ++#define udev_device_get_syspath udev_device_get_syspath_dylibloader_orig_libudev ++#define udev_device_get_sysname udev_device_get_sysname_dylibloader_orig_libudev ++#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_orig_libudev ++#define udev_device_get_devnode udev_device_get_devnode_dylibloader_orig_libudev ++#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_orig_libudev ++#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_orig_libudev ++#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_orig_libudev ++#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_orig_libudev ++#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_orig_libudev ++#define udev_device_get_property_value udev_device_get_property_value_dylibloader_orig_libudev ++#define udev_device_get_driver udev_device_get_driver_dylibloader_orig_libudev ++#define udev_device_get_devnum udev_device_get_devnum_dylibloader_orig_libudev ++#define udev_device_get_action udev_device_get_action_dylibloader_orig_libudev ++#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_orig_libudev ++#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_orig_libudev ++#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_orig_libudev ++#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_orig_libudev ++#define udev_device_has_tag udev_device_has_tag_dylibloader_orig_libudev ++#define udev_monitor_ref udev_monitor_ref_dylibloader_orig_libudev ++#define udev_monitor_unref udev_monitor_unref_dylibloader_orig_libudev ++#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_orig_libudev ++#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_orig_libudev ++#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_orig_libudev ++#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_orig_libudev ++#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_orig_libudev ++#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_orig_libudev ++#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_orig_libudev ++#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_orig_libudev ++#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_orig_libudev ++#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_orig_libudev ++#define udev_enumerate_ref udev_enumerate_ref_dylibloader_orig_libudev ++#define udev_enumerate_unref udev_enumerate_unref_dylibloader_orig_libudev ++#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_orig_libudev ++#define udev_enumerate_new udev_enumerate_new_dylibloader_orig_libudev ++#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_orig_libudev ++#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_orig_libudev ++#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_orig_libudev ++#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_orig_libudev ++#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_orig_libudev ++#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_orig_libudev ++#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_orig_libudev ++#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_orig_libudev ++#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_orig_libudev ++#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_orig_libudev ++#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_orig_libudev ++#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_orig_libudev ++#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_orig_libudev ++#define udev_queue_ref udev_queue_ref_dylibloader_orig_libudev ++#define udev_queue_unref udev_queue_unref_dylibloader_orig_libudev ++#define udev_queue_get_udev udev_queue_get_udev_dylibloader_orig_libudev ++#define udev_queue_new udev_queue_new_dylibloader_orig_libudev ++#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_orig_libudev ++#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_orig_libudev ++#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_orig_libudev ++#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_orig_libudev ++#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_orig_libudev ++#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_orig_libudev ++#define udev_queue_get_fd udev_queue_get_fd_dylibloader_orig_libudev ++#define udev_queue_flush udev_queue_flush_dylibloader_orig_libudev ++#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_orig_libudev ++#define udev_hwdb_new udev_hwdb_new_dylibloader_orig_libudev ++#define udev_hwdb_ref udev_hwdb_ref_dylibloader_orig_libudev ++#define udev_hwdb_unref udev_hwdb_unref_dylibloader_orig_libudev ++#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_orig_libudev ++#define udev_util_encode_string udev_util_encode_string_dylibloader_orig_libudev ++#include ++#undef udev_ref ++#undef udev_unref ++#undef udev_new ++#undef udev_set_log_fn ++#undef udev_get_log_priority ++#undef udev_set_log_priority ++#undef udev_get_userdata ++#undef udev_set_userdata ++#undef udev_list_entry_get_next ++#undef udev_list_entry_get_by_name ++#undef udev_list_entry_get_name ++#undef udev_list_entry_get_value ++#undef udev_device_ref ++#undef udev_device_unref ++#undef udev_device_get_udev ++#undef udev_device_new_from_syspath ++#undef udev_device_new_from_devnum ++#undef udev_device_new_from_subsystem_sysname ++#undef udev_device_new_from_device_id ++#undef udev_device_new_from_environment ++#undef udev_device_get_parent ++#undef udev_device_get_parent_with_subsystem_devtype ++#undef udev_device_get_devpath ++#undef udev_device_get_subsystem ++#undef udev_device_get_devtype ++#undef udev_device_get_syspath ++#undef udev_device_get_sysname ++#undef udev_device_get_sysnum ++#undef udev_device_get_devnode ++#undef udev_device_get_is_initialized ++#undef udev_device_get_devlinks_list_entry ++#undef udev_device_get_properties_list_entry ++#undef udev_device_get_tags_list_entry ++#undef udev_device_get_sysattr_list_entry ++#undef udev_device_get_property_value ++#undef udev_device_get_driver ++#undef udev_device_get_devnum ++#undef udev_device_get_action ++#undef udev_device_get_seqnum ++#undef udev_device_get_usec_since_initialized ++#undef udev_device_get_sysattr_value ++#undef udev_device_set_sysattr_value ++#undef udev_device_has_tag ++#undef udev_monitor_ref ++#undef udev_monitor_unref ++#undef udev_monitor_get_udev ++#undef udev_monitor_new_from_netlink ++#undef udev_monitor_enable_receiving ++#undef udev_monitor_set_receive_buffer_size ++#undef udev_monitor_get_fd ++#undef udev_monitor_receive_device ++#undef udev_monitor_filter_add_match_subsystem_devtype ++#undef udev_monitor_filter_add_match_tag ++#undef udev_monitor_filter_update ++#undef udev_monitor_filter_remove ++#undef udev_enumerate_ref ++#undef udev_enumerate_unref ++#undef udev_enumerate_get_udev ++#undef udev_enumerate_new ++#undef udev_enumerate_add_match_subsystem ++#undef udev_enumerate_add_nomatch_subsystem ++#undef udev_enumerate_add_match_sysattr ++#undef udev_enumerate_add_nomatch_sysattr ++#undef udev_enumerate_add_match_property ++#undef udev_enumerate_add_match_sysname ++#undef udev_enumerate_add_match_tag ++#undef udev_enumerate_add_match_parent ++#undef udev_enumerate_add_match_is_initialized ++#undef udev_enumerate_add_syspath ++#undef udev_enumerate_scan_devices ++#undef udev_enumerate_scan_subsystems ++#undef udev_enumerate_get_list_entry ++#undef udev_queue_ref ++#undef udev_queue_unref ++#undef udev_queue_get_udev ++#undef udev_queue_new ++#undef udev_queue_get_kernel_seqnum ++#undef udev_queue_get_udev_seqnum ++#undef udev_queue_get_udev_is_active ++#undef udev_queue_get_queue_is_empty ++#undef udev_queue_get_seqnum_is_finished ++#undef udev_queue_get_seqnum_sequence_is_finished ++#undef udev_queue_get_fd ++#undef udev_queue_flush ++#undef udev_queue_get_queued_list_entry ++#undef udev_hwdb_new ++#undef udev_hwdb_ref ++#undef udev_hwdb_unref ++#undef udev_hwdb_get_properties_list_entry ++#undef udev_util_encode_string ++#ifdef __cplusplus ++extern "C" { ++#endif ++#define udev_ref udev_ref_dylibloader_wrapper_libudev ++#define udev_unref udev_unref_dylibloader_wrapper_libudev ++#define udev_new udev_new_dylibloader_wrapper_libudev ++#define udev_set_log_fn udev_set_log_fn_dylibloader_wrapper_libudev ++#define udev_get_log_priority udev_get_log_priority_dylibloader_wrapper_libudev ++#define udev_set_log_priority udev_set_log_priority_dylibloader_wrapper_libudev ++#define udev_get_userdata udev_get_userdata_dylibloader_wrapper_libudev ++#define udev_set_userdata udev_set_userdata_dylibloader_wrapper_libudev ++#define udev_list_entry_get_next udev_list_entry_get_next_dylibloader_wrapper_libudev ++#define udev_list_entry_get_by_name udev_list_entry_get_by_name_dylibloader_wrapper_libudev ++#define udev_list_entry_get_name udev_list_entry_get_name_dylibloader_wrapper_libudev ++#define udev_list_entry_get_value udev_list_entry_get_value_dylibloader_wrapper_libudev ++#define udev_device_ref udev_device_ref_dylibloader_wrapper_libudev ++#define udev_device_unref udev_device_unref_dylibloader_wrapper_libudev ++#define udev_device_get_udev udev_device_get_udev_dylibloader_wrapper_libudev ++#define udev_device_new_from_syspath udev_device_new_from_syspath_dylibloader_wrapper_libudev ++#define udev_device_new_from_devnum udev_device_new_from_devnum_dylibloader_wrapper_libudev ++#define udev_device_new_from_subsystem_sysname udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev ++#define udev_device_new_from_device_id udev_device_new_from_device_id_dylibloader_wrapper_libudev ++#define udev_device_new_from_environment udev_device_new_from_environment_dylibloader_wrapper_libudev ++#define udev_device_get_parent udev_device_get_parent_dylibloader_wrapper_libudev ++#define udev_device_get_parent_with_subsystem_devtype udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev ++#define udev_device_get_devpath udev_device_get_devpath_dylibloader_wrapper_libudev ++#define udev_device_get_subsystem udev_device_get_subsystem_dylibloader_wrapper_libudev ++#define udev_device_get_devtype udev_device_get_devtype_dylibloader_wrapper_libudev ++#define udev_device_get_syspath udev_device_get_syspath_dylibloader_wrapper_libudev ++#define udev_device_get_sysname udev_device_get_sysname_dylibloader_wrapper_libudev ++#define udev_device_get_sysnum udev_device_get_sysnum_dylibloader_wrapper_libudev ++#define udev_device_get_devnode udev_device_get_devnode_dylibloader_wrapper_libudev ++#define udev_device_get_is_initialized udev_device_get_is_initialized_dylibloader_wrapper_libudev ++#define udev_device_get_devlinks_list_entry udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev ++#define udev_device_get_properties_list_entry udev_device_get_properties_list_entry_dylibloader_wrapper_libudev ++#define udev_device_get_tags_list_entry udev_device_get_tags_list_entry_dylibloader_wrapper_libudev ++#define udev_device_get_sysattr_list_entry udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev ++#define udev_device_get_property_value udev_device_get_property_value_dylibloader_wrapper_libudev ++#define udev_device_get_driver udev_device_get_driver_dylibloader_wrapper_libudev ++#define udev_device_get_devnum udev_device_get_devnum_dylibloader_wrapper_libudev ++#define udev_device_get_action udev_device_get_action_dylibloader_wrapper_libudev ++#define udev_device_get_seqnum udev_device_get_seqnum_dylibloader_wrapper_libudev ++#define udev_device_get_usec_since_initialized udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev ++#define udev_device_get_sysattr_value udev_device_get_sysattr_value_dylibloader_wrapper_libudev ++#define udev_device_set_sysattr_value udev_device_set_sysattr_value_dylibloader_wrapper_libudev ++#define udev_device_has_tag udev_device_has_tag_dylibloader_wrapper_libudev ++#define udev_monitor_ref udev_monitor_ref_dylibloader_wrapper_libudev ++#define udev_monitor_unref udev_monitor_unref_dylibloader_wrapper_libudev ++#define udev_monitor_get_udev udev_monitor_get_udev_dylibloader_wrapper_libudev ++#define udev_monitor_new_from_netlink udev_monitor_new_from_netlink_dylibloader_wrapper_libudev ++#define udev_monitor_enable_receiving udev_monitor_enable_receiving_dylibloader_wrapper_libudev ++#define udev_monitor_set_receive_buffer_size udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev ++#define udev_monitor_get_fd udev_monitor_get_fd_dylibloader_wrapper_libudev ++#define udev_monitor_receive_device udev_monitor_receive_device_dylibloader_wrapper_libudev ++#define udev_monitor_filter_add_match_subsystem_devtype udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev ++#define udev_monitor_filter_add_match_tag udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev ++#define udev_monitor_filter_update udev_monitor_filter_update_dylibloader_wrapper_libudev ++#define udev_monitor_filter_remove udev_monitor_filter_remove_dylibloader_wrapper_libudev ++#define udev_enumerate_ref udev_enumerate_ref_dylibloader_wrapper_libudev ++#define udev_enumerate_unref udev_enumerate_unref_dylibloader_wrapper_libudev ++#define udev_enumerate_get_udev udev_enumerate_get_udev_dylibloader_wrapper_libudev ++#define udev_enumerate_new udev_enumerate_new_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_subsystem udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev ++#define udev_enumerate_add_nomatch_subsystem udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_sysattr udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev ++#define udev_enumerate_add_nomatch_sysattr udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_property udev_enumerate_add_match_property_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_sysname udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_tag udev_enumerate_add_match_tag_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_parent udev_enumerate_add_match_parent_dylibloader_wrapper_libudev ++#define udev_enumerate_add_match_is_initialized udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev ++#define udev_enumerate_add_syspath udev_enumerate_add_syspath_dylibloader_wrapper_libudev ++#define udev_enumerate_scan_devices udev_enumerate_scan_devices_dylibloader_wrapper_libudev ++#define udev_enumerate_scan_subsystems udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev ++#define udev_enumerate_get_list_entry udev_enumerate_get_list_entry_dylibloader_wrapper_libudev ++#define udev_queue_ref udev_queue_ref_dylibloader_wrapper_libudev ++#define udev_queue_unref udev_queue_unref_dylibloader_wrapper_libudev ++#define udev_queue_get_udev udev_queue_get_udev_dylibloader_wrapper_libudev ++#define udev_queue_new udev_queue_new_dylibloader_wrapper_libudev ++#define udev_queue_get_kernel_seqnum udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev ++#define udev_queue_get_udev_seqnum udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev ++#define udev_queue_get_udev_is_active udev_queue_get_udev_is_active_dylibloader_wrapper_libudev ++#define udev_queue_get_queue_is_empty udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev ++#define udev_queue_get_seqnum_is_finished udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev ++#define udev_queue_get_seqnum_sequence_is_finished udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev ++#define udev_queue_get_fd udev_queue_get_fd_dylibloader_wrapper_libudev ++#define udev_queue_flush udev_queue_flush_dylibloader_wrapper_libudev ++#define udev_queue_get_queued_list_entry udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev ++#define udev_hwdb_new udev_hwdb_new_dylibloader_wrapper_libudev ++#define udev_hwdb_ref udev_hwdb_ref_dylibloader_wrapper_libudev ++#define udev_hwdb_unref udev_hwdb_unref_dylibloader_wrapper_libudev ++#define udev_hwdb_get_properties_list_entry udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev ++#define udev_util_encode_string udev_util_encode_string_dylibloader_wrapper_libudev ++extern struct udev* (*udev_ref_dylibloader_wrapper_libudev)(struct udev*); ++extern struct udev* (*udev_unref_dylibloader_wrapper_libudev)(struct udev*); ++extern struct udev* (*udev_new_dylibloader_wrapper_libudev)( void); ++extern void (*udev_set_log_fn_dylibloader_wrapper_libudev)(struct udev*, void*); ++extern int (*udev_get_log_priority_dylibloader_wrapper_libudev)(struct udev*); ++extern void (*udev_set_log_priority_dylibloader_wrapper_libudev)(struct udev*, int); ++extern void* (*udev_get_userdata_dylibloader_wrapper_libudev)(struct udev*); ++extern void (*udev_set_userdata_dylibloader_wrapper_libudev)(struct udev*, void*); ++extern struct udev_list_entry* (*udev_list_entry_get_next_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++extern struct udev_list_entry* (*udev_list_entry_get_by_name_dylibloader_wrapper_libudev)(struct udev_list_entry*,const char*); ++extern const char* (*udev_list_entry_get_name_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++extern const char* (*udev_list_entry_get_value_dylibloader_wrapper_libudev)(struct udev_list_entry*); ++extern struct udev_device* (*udev_device_ref_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_device* (*udev_device_unref_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev* (*udev_device_get_udev_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_device* (*udev_device_new_from_syspath_dylibloader_wrapper_libudev)(struct udev*,const char*); ++extern struct udev_device* (*udev_device_new_from_devnum_dylibloader_wrapper_libudev)(struct udev*, char, dev_t); ++extern struct udev_device* (*udev_device_new_from_subsystem_sysname_dylibloader_wrapper_libudev)(struct udev*,const char*,const char*); ++extern struct udev_device* (*udev_device_new_from_device_id_dylibloader_wrapper_libudev)(struct udev*,const char*); ++extern struct udev_device* (*udev_device_new_from_environment_dylibloader_wrapper_libudev)(struct udev*); ++extern struct udev_device* (*udev_device_get_parent_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_device* (*udev_device_get_parent_with_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*); ++extern const char* (*udev_device_get_devpath_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_subsystem_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_devtype_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_syspath_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_sysname_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_sysnum_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_devnode_dylibloader_wrapper_libudev)(struct udev_device*); ++extern int (*udev_device_get_is_initialized_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_list_entry* (*udev_device_get_devlinks_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_list_entry* (*udev_device_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_list_entry* (*udev_device_get_tags_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++extern struct udev_list_entry* (*udev_device_get_sysattr_list_entry_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_property_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++extern const char* (*udev_device_get_driver_dylibloader_wrapper_libudev)(struct udev_device*); ++extern dev_t (*udev_device_get_devnum_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_action_dylibloader_wrapper_libudev)(struct udev_device*); ++extern unsigned long long int (*udev_device_get_seqnum_dylibloader_wrapper_libudev)(struct udev_device*); ++extern unsigned long long int (*udev_device_get_usec_since_initialized_dylibloader_wrapper_libudev)(struct udev_device*); ++extern const char* (*udev_device_get_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++extern int (*udev_device_set_sysattr_value_dylibloader_wrapper_libudev)(struct udev_device*,const char*,const char*); ++extern int (*udev_device_has_tag_dylibloader_wrapper_libudev)(struct udev_device*,const char*); ++extern struct udev_monitor* (*udev_monitor_ref_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern struct udev_monitor* (*udev_monitor_unref_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern struct udev* (*udev_monitor_get_udev_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern struct udev_monitor* (*udev_monitor_new_from_netlink_dylibloader_wrapper_libudev)(struct udev*,const char*); ++extern int (*udev_monitor_enable_receiving_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern int (*udev_monitor_set_receive_buffer_size_dylibloader_wrapper_libudev)(struct udev_monitor*, int); ++extern int (*udev_monitor_get_fd_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern struct udev_device* (*udev_monitor_receive_device_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern int (*udev_monitor_filter_add_match_subsystem_devtype_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*,const char*); ++extern int (*udev_monitor_filter_add_match_tag_dylibloader_wrapper_libudev)(struct udev_monitor*,const char*); ++extern int (*udev_monitor_filter_update_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern int (*udev_monitor_filter_remove_dylibloader_wrapper_libudev)(struct udev_monitor*); ++extern struct udev_enumerate* (*udev_enumerate_ref_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern struct udev_enumerate* (*udev_enumerate_unref_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern struct udev* (*udev_enumerate_get_udev_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern struct udev_enumerate* (*udev_enumerate_new_dylibloader_wrapper_libudev)(struct udev*); ++extern int (*udev_enumerate_add_match_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++extern int (*udev_enumerate_add_nomatch_subsystem_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++extern int (*udev_enumerate_add_match_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++extern int (*udev_enumerate_add_nomatch_sysattr_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++extern int (*udev_enumerate_add_match_property_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*,const char*); ++extern int (*udev_enumerate_add_match_sysname_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++extern int (*udev_enumerate_add_match_tag_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++extern int (*udev_enumerate_add_match_parent_dylibloader_wrapper_libudev)(struct udev_enumerate*,struct udev_device*); ++extern int (*udev_enumerate_add_match_is_initialized_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern int (*udev_enumerate_add_syspath_dylibloader_wrapper_libudev)(struct udev_enumerate*,const char*); ++extern int (*udev_enumerate_scan_devices_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern int (*udev_enumerate_scan_subsystems_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern struct udev_list_entry* (*udev_enumerate_get_list_entry_dylibloader_wrapper_libudev)(struct udev_enumerate*); ++extern struct udev_queue* (*udev_queue_ref_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern struct udev_queue* (*udev_queue_unref_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern struct udev* (*udev_queue_get_udev_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern struct udev_queue* (*udev_queue_new_dylibloader_wrapper_libudev)(struct udev*); ++extern unsigned long long int (*udev_queue_get_kernel_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern unsigned long long int (*udev_queue_get_udev_seqnum_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern int (*udev_queue_get_udev_is_active_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern int (*udev_queue_get_queue_is_empty_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern int (*udev_queue_get_seqnum_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int); ++extern int (*udev_queue_get_seqnum_sequence_is_finished_dylibloader_wrapper_libudev)(struct udev_queue*, unsigned long long int, unsigned long long int); ++extern int (*udev_queue_get_fd_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern int (*udev_queue_flush_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern struct udev_list_entry* (*udev_queue_get_queued_list_entry_dylibloader_wrapper_libudev)(struct udev_queue*); ++extern struct udev_hwdb* (*udev_hwdb_new_dylibloader_wrapper_libudev)(struct udev*); ++extern struct udev_hwdb* (*udev_hwdb_ref_dylibloader_wrapper_libudev)(struct udev_hwdb*); ++extern struct udev_hwdb* (*udev_hwdb_unref_dylibloader_wrapper_libudev)(struct udev_hwdb*); ++extern struct udev_list_entry* (*udev_hwdb_get_properties_list_entry_dylibloader_wrapper_libudev)(struct udev_hwdb*,const char*, unsigned); ++extern int (*udev_util_encode_string_dylibloader_wrapper_libudev)(const char*, char*, size_t); ++int initialize_libudev(); ++#ifdef __cplusplus ++} ++#endif ++#endif +diff --git a/platform/x11es/logo.png b/platform/x11es/logo.png +new file mode 100644 +index 0000000000000000000000000000000000000000..078654b757f2707aa8f1333c368ea48f73cc2cd3 +GIT binary patch +literal 1679 +zcmV;A25|X_P)lQ61ni*UauD? +zpL{a-9WgO6xa+RFP*PG75-^n4vnawRYep9d0Yf7o8$5+LQRA9#EVf1uv)Dyl0wh!xbemtasMsn;HTGPG2pK|VYiym +zp+kp|fGUnFp@29>%N~h3P`IGc;c&Q|PG>5;yY8;bQ?PrC0jTz&waSFX=?VDht=v!@ +zZ06XL7_nrY43{eYgamLAT~t(*DLOjZ6B`?w9tZ?3dH)_gT4o5K#(+H&qp@zV9>2Y2 +zgIXItpAQs#yWMWjVif!*+7lEOnLar=*_xA+6DTMs$jQviy!w`#u5TOkMIqMo6cE#S7=7t*s5JJ^0{*p)>^5D3C0Q2^?=RDt^CTPmir-Wo3a2F1R2& +zB_-uqdf3|1(gKzG`g(+0RaI4ZKUhKmr?6;~WP3iQ&ZwxlPfAKM<>uzPPCxzhq*G2g +zvyG&MD$uC5NMH8wV4@#4i;wrm-grhOtfz6;Zk$M=|H?TiWwN>7ii +zrKP1&7hZT_E<@kNX0u_&j2Z3T)7piE-nVSof{7C+;)4%90Ox`mt0G#Ng4;+cKYKKz +zpiz9r?Cfk8Jy&yo8W*j5?z!i1B-^}sGnh6oeQFw1#xt6Ee3#FMBiDjL^gMEwtgI|Q +z!=FVlZ}a$JaF&d8~K+lj_xT!B8At9Z)f5GGN;HjscLb$D5xe{9T +z(@#HvjR@?Tp@Qs20hxUFYCoXl=EOwBE>4bTS1Za&v3~l{%{sjL^0NrH6)RRi%f9&X +zD=>xaDNqZN>-|Dz0#5B3tvp0u0joxQV}r&rur-)FyND +zQc^+6v15WA(LRB@@4g#{vK>2jV){QB2u=}L+e={ANF(Q!Ay`#Z|Ka1Vd$@LbcpZd` +zP^2msN$&Bsco(%va^o;&<)QN@I^1#RT{x6Yoi-i)-n9VLc5pt)+BdhQrnGV5-|=|Q3!+1NEbkYZ3xnBIsHLoR=MDw8( +z{?Erla>@DRR=pw~KO<9o^U3XE{NT65&H=BAx|>fi{z9+s=2^d0Mg7di^Q6^mdi9$- +z#DYPuiD38V#Kubt#YbG@266^Tl|^pSI)P!C|seAQMjp`b1o?%3EG<<+>8vwEJNic(IkE?H2b-+ +zM}^*dDJq)xLgUg%lAP<7YlUx4j{Gm!ASYSm0`@Sh^wz=}FcY ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++// ICCCM ++#define WM_NormalState 1L // window normal state ++#define WM_IconicState 3L // window minimized ++// EWMH ++#define _NET_WM_STATE_REMOVE 0L // remove/unset property ++#define _NET_WM_STATE_ADD 1L // add/set property ++#define _NET_WM_STATE_TOGGLE 2L // toggle property ++ ++#include ++#include ++#include ++#include ++#include ++ ++//stupid linux.h ++#ifdef KEY_TAB ++#undef KEY_TAB ++#endif ++ ++#undef CursorShape ++ ++#include ++ ++// 2.2 is the first release with multitouch ++#define XINPUT_CLIENT_VERSION_MAJOR 2 ++#define XINPUT_CLIENT_VERSION_MINOR 2 ++ ++#define VALUATOR_ABSX 0 ++#define VALUATOR_ABSY 1 ++#define VALUATOR_PRESSURE 2 ++#define VALUATOR_TILTX 3 ++#define VALUATOR_TILTY 4 ++ ++//#define DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED ++#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED ++#define DEBUG_LOG_X11(...) printf(__VA_ARGS__) ++#else ++#define DEBUG_LOG_X11(...) ++#endif ++ ++static const double abs_resolution_mult = 10000.0; ++static const double abs_resolution_range_mult = 10.0; ++ ++static String get_atom_name(Display *p_disp, Atom p_atom) { ++ char *name = XGetAtomName(p_disp, p_atom); ++ ERR_FAIL_NULL_V_MSG(name, String(), "Atom is invalid."); ++ String ret; ++ ret.parse_utf8(name); ++ XFree(name); ++ return ret; ++} ++ ++#ifdef SPEECHD_ENABLED ++ ++bool OS_X11::tts_is_speaking() const { ++ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND_V(!tts, false); ++ return tts->is_speaking(); ++} ++ ++bool OS_X11::tts_is_paused() const { ++ ERR_FAIL_COND_V_MSG(!tts, false, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND_V(!tts, false); ++ return tts->is_paused(); ++} ++ ++Array OS_X11::tts_get_voices() const { ++ ERR_FAIL_COND_V_MSG(!tts, Array(), "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND_V(!tts, Array()); ++ return tts->get_voices(); ++} ++ ++void OS_X11::tts_speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) { ++ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND(!tts); ++ tts->speak(p_text, p_voice, p_volume, p_pitch, p_rate, p_utterance_id, p_interrupt); ++} ++ ++void OS_X11::tts_pause() { ++ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND(!tts); ++ tts->pause(); ++} ++ ++void OS_X11::tts_resume() { ++ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND(!tts); ++ tts->resume(); ++} ++ ++void OS_X11::tts_stop() { ++ ERR_FAIL_COND_MSG(!tts, "Enable the \"audio/general/text_to_speech\" project setting to use text-to-speech."); ++ ERR_FAIL_COND(!tts); ++ tts->stop(); ++} ++ ++#endif ++ ++void OS_X11::initialize_core() { ++ crash_handler.initialize(); ++ ++ OS_Unix::initialize_core(); ++} ++ ++int OS_X11::get_current_video_driver() const { ++ return video_driver_index; ++} ++ ++Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { ++ long im_event_mask = 0; ++ last_button_state = 0; ++ ++ xmbstring = nullptr; ++ x11_window = 0; ++ last_click_ms = 0; ++ last_click_button_index = -1; ++ last_click_pos = Point2(-100, -100); ++ args = OS::get_singleton()->get_cmdline_args(); ++ current_videomode = p_desired; ++ main_loop = nullptr; ++ last_timestamp = 0; ++ last_mouse_pos_valid = false; ++ last_keyrelease_time = 0; ++ xdnd_version = 0; ++ ++ XInitThreads(); ++ ++ /** XLIB INITIALIZATION **/ ++ x11_display = XOpenDisplay(nullptr); ++ ++ if (!x11_display) { ++ ERR_PRINT("X11 Display is not available"); ++ return ERR_UNAVAILABLE; ++ } ++ ++ char *modifiers = nullptr; ++ Bool xkb_dar = False; ++ XAutoRepeatOn(x11_display); ++ xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, nullptr); ++ ++ // Try to support IME if detectable auto-repeat is supported ++ if (xkb_dar == True) { ++#ifdef X_HAVE_UTF8_STRING ++ // Xutf8LookupString will be used later instead of XmbLookupString before ++ // the multibyte sequences can be converted to unicode string. ++ modifiers = XSetLocaleModifiers(""); ++#endif ++ } ++ ++ if (modifiers == nullptr) { ++ if (is_stdout_verbose()) { ++ WARN_PRINT("IME is disabled"); ++ } ++ XSetLocaleModifiers("@im=none"); ++ WARN_PRINT("Error setting locale modifiers"); ++ } ++ ++ const char *err; ++ xrr_get_monitors = nullptr; ++ xrr_free_monitors = nullptr; ++ int xrandr_major = 0; ++ int xrandr_minor = 0; ++ int event_base, error_base; ++ xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base); ++ xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY); ++ if (!xrandr_handle) { ++ err = dlerror(); ++ // For some arcane reason, NetBSD now ships libXrandr.so.3 while the rest of the world has libXrandr.so.2... ++ // In case this happens for other X11 platforms in the future, let's give it a try too before failing. ++ xrandr_handle = dlopen("libXrandr.so.3", RTLD_LAZY); ++ if (!xrandr_handle) { ++ fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err); ++ } ++ } else { ++ XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor); ++ if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) { ++ xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors"); ++ if (!xrr_get_monitors) { ++ err = dlerror(); ++ fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err); ++ } else { ++ xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors"); ++ if (!xrr_free_monitors) { ++ err = dlerror(); ++ fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err); ++ xrr_get_monitors = nullptr; ++ } ++ } ++ } ++ } ++ ++ if (!refresh_device_info()) { ++ OS::get_singleton()->alert("Your system does not support XInput 2.\n" ++ "Please upgrade your distribution.", ++ "Unable to initialize XInput"); ++ return ERR_UNAVAILABLE; ++ } ++ ++ xim = XOpenIM(x11_display, nullptr, nullptr, nullptr); ++ ++ if (xim == nullptr) { ++ WARN_PRINT("XOpenIM failed"); ++ xim_style = 0L; ++ } else { ++ ::XIMCallback im_destroy_callback; ++ im_destroy_callback.client_data = (::XPointer)(this); ++ im_destroy_callback.callback = (::XIMProc)(xim_destroy_callback); ++ if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback, ++ NULL) != nullptr) { ++ WARN_PRINT("Error setting XIM destroy callback"); ++ } ++ ++ ::XIMStyles *xim_styles = nullptr; ++ xim_style = 0L; ++ char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); ++ if (imvalret != nullptr || xim_styles == nullptr) { ++ fprintf(stderr, "Input method doesn't support any styles\n"); ++ } ++ ++ if (xim_styles) { ++ xim_style = 0L; ++ for (int i = 0; i < xim_styles->count_styles; i++) { ++ if (xim_styles->supported_styles[i] == ++ (XIMPreeditNothing | XIMStatusNothing)) { ++ xim_style = xim_styles->supported_styles[i]; ++ break; ++ } ++ } ++ ++ XFree(xim_styles); ++ } ++ XFree(imvalret); ++ } ++ ++/* ++ char* windowid = getenv("GODOT_WINDOWID"); ++ if (windowid) { ++ ++ //freopen("/home/punto/stdout", "w", stdout); ++ //reopen("/home/punto/stderr", "w", stderr); ++ x11_window = atol(windowid); ++ ++ XWindowAttributes xwa; ++ XGetWindowAttributes(x11_display,x11_window,&xwa); ++ ++ current_videomode.width = xwa.width; ++ current_videomode.height = xwa.height; ++ }; ++ */ ++ ++// maybe contextgl wants to be in charge of creating the window ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ if (getenv("DRI_PRIME") == nullptr) { ++ int use_prime = -1; ++ ++ if (getenv("PRIMUS_DISPLAY") || ++ getenv("PRIMUS_libGLd") || ++ getenv("PRIMUS_libGLa") || ++ getenv("PRIMUS_libGL") || ++ getenv("PRIMUS_LOAD_GLOBAL") || ++ getenv("BUMBLEBEE_SOCKET")) { ++ print_verbose("Optirun/primusrun detected. Skipping GPU detection"); ++ use_prime = 0; ++ } ++ ++ // Some tools use fake libGL libraries and have them override the real one using ++ // LD_LIBRARY_PATH, so we skip them. *But* Steam also sets LD_LIBRARY_PATH for its ++ // runtime and includes system `/lib` and `/lib64`... so ignore Steam. ++ if (use_prime == -1 && getenv("LD_LIBRARY_PATH") && !getenv("STEAM_RUNTIME_LIBRARY_PATH")) { ++ String ld_library_path(getenv("LD_LIBRARY_PATH")); ++ Vector libraries = ld_library_path.split(":"); ++ ++ for (int i = 0; i < libraries.size(); ++i) { ++ if (FileAccess::exists(libraries[i] + "/libGL.so.1") || ++ FileAccess::exists(libraries[i] + "/libGL.so")) { ++ print_verbose("Custom libGL override detected. Skipping GPU detection"); ++ use_prime = 0; ++ } ++ } ++ } ++ ++ if (use_prime == -1) { ++ print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic."); ++ use_prime = detect_prime(); ++ } ++ ++ if (use_prime) { ++ print_line("Found discrete GPU, setting DRI_PRIME=1 to use it."); ++ print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU."); ++ setenv("DRI_PRIME", "1", 1); ++ } ++ } ++ ++ ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE; ++ ++ if (p_video_driver == VIDEO_DRIVER_GLES2) { ++ opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; ++ } ++ ++ bool editor = Engine::get_singleton()->is_editor_hint(); ++ bool gl_initialization_error = false; ++ ++ context_gl = nullptr; ++ while (!context_gl) { ++ context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); ++ ++ if (context_gl->initialize() != OK) { ++ memdelete(context_gl); ++ context_gl = nullptr; ++ ++ if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) { ++ if (p_video_driver == VIDEO_DRIVER_GLES2) { ++ gl_initialization_error = true; ++ break; ++ } ++ ++ p_video_driver = VIDEO_DRIVER_GLES2; ++ opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; ++ } else { ++ gl_initialization_error = true; ++ break; ++ } ++ } ++ } ++ ++ while (true) { ++ if (opengl_api_type == ContextGL_X11::GLES_3_0_COMPATIBLE) { ++ if (RasterizerGLES3::is_viable() == OK) { ++ RasterizerGLES3::register_config(); ++ RasterizerGLES3::make_current(); ++ break; ++ } else { ++ if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) { ++ p_video_driver = VIDEO_DRIVER_GLES2; ++ opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; ++ continue; ++ } else { ++ gl_initialization_error = true; ++ break; ++ } ++ } ++ } ++ ++ if (opengl_api_type == ContextGL_X11::GLES_2_0_COMPATIBLE) { ++ if (RasterizerGLES2::is_viable() == OK) { ++ RasterizerGLES2::register_config(); ++ RasterizerGLES2::make_current(); ++ break; ++ } else { ++ gl_initialization_error = true; ++ break; ++ } ++ } ++ } ++ ++ if (gl_initialization_error) { ++ OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" ++ "Please update your drivers or if you have a very old or integrated GPU, upgrade it.\n" ++ "If you have updated your graphics drivers recently, try rebooting.\n" ++ "Alternatively, you can force software rendering by running Godot with the `LIBGL_ALWAYS_SOFTWARE=1`\n" ++ "environment variable set, but this will be very slow.", ++ "Unable to initialize Video driver"); ++ return ERR_UNAVAILABLE; ++ } ++ ++ video_driver_index = p_video_driver; ++ ++ context_gl->set_use_vsync(current_videomode.use_vsync); ++ ++#endif ++ ++#ifdef SPEECHD_ENABLED ++ // Init TTS ++ bool tts_enabled = GLOBAL_GET("audio/general/text_to_speech"); ++ if (tts_enabled) { ++ tts = memnew(TTS_Linux); ++ } ++#endif ++ ++ visual_server = memnew(VisualServerRaster); ++ if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { ++ visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); ++ } ++ ++ if (current_videomode.maximized) { ++ current_videomode.maximized = false; ++ set_window_maximized(true); ++ // borderless fullscreen window mode ++ } else if (current_videomode.fullscreen) { ++ current_videomode.fullscreen = false; ++ set_window_fullscreen(true); ++ } else if (current_videomode.borderless_window) { ++ Hints hints; ++ Atom property; ++ hints.flags = 2; ++ hints.decorations = 0; ++ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); ++ if (property != None) { ++ XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); ++ } ++ } ++ ++ // make PID known to X11 ++ { ++ const long pid = this->get_process_id(); ++ Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False); ++ if (net_wm_pid != None) { ++ XChangeProperty(x11_display, x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); ++ } ++ } ++ ++ // disable resizable window ++ if (!current_videomode.resizable && !current_videomode.fullscreen) { ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ xsh->flags = PMinSize | PMaxSize; ++ XWindowAttributes xwa; ++ if (current_videomode.fullscreen) { ++ XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa); ++ } else { ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ } ++ xsh->min_width = xwa.width; ++ xsh->max_width = xwa.width; ++ xsh->min_height = xwa.height; ++ xsh->max_height = xwa.height; ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ } ++ ++ if (current_videomode.always_on_top) { ++ current_videomode.always_on_top = false; ++ set_window_always_on_top(true); ++ } ++ ++ ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE); ++ ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE); ++ ++ XSetWindowAttributes new_attr; ++ ++ new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ++ ButtonReleaseMask | EnterWindowMask | ++ LeaveWindowMask | PointerMotionMask | ++ Button1MotionMask | ++ Button2MotionMask | Button3MotionMask | ++ Button4MotionMask | Button5MotionMask | ++ ButtonMotionMask | KeymapStateMask | ++ ExposureMask | VisibilityChangeMask | ++ StructureNotifyMask | ++ SubstructureNotifyMask | SubstructureRedirectMask | ++ FocusChangeMask | PropertyChangeMask | ++ ColormapChangeMask | OwnerGrabButtonMask | ++ im_event_mask; ++ ++ XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); ++ ++ static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {}; ++ static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {}; ++ ++ xi.all_event_mask.deviceid = XIAllDevices; ++ xi.all_event_mask.mask_len = sizeof(all_mask_data); ++ xi.all_event_mask.mask = all_mask_data; ++ ++ xi.all_master_event_mask.deviceid = XIAllMasterDevices; ++ xi.all_master_event_mask.mask_len = sizeof(all_master_mask_data); ++ xi.all_master_event_mask.mask = all_master_mask_data; ++ ++ XISetMask(xi.all_event_mask.mask, XI_HierarchyChanged); ++ XISetMask(xi.all_master_event_mask.mask, XI_DeviceChanged); ++ XISetMask(xi.all_master_event_mask.mask, XI_RawMotion); ++ ++#ifdef TOUCH_ENABLED ++ if (xi.touch_devices.size()) { ++ XISetMask(xi.all_event_mask.mask, XI_TouchBegin); ++ XISetMask(xi.all_event_mask.mask, XI_TouchUpdate); ++ XISetMask(xi.all_event_mask.mask, XI_TouchEnd); ++ XISetMask(xi.all_event_mask.mask, XI_TouchOwnership); ++ } ++#endif ++ ++ XISelectEvents(x11_display, x11_window, &xi.all_event_mask, 1); ++ XISelectEvents(x11_display, DefaultRootWindow(x11_display), &xi.all_master_event_mask, 1); ++ ++ /* set the titlebar name */ ++ XStoreName(x11_display, x11_window, "Godot"); ++ ++ wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); ++ XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); ++ ++ im_active = false; ++ im_position = Vector2(); ++ ++ if (xim && xim_style) { ++ xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)nullptr); ++ if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != nullptr) { ++ WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value"); ++ XDestroyIC(xic); ++ xic = nullptr; ++ } ++ if (xic) { ++ XUnsetICFocus(xic); ++ } else { ++ WARN_PRINT("XCreateIC couldn't create xic"); ++ } ++ } else { ++ xic = nullptr; ++ WARN_PRINT("XCreateIC couldn't create xic"); ++ } ++ ++ cursor_size = XcursorGetDefaultSize(x11_display); ++ cursor_theme = XcursorGetTheme(x11_display); ++ ++ if (!cursor_theme) { ++ print_verbose("XcursorGetTheme could not get cursor theme"); ++ cursor_theme = "default"; ++ } ++ ++ for (int i = 0; i < CURSOR_MAX; i++) { ++ cursors[i] = None; ++ img[i] = nullptr; ++ } ++ ++ current_cursor = CURSOR_ARROW; ++ ++ for (int i = 0; i < CURSOR_MAX; i++) { ++ static const char *cursor_file[] = { ++ "left_ptr", ++ "xterm", ++ "hand2", ++ "cross", ++ "watch", ++ "left_ptr_watch", ++ "fleur", ++ "hand1", ++ "X_cursor", ++ "sb_v_double_arrow", ++ "sb_h_double_arrow", ++ "size_bdiag", ++ "size_fdiag", ++ "hand1", ++ "sb_v_double_arrow", ++ "sb_h_double_arrow", ++ "question_arrow" ++ }; ++ ++ img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size); ++ if (img[i]) { ++ cursors[i] = XcursorImageLoadCursor(x11_display, img[i]); ++ } else { ++ print_verbose("Failed loading custom cursor: " + String(cursor_file[i])); ++ } ++ } ++ ++ { ++ // Creating an empty/transparent cursor ++ ++ // Create 1x1 bitmap ++ Pixmap cursormask = XCreatePixmap(x11_display, ++ RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1); ++ ++ // Fill with zero ++ XGCValues xgc; ++ xgc.function = GXclear; ++ GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); ++ XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1); ++ ++ // Color value doesn't matter. Mask zero means no foreground or background will be drawn ++ XColor col = {}; ++ ++ Cursor cursor = XCreatePixmapCursor(x11_display, ++ cursormask, // source (using cursor mask as placeholder, since it'll all be ignored) ++ cursormask, // mask ++ &col, &col, 0, 0); ++ ++ XFreePixmap(x11_display, cursormask); ++ XFreeGC(x11_display, gc); ++ ++ if (cursor == None) { ++ ERR_PRINT("FAILED CREATING CURSOR"); ++ } ++ ++ null_cursor = cursor; ++ } ++ set_cursor_shape(CURSOR_BUSY); ++ ++ //Set Xdnd (drag & drop) support ++ Atom XdndAware = XInternAtom(x11_display, "XdndAware", False); ++ Atom version = 5; ++ if (XdndAware != None) { ++ XChangeProperty(x11_display, x11_window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1); ++ } ++ ++ xdnd_enter = XInternAtom(x11_display, "XdndEnter", False); ++ xdnd_position = XInternAtom(x11_display, "XdndPosition", False); ++ xdnd_status = XInternAtom(x11_display, "XdndStatus", False); ++ xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False); ++ xdnd_drop = XInternAtom(x11_display, "XdndDrop", False); ++ xdnd_finished = XInternAtom(x11_display, "XdndFinished", False); ++ xdnd_selection = XInternAtom(x11_display, "XdndSelection", False); ++ requested = None; ++ ++ visual_server->init(); ++ ++ AudioDriverManager::initialize(p_audio_driver); ++ ++ input = memnew(InputDefault); ++ ++ window_has_focus = true; // Set focus to true at init ++#ifdef JOYDEV_ENABLED ++ joypad = memnew(JoypadLinux(input)); ++#endif ++ ++ power_manager = memnew(PowerX11); ++ ++ if (p_desired.layered) { ++ set_window_per_pixel_transparency_enabled(true); ++ } ++ ++ XEvent xevent; ++ while (XPending(x11_display) > 0) { ++ XNextEvent(x11_display, &xevent); ++ if (xevent.type == ConfigureNotify) { ++ _window_changed(&xevent); ++ } ++ } ++ ++ events_thread.start(_poll_events_thread, this); ++ ++ update_real_mouse_position(); ++ ++ return OK; ++} ++ ++bool OS_X11::refresh_device_info() { ++ int event_base, error_base; ++ ++ print_verbose("XInput: Refreshing devices."); ++ ++ if (!XQueryExtension(x11_display, "XInputExtension", &xi.opcode, &event_base, &error_base)) { ++ print_verbose("XInput extension not available. Please upgrade your distribution."); ++ return false; ++ } ++ ++ int xi_major_query = XINPUT_CLIENT_VERSION_MAJOR; ++ int xi_minor_query = XINPUT_CLIENT_VERSION_MINOR; ++ ++ if (XIQueryVersion(x11_display, &xi_major_query, &xi_minor_query) != Success) { ++ print_verbose(vformat("XInput 2 not available (server supports %d.%d).", xi_major_query, xi_minor_query)); ++ xi.opcode = 0; ++ return false; ++ } ++ ++ if (xi_major_query < XINPUT_CLIENT_VERSION_MAJOR || (xi_major_query == XINPUT_CLIENT_VERSION_MAJOR && xi_minor_query < XINPUT_CLIENT_VERSION_MINOR)) { ++ print_verbose(vformat("XInput %d.%d not available (server supports %d.%d). Touch input unavailable.", ++ XINPUT_CLIENT_VERSION_MAJOR, XINPUT_CLIENT_VERSION_MINOR, xi_major_query, xi_minor_query)); ++ } ++ ++ xi.absolute_devices.clear(); ++ xi.touch_devices.clear(); ++ xi.pen_inverted_devices.clear(); ++ ++ int dev_count; ++ XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count); ++ ++ for (int i = 0; i < dev_count; i++) { ++ XIDeviceInfo *dev = &info[i]; ++ if (!dev->enabled) { ++ continue; ++ } ++ if (!(dev->use == XISlavePointer || dev->use == XIFloatingSlave)) { ++ continue; ++ } ++ ++ bool direct_touch = false; ++ bool absolute_mode = false; ++ int resolution_x = 0; ++ int resolution_y = 0; ++ double abs_x_min = 0; ++ double abs_x_max = 0; ++ double abs_y_min = 0; ++ double abs_y_max = 0; ++ double pressure_min = 0; ++ double pressure_max = 0; ++ double tilt_x_min = 0; ++ double tilt_x_max = 0; ++ double tilt_y_min = 0; ++ double tilt_y_max = 0; ++ for (int j = 0; j < dev->num_classes; j++) { ++#ifdef TOUCH_ENABLED ++ if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { ++ direct_touch = true; ++ } ++#endif ++ if (dev->classes[j]->type == XIValuatorClass) { ++ XIValuatorClassInfo *class_info = (XIValuatorClassInfo *)dev->classes[j]; ++ ++ if (class_info->number == VALUATOR_ABSX && class_info->mode == XIModeAbsolute) { ++ resolution_x = class_info->resolution; ++ abs_x_min = class_info->min; ++ abs_x_max = class_info->max; ++ absolute_mode = true; ++ } else if (class_info->number == VALUATOR_ABSY && class_info->mode == XIModeAbsolute) { ++ resolution_y = class_info->resolution; ++ abs_y_min = class_info->min; ++ abs_y_max = class_info->max; ++ absolute_mode = true; ++ } else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) { ++ pressure_min = class_info->min; ++ pressure_max = class_info->max; ++ } else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) { ++ tilt_x_min = class_info->min; ++ tilt_x_max = class_info->max; ++ } else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) { ++ tilt_y_min = class_info->min; ++ tilt_y_max = class_info->max; ++ } ++ } ++ } ++ if (direct_touch) { ++ xi.touch_devices.push_back(dev->deviceid); ++ print_verbose("XInput: Using touch device: " + String(dev->name)); ++ } ++ if (absolute_mode) { ++ // If no resolution was reported, use the min/max ranges. ++ if (resolution_x <= 0) { ++ resolution_x = (abs_x_max - abs_x_min) * abs_resolution_range_mult; ++ } ++ if (resolution_y <= 0) { ++ resolution_y = (abs_y_max - abs_y_min) * abs_resolution_range_mult; ++ } ++ xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y); ++ print_verbose("XInput: Absolute pointing device: " + String(dev->name)); ++ } ++ ++ xi.pressure = 0; ++ xi.pen_pressure_range[dev->deviceid] = Vector2(pressure_min, pressure_max); ++ xi.pen_tilt_x_range[dev->deviceid] = Vector2(tilt_x_min, tilt_x_max); ++ xi.pen_tilt_y_range[dev->deviceid] = Vector2(tilt_y_min, tilt_y_max); ++ xi.pen_inverted_devices[dev->deviceid] = String(dev->name).findn("eraser") > 0; ++ } ++ ++ XIFreeDeviceInfo(info); ++#ifdef TOUCH_ENABLED ++ if (!xi.touch_devices.size()) { ++ print_verbose("XInput: No touch devices found."); ++ } ++#endif ++ ++ return true; ++} ++ ++void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, ++ ::XPointer call_data) { ++ WARN_PRINT("Input method stopped"); ++ OS_X11 *os = reinterpret_cast(client_data); ++ os->xim = nullptr; ++ os->xic = nullptr; ++} ++ ++void OS_X11::set_ime_active(const bool p_active) { ++ im_active = p_active; ++ ++ if (!xic) { ++ return; ++ } ++ ++ // Block events polling while changing input focus ++ // because it triggers some event polling internally. ++ if (p_active) { ++ { ++ MutexLock mutex_lock(events_mutex); ++ XSetICFocus(xic); ++ } ++ set_ime_position(im_position); ++ } else { ++ MutexLock mutex_lock(events_mutex); ++ XUnsetICFocus(xic); ++ } ++} ++ ++void OS_X11::set_ime_position(const Point2 &p_pos) { ++ im_position = p_pos; ++ ++ if (!xic) { ++ return; ++ } ++ ++ ::XPoint spot; ++ spot.x = short(p_pos.x); ++ spot.y = short(p_pos.y); ++ XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); ++ ++ { ++ // Block events polling during this call ++ // because it triggers some event polling internally. ++ MutexLock mutex_lock(events_mutex); ++ XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL); ++ } ++ ++ XFree(preedit_attr); ++} ++ ++String OS_X11::get_unique_id() const { ++ static String machine_id; ++ if (machine_id.empty()) { ++ if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) { ++ while (machine_id.empty() && !f->eof_reached()) { ++ machine_id = f->get_line().strip_edges(); ++ } ++ f->close(); ++ memdelete(f); ++ } ++ } ++ return machine_id; ++} ++ ++String OS_X11::get_processor_name() const { ++ FileAccessRef f = FileAccess::open("/proc/cpuinfo", FileAccess::READ); ++ ERR_FAIL_COND_V_MSG(!f, "", String("Couldn't open `/proc/cpuinfo` to get the CPU model name. Returning an empty string.")); ++ ++ while (!f->eof_reached()) { ++ const String line = f->get_line(); ++ if (line.find("model name") != -1) { ++ return line.split(":")[1].strip_edges(); ++ } ++ } ++ ++ ERR_FAIL_V_MSG("", String("Couldn't get the CPU model name from `/proc/cpuinfo`. Returning an empty string.")); ++} ++ ++void OS_X11::finalize() { ++ events_thread_done = true; ++ events_thread.wait_to_finish(); ++ ++ if (main_loop) { ++ memdelete(main_loop); ++ } ++ main_loop = nullptr; ++ ++ /* ++ if (debugger_connection_console) { ++ memdelete(debugger_connection_console); ++ } ++ */ ++#ifdef ALSAMIDI_ENABLED ++ driver_alsamidi.close(); ++#endif ++ ++#ifdef SPEECHD_ENABLED ++ if (tts) { ++ memdelete(tts); ++ } ++#endif ++ ++#ifdef JOYDEV_ENABLED ++ memdelete(joypad); ++#endif ++ ++ xi.touch_devices.clear(); ++ xi.state.clear(); ++ ++ memdelete(input); ++ ++ cursors_cache.clear(); ++ visual_server->finish(); ++ memdelete(visual_server); ++ //memdelete(rasterizer); ++ ++ memdelete(power_manager); ++ ++ if (xrandr_handle) { ++ dlclose(xrandr_handle); ++ } ++ ++ if (!OS::get_singleton()->is_no_window_mode_enabled()) { ++ XUnmapWindow(x11_display, x11_window); ++ } ++ XDestroyWindow(x11_display, x11_window); ++ ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ memdelete(context_gl); ++#endif ++ for (int i = 0; i < CURSOR_MAX; i++) { ++ if (cursors[i] != None) { ++ XFreeCursor(x11_display, cursors[i]); ++ } ++ if (img[i] != nullptr) { ++ XcursorImageDestroy(img[i]); ++ } ++ }; ++ ++ if (xic) { ++ XDestroyIC(xic); ++ } ++ if (xim) { ++ XCloseIM(xim); ++ } ++ ++ XCloseDisplay(x11_display); ++ if (xmbstring) { ++ memfree(xmbstring); ++ } ++ ++ args.clear(); ++} ++ ++bool OS_X11::is_offscreen_gl_available() const { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ return context_gl->is_offscreen_available(); ++#else ++ return false; ++#endif ++} ++ ++void OS_X11::set_offscreen_gl_current(bool p_current) { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ if (p_current) { ++ return context_gl->make_offscreen_current(); ++ } else { ++ return context_gl->release_offscreen_current(); ++ } ++#endif ++} ++ ++void OS_X11::set_mouse_mode(MouseMode p_mode) { ++ if (p_mode == mouse_mode) { ++ return; ++ } ++ ++ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { ++ XUngrabPointer(x11_display, CurrentTime); ++ } ++ ++ // The only modes that show a cursor are VISIBLE and CONFINED ++ bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); ++ ++ if (showCursor) { ++ XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor ++ } else { ++ XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor ++ } ++ ++ mouse_mode = p_mode; ++ ++ if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { ++ //flush pending motion events ++ flush_mouse_motion(); ++ ++ if (XGrabPointer( ++ x11_display, x11_window, True, ++ ButtonPressMask | ButtonReleaseMask | PointerMotionMask, ++ GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime) != GrabSuccess) { ++ ERR_PRINT("NO GRAB"); ++ } ++ ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ center.x = current_videomode.width / 2; ++ center.y = current_videomode.height / 2; ++ ++ XWarpPointer(x11_display, None, x11_window, ++ 0, 0, 0, 0, (int)center.x, (int)center.y); ++ ++ input->set_mouse_position(center); ++ } ++ } else { ++ do_mouse_warp = false; ++ } ++ ++ XFlush(x11_display); ++} ++ ++void OS_X11::warp_mouse_position(const Point2 &p_to) { ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ last_mouse_pos = p_to; ++ } else { ++ /*XWindowAttributes xwa; ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ printf("%d %d\n", xwa.x, xwa.y); needed? */ ++ ++ XWarpPointer(x11_display, None, x11_window, ++ 0, 0, 0, 0, (int)p_to.x, (int)p_to.y); ++ } ++} ++ ++void OS_X11::flush_mouse_motion() { ++ // Block events polling while flushing motion events. ++ MutexLock mutex_lock(events_mutex); ++ ++ for (uint32_t event_index = 0; event_index < polled_events.size(); ++event_index) { ++ XEvent &event = polled_events[event_index]; ++ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) { ++ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; ++ if (event_data->evtype == XI_RawMotion) { ++ XFreeEventData(x11_display, &event.xcookie); ++ polled_events.remove(event_index--); ++ continue; ++ } ++ XFreeEventData(x11_display, &event.xcookie); ++ break; ++ } ++ } ++ ++ xi.relative_motion.x = 0; ++ xi.relative_motion.y = 0; ++} ++ ++OS::MouseMode OS_X11::get_mouse_mode() const { ++ return mouse_mode; ++} ++ ++int OS_X11::get_mouse_button_state() const { ++ return last_button_state; ++} ++ ++Point2 OS_X11::get_mouse_position() const { ++ return last_mouse_pos; ++} ++ ++bool OS_X11::get_window_per_pixel_transparency_enabled() const { ++ if (!is_layered_allowed()) { ++ return false; ++ } ++ return layered_window; ++} ++ ++void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) { ++ if (!is_layered_allowed()) { ++ return; ++ } ++ if (layered_window != p_enabled) { ++ if (p_enabled) { ++ layered_window = true; ++ } else { ++ layered_window = false; ++ } ++ } ++} ++ ++void OS_X11::set_window_title(const String &p_title) { ++ XStoreName(x11_display, x11_window, p_title.utf8().get_data()); ++ ++ Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false); ++ Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false); ++ if (_net_wm_name != None && utf8_string != None) { ++ XChangeProperty(x11_display, x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length()); ++ } ++} ++ ++void OS_X11::set_window_mouse_passthrough(const PoolVector2Array &p_region) { ++ int event_base, error_base; ++ const Bool ext_okay = XShapeQueryExtension(x11_display, &event_base, &error_base); ++ if (ext_okay) { ++ Region region; ++ if (p_region.size() == 0) { ++ region = XCreateRegion(); ++ XRectangle rect; ++ rect.x = 0; ++ rect.y = 0; ++ rect.width = get_real_window_size().x; ++ rect.height = get_real_window_size().y; ++ XUnionRectWithRegion(&rect, region, region); ++ } else { ++ XPoint *points = (XPoint *)memalloc(sizeof(XPoint) * p_region.size()); ++ for (int i = 0; i < p_region.size(); i++) { ++ points[i].x = p_region[i].x; ++ points[i].y = p_region[i].y; ++ } ++ region = XPolygonRegion(points, p_region.size(), EvenOddRule); ++ memfree(points); ++ } ++ XShapeCombineRegion(x11_display, x11_window, ShapeInput, 0, 0, region, ShapeSet); ++ XDestroyRegion(region); ++ } ++} ++ ++void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) { ++} ++ ++OS::VideoMode OS_X11::get_video_mode(int p_screen) const { ++ return current_videomode; ++} ++ ++void OS_X11::get_fullscreen_mode_list(List *p_list, int p_screen) const { ++} ++ ++void OS_X11::set_wm_fullscreen(bool p_enabled) { ++ if (p_enabled && !get_borderless_window()) { ++ // remove decorations if the window is not already borderless ++ Hints hints; ++ Atom property; ++ hints.flags = 2; ++ hints.decorations = 0; ++ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); ++ if (property != None) { ++ XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); ++ } ++ } ++ ++ if (p_enabled && !is_window_resizable()) { ++ // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. ++ XSizeHints *xsh; ++ ++ xsh = XAllocSizeHints(); ++ xsh->flags = 0L; ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ } ++ ++ // Using EWMH -- Extended Window Manager Hints ++ XEvent xev; ++ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = wm_state; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; ++ xev.xclient.data.l[1] = wm_fullscreen; ++ xev.xclient.data.l[2] = 0; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++ ++ // set bypass compositor hint ++ Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); ++ unsigned long compositing_disable_on = p_enabled ? 1 : 0; ++ if (bypass_compositor != None) { ++ XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); ++ } ++ ++ XFlush(x11_display); ++ ++ if (!p_enabled) { ++ // Reset the non-resizable flags if we un-set these before. ++ Size2 size = get_window_size(); ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ if (!is_window_resizable()) { ++ xsh->flags = PMinSize | PMaxSize; ++ xsh->min_width = size.x; ++ xsh->max_width = size.x; ++ xsh->min_height = size.y; ++ xsh->max_height = size.y; ++ } else { ++ xsh->flags = 0L; ++ if (min_size != Size2()) { ++ xsh->flags |= PMinSize; ++ xsh->min_width = min_size.x; ++ xsh->min_height = min_size.y; ++ } ++ if (max_size != Size2()) { ++ xsh->flags |= PMaxSize; ++ xsh->max_width = max_size.x; ++ xsh->max_height = max_size.y; ++ } ++ } ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ ++ // put back or remove decorations according to the last set borderless state ++ Hints hints; ++ Atom property; ++ hints.flags = 2; ++ hints.decorations = current_videomode.borderless_window ? 0 : 1; ++ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); ++ if (property != None) { ++ XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); ++ } ++ } ++} ++ ++void OS_X11::set_wm_above(bool p_enabled) { ++ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False); ++ ++ XClientMessageEvent xev; ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.window = x11_window; ++ xev.message_type = wm_state; ++ xev.format = 32; ++ xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; ++ xev.data.l[1] = wm_above; ++ xev.data.l[3] = 1; ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev); ++} ++ ++int OS_X11::get_screen_count() const { ++ // Using Xinerama Extension ++ int event_base, error_base; ++ const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); ++ if (!ext_okay) { ++ return 0; ++ } ++ ++ int count; ++ XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); ++ XFree(xsi); ++ return count; ++} ++ ++int OS_X11::get_current_screen() const { ++ int x, y; ++ Window child; ++ XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child); ++ ++ int count = get_screen_count(); ++ for (int i = 0; i < count; i++) { ++ Point2i pos = get_screen_position(i); ++ Size2i size = get_screen_size(i); ++ if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height)) { ++ return i; ++ } ++ } ++ return 0; ++} ++ ++void OS_X11::set_current_screen(int p_screen) { ++ if (p_screen == -1) { ++ p_screen = get_current_screen(); ++ } ++ ++ // Check if screen is valid ++ ERR_FAIL_INDEX(p_screen, get_screen_count()); ++ ++ if (current_videomode.fullscreen) { ++ Point2i position = get_screen_position(p_screen); ++ Size2i size = get_screen_size(p_screen); ++ ++ XMoveResizeWindow(x11_display, x11_window, position.x, position.y, size.x, size.y); ++ } else { ++ if (p_screen != get_current_screen()) { ++ Vector2 ofs = get_window_position() - get_screen_position(get_current_screen()); ++ set_window_position(ofs + get_screen_position(p_screen)); ++ } ++ } ++} ++ ++Point2 OS_X11::get_screen_position(int p_screen) const { ++ if (p_screen == -1) { ++ p_screen = get_current_screen(); ++ } ++ ++ // Using Xinerama Extension ++ int event_base, error_base; ++ const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); ++ if (!ext_okay) { ++ return Point2i(0, 0); ++ } ++ ++ int count; ++ XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); ++ ++ // Check if screen is valid ++ if (p_screen < 0 || p_screen >= count) { ++ XFree(xsi); ++ ERR_FAIL_V_MSG(Point2i(0, 0), vformat("Index %d is out of bounds (count = %d)", p_screen, count)); ++ } ++ ++ Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org); ++ ++ XFree(xsi); ++ ++ return position; ++} ++ ++Size2 OS_X11::get_screen_size(int p_screen) const { ++ if (p_screen == -1) { ++ p_screen = get_current_screen(); ++ } ++ ++ // Using Xinerama Extension ++ int event_base, error_base; ++ const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); ++ if (!ext_okay) { ++ return Size2i(0, 0); ++ } ++ ++ int count; ++ XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); ++ ++ // Check if screen is valid ++ if (p_screen < 0 || p_screen >= count) { ++ XFree(xsi); ++ ERR_FAIL_V_MSG(Size2i(0, 0), vformat("Index %d is out of bounds (count = %d)", p_screen, count)); ++ } ++ ++ Size2i size = Point2i(xsi[p_screen].width, xsi[p_screen].height); ++ XFree(xsi); ++ return size; ++} ++ ++int OS_X11::get_screen_dpi(int p_screen) const { ++ if (p_screen == -1) { ++ p_screen = get_current_screen(); ++ } ++ ++ //invalid screen? ++ ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0); ++ ++ //Get physical monitor Dimensions through XRandR and calculate dpi ++ Size2 sc = get_screen_size(p_screen); ++ if (xrandr_ext_ok) { ++ int count = 0; ++ if (xrr_get_monitors) { ++ xrr_monitor_info *monitors = xrr_get_monitors(x11_display, x11_window, true, &count); ++ if (p_screen < count) { ++ double xdpi = sc.width / (double)monitors[p_screen].mwidth * 25.4; ++ double ydpi = sc.height / (double)monitors[p_screen].mheight * 25.4; ++ xrr_free_monitors(monitors); ++ return (xdpi + ydpi) / 2; ++ } ++ xrr_free_monitors(monitors); ++ } else if (p_screen == 0) { ++ XRRScreenSize *sizes = XRRSizes(x11_display, 0, &count); ++ if (sizes) { ++ double xdpi = sc.width / (double)sizes[0].mwidth * 25.4; ++ double ydpi = sc.height / (double)sizes[0].mheight * 25.4; ++ return (xdpi + ydpi) / 2; ++ } ++ } ++ } ++ ++ int width_mm = DisplayWidthMM(x11_display, p_screen); ++ int height_mm = DisplayHeightMM(x11_display, p_screen); ++ double xdpi = (width_mm ? sc.width / (double)width_mm * 25.4 : 0); ++ double ydpi = (height_mm ? sc.height / (double)height_mm * 25.4 : 0); ++ if (xdpi || ydpi) { ++ return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1); ++ } ++ ++ //could not get dpi ++ return 96; ++} ++ ++float OS_X11::get_screen_refresh_rate(int p_screen) const { ++ if (p_screen == -1) { ++ p_screen = get_current_screen(); ++ } ++ ++ //invalid screen? ++ ERR_FAIL_INDEX_V(p_screen, get_screen_count(), OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK); ++ ++ //Use xrandr to get screen refresh rate. ++ if (xrandr_ext_ok) { ++ XRRScreenResources *screen_info = XRRGetScreenResources(x11_display, x11_window); ++ if (screen_info) { ++ RRMode current_mode = 0; ++ xrr_monitor_info *monitors = nullptr; ++ ++ if (xrr_get_monitors) { ++ int count = 0; ++ monitors = xrr_get_monitors(x11_display, x11_window, true, &count); ++ ERR_FAIL_INDEX_V(p_screen, count, OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK); ++ } else { ++ ERR_PRINT("An error occurred while trying to get the screen refresh rate."); ++ return OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK; ++ } ++ ++ bool found_active_mode = false; ++ for (int crtc = 0; crtc < screen_info->ncrtc; crtc++) { // Loop through outputs to find which one is currently outputting. ++ XRRCrtcInfo *monitor_info = XRRGetCrtcInfo(x11_display, screen_info, screen_info->crtcs[crtc]); ++ if (monitor_info->x != monitors[p_screen].x || monitor_info->y != monitors[p_screen].y) { // If X and Y aren't the same as the monitor we're looking for, this isn't the right monitor. Continue. ++ continue; ++ } ++ ++ if (monitor_info->mode != None) { ++ current_mode = monitor_info->mode; ++ found_active_mode = true; ++ break; ++ } ++ } ++ ++ if (found_active_mode) { ++ for (int mode = 0; mode < screen_info->nmode; mode++) { ++ XRRModeInfo m_info = screen_info->modes[mode]; ++ if (m_info.id == current_mode) { ++ // Snap to nearest 0.01 to stay consistent with other platforms. ++ return Math::stepify((float)m_info.dotClock / ((float)m_info.hTotal * (float)m_info.vTotal), 0.01); ++ } ++ } ++ } ++ ++ ERR_PRINT("An error occurred while trying to get the screen refresh rate."); // We should have returned the refresh rate by now. An error must have occurred. ++ return OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK; ++ } else { ++ ERR_PRINT("An error occurred while trying to get the screen refresh rate."); ++ return OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK; ++ } ++ } ++ ERR_PRINT("An error occurred while trying to get the screen refresh rate."); ++ return OS::get_singleton()->SCREEN_REFRESH_RATE_FALLBACK; ++} ++ ++Point2 OS_X11::get_window_position() const { ++ int x, y; ++ Window child; ++ XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child); ++ return Point2i(x, y); ++} ++ ++void OS_X11::set_window_position(const Point2 &p_position) { ++ int x = 0; ++ int y = 0; ++ if (!get_borderless_window()) { ++ //exclude window decorations ++ XSync(x11_display, False); ++ Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True); ++ if (prop != None) { ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { ++ if (format == 32 && len == 4) { ++ long *extents = (long *)data; ++ x = extents[0]; ++ y = extents[2]; ++ } ++ XFree(data); ++ } ++ } ++ } ++ XMoveWindow(x11_display, x11_window, p_position.x - x, p_position.y - y); ++ update_real_mouse_position(); ++} ++ ++Size2 OS_X11::get_window_size() const { ++ // Use current_videomode width and height instead of XGetWindowAttributes ++ // since right after a XResizeWindow the attributes may not be updated yet ++ return Size2i(current_videomode.width, current_videomode.height); ++} ++ ++Size2 OS_X11::get_real_window_size() const { ++ XWindowAttributes xwa; ++ XSync(x11_display, False); ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ int w = xwa.width; ++ int h = xwa.height; ++ Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True); ++ if (prop != None) { ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { ++ if (format == 32 && len == 4) { ++ long *extents = (long *)data; ++ w += extents[0] + extents[1]; // left, right ++ h += extents[2] + extents[3]; // top, bottom ++ } ++ XFree(data); ++ } ++ } ++ return Size2(w, h); ++} ++ ++Size2 OS_X11::get_max_window_size() const { ++ return max_size; ++} ++ ++Size2 OS_X11::get_min_window_size() const { ++ return min_size; ++} ++ ++void OS_X11::set_min_window_size(const Size2 p_size) { ++ if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) { ++ ERR_PRINT("Minimum window size can't be larger than maximum window size!"); ++ return; ++ } ++ min_size = p_size; ++ ++ if (is_window_resizable()) { ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ xsh->flags = 0L; ++ if (min_size != Size2()) { ++ xsh->flags |= PMinSize; ++ xsh->min_width = min_size.x; ++ xsh->min_height = min_size.y; ++ } ++ if (max_size != Size2()) { ++ xsh->flags |= PMaxSize; ++ xsh->max_width = max_size.x; ++ xsh->max_height = max_size.y; ++ } ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ ++ XFlush(x11_display); ++ } ++} ++ ++void OS_X11::set_max_window_size(const Size2 p_size) { ++ if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) { ++ ERR_PRINT("Maximum window size can't be smaller than minimum window size!"); ++ return; ++ } ++ max_size = p_size; ++ ++ if (is_window_resizable()) { ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ xsh->flags = 0L; ++ if (min_size != Size2()) { ++ xsh->flags |= PMinSize; ++ xsh->min_width = min_size.x; ++ xsh->min_height = min_size.y; ++ } ++ if (max_size != Size2()) { ++ xsh->flags |= PMaxSize; ++ xsh->max_width = max_size.x; ++ xsh->max_height = max_size.y; ++ } ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ ++ XFlush(x11_display); ++ } ++} ++ ++void OS_X11::set_window_size(const Size2 p_size) { ++ if (current_videomode.width == p_size.width && current_videomode.height == p_size.height) { ++ return; ++ } ++ ++ XWindowAttributes xwa; ++ XSync(x11_display, False); ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ int old_w = xwa.width; ++ int old_h = xwa.height; ++ ++ Size2 size = p_size; ++ ++ ERR_FAIL_COND(Math::is_nan(size.x) || Math::is_nan(size.y)); ++ size.x = MAX(1, size.x); ++ size.y = MAX(1, size.y); ++ ++ // If window resizable is disabled we need to update the attributes first ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ if (!is_window_resizable()) { ++ xsh->flags = PMinSize | PMaxSize; ++ xsh->min_width = size.x; ++ xsh->max_width = size.x; ++ xsh->min_height = size.y; ++ xsh->max_height = size.y; ++ } else { ++ xsh->flags = 0L; ++ if (min_size != Size2()) { ++ xsh->flags |= PMinSize; ++ xsh->min_width = min_size.x; ++ xsh->min_height = min_size.y; ++ } ++ if (max_size != Size2()) { ++ xsh->flags |= PMaxSize; ++ xsh->max_width = max_size.x; ++ xsh->max_height = max_size.y; ++ } ++ } ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ ++ // Resize the window ++ XResizeWindow(x11_display, x11_window, size.x, size.y); ++ ++ // Update our videomode width and height ++ current_videomode.width = size.x; ++ current_videomode.height = size.y; ++ ++ for (int timeout = 0; timeout < 50; ++timeout) { ++ XSync(x11_display, False); ++ XGetWindowAttributes(x11_display, x11_window, &xwa); ++ ++ if (old_w != xwa.width || old_h != xwa.height) { ++ break; ++ } ++ ++ usleep(10000); ++ } ++} ++ ++void OS_X11::set_window_fullscreen(bool p_enabled) { ++ if (current_videomode.fullscreen == p_enabled) { ++ return; ++ } ++ ++ if (layered_window) { ++ set_window_per_pixel_transparency_enabled(false); ++ } ++ ++ if (p_enabled && current_videomode.always_on_top) { ++ // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) ++ set_window_maximized(true); ++ } ++ set_wm_fullscreen(p_enabled); ++ if (!p_enabled && current_videomode.always_on_top) { ++ // Restore ++ set_window_maximized(false); ++ } ++ if (!p_enabled) { ++ set_window_position(last_position_before_fs); ++ } else { ++ last_position_before_fs = get_window_position(); ++ } ++ current_videomode.fullscreen = p_enabled; ++} ++ ++bool OS_X11::is_window_fullscreen() const { ++ return current_videomode.fullscreen; ++} ++ ++void OS_X11::set_window_resizable(bool p_enabled) { ++ XSizeHints *xsh; ++ xsh = XAllocSizeHints(); ++ if (!p_enabled) { ++ Size2 size = get_window_size(); ++ ++ xsh->flags = PMinSize | PMaxSize; ++ xsh->min_width = size.x; ++ xsh->max_width = size.x; ++ xsh->min_height = size.y; ++ xsh->max_height = size.y; ++ } else { ++ xsh->flags = 0L; ++ if (min_size != Size2()) { ++ xsh->flags |= PMinSize; ++ xsh->min_width = min_size.x; ++ xsh->min_height = min_size.y; ++ } ++ if (max_size != Size2()) { ++ xsh->flags |= PMaxSize; ++ xsh->max_width = max_size.x; ++ xsh->max_height = max_size.y; ++ } ++ } ++ ++ XSetWMNormalHints(x11_display, x11_window, xsh); ++ XFree(xsh); ++ ++ current_videomode.resizable = p_enabled; ++ ++ XFlush(x11_display); ++} ++ ++bool OS_X11::is_window_resizable() const { ++ return current_videomode.resizable; ++} ++ ++void OS_X11::set_window_minimized(bool p_enabled) { ++ if (is_no_window_mode_enabled()) { ++ return; ++ } ++ // Using ICCCM -- Inter-Client Communication Conventions Manual ++ XEvent xev; ++ Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = wm_change; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++ ++ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = wm_state; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = _NET_WM_STATE_ADD; ++ xev.xclient.data.l[1] = wm_hidden; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++} ++ ++bool OS_X11::is_window_minimized() const { ++ // Using ICCCM -- Inter-Client Communication Conventions Manual ++ Atom property = XInternAtom(x11_display, "WM_STATE", True); ++ if (property == None) { ++ return false; ++ } ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ bool retval = false; ++ ++ int result = XGetWindowProperty( ++ x11_display, ++ x11_window, ++ property, ++ 0, ++ 32, ++ False, ++ AnyPropertyType, ++ &type, ++ &format, ++ &len, ++ &remaining, ++ &data); ++ ++ if (result == Success) { ++ long *state = (long *)data; ++ if (state[0] == WM_IconicState) { ++ retval = true; ++ } ++ XFree(data); ++ } ++ ++ return retval; ++} ++ ++void OS_X11::set_window_maximized(bool p_enabled) { ++ if (is_no_window_mode_enabled()) { ++ return; ++ } ++ if (is_window_maximized() == p_enabled) { ++ return; ++ } ++ ++ // Using EWMH -- Extended Window Manager Hints ++ XEvent xev; ++ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); ++ Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = wm_state; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; ++ xev.xclient.data.l[1] = wm_max_horz; ++ xev.xclient.data.l[2] = wm_max_vert; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++ ++ if (p_enabled && is_window_maximize_allowed()) { ++ // Wait for effective resizing (so the GLX context is too). ++ // Give up after 0.5s, it's not going to happen on this WM. ++ // https://github.com/godotengine/godot/issues/19978 ++ for (int attempt = 0; !is_window_maximized() && attempt < 50; attempt++) { ++ usleep(10000); ++ } ++ } ++ ++ maximized = p_enabled; ++} ++ ++// Just a helper to reduce code duplication in `is_window_maximize_allowed` ++// and `is_window_maximized`. ++bool OS_X11::window_maximize_check(const char *p_atom_name) const { ++ Atom property = XInternAtom(x11_display, p_atom_name, False); ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ bool retval = false; ++ ++ if (property == None) { ++ return false; ++ } ++ ++ int result = XGetWindowProperty( ++ x11_display, ++ x11_window, ++ property, ++ 0, ++ 1024, ++ False, ++ XA_ATOM, ++ &type, ++ &format, ++ &len, ++ &remaining, ++ &data); ++ ++ if (result == Success) { ++ Atom *atoms = (Atom *)data; ++ Atom wm_act_max_horz; ++ Atom wm_act_max_vert; ++ if (strcmp(p_atom_name, "_NET_WM_STATE") == 0) { ++ wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); ++ wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); ++ } else { ++ wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False); ++ wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False); ++ } ++ bool found_wm_act_max_horz = false; ++ bool found_wm_act_max_vert = false; ++ ++ for (uint64_t i = 0; i < len; i++) { ++ if (atoms[i] == wm_act_max_horz) { ++ found_wm_act_max_horz = true; ++ } ++ if (atoms[i] == wm_act_max_vert) { ++ found_wm_act_max_vert = true; ++ } ++ ++ if (found_wm_act_max_horz || found_wm_act_max_vert) { ++ retval = true; ++ break; ++ } ++ } ++ ++ XFree(data); ++ } ++ ++ return retval; ++} ++ ++bool OS_X11::window_fullscreen_check() const { ++ // Using EWMH -- Extended Window Manager Hints ++ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ bool retval = false; ++ ++ if (property == None) { ++ return retval; ++ } ++ ++ int result = XGetWindowProperty( ++ x11_display, ++ x11_window, ++ property, ++ 0, ++ 1024, ++ False, ++ XA_ATOM, ++ &type, ++ &format, ++ &len, ++ &remaining, ++ &data); ++ ++ if (result == Success) { ++ Atom *atoms = (Atom *)data; ++ Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); ++ for (uint64_t i = 0; i < len; i++) { ++ if (atoms[i] == wm_fullscreen) { ++ retval = true; ++ break; ++ } ++ } ++ XFree(data); ++ } ++ ++ return retval; ++} ++ ++bool OS_X11::is_window_maximize_allowed() const { ++ return window_maximize_check("_NET_WM_ALLOWED_ACTIONS"); ++} ++ ++bool OS_X11::is_window_maximized() const { ++ // Using EWMH -- Extended Window Manager Hints ++ return window_maximize_check("_NET_WM_STATE"); ++} ++ ++void OS_X11::set_window_always_on_top(bool p_enabled) { ++ if (is_window_always_on_top() == p_enabled) { ++ return; ++ } ++ ++ if (p_enabled && current_videomode.fullscreen) { ++ // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) ++ set_window_maximized(true); ++ } ++ set_wm_above(p_enabled); ++ if (!p_enabled && !current_videomode.fullscreen) { ++ // Restore ++ set_window_maximized(false); ++ } ++ ++ current_videomode.always_on_top = p_enabled; ++} ++ ++bool OS_X11::is_window_always_on_top() const { ++ return current_videomode.always_on_top; ++} ++ ++bool OS_X11::is_window_focused() const { ++ return window_focused; ++} ++ ++void OS_X11::set_borderless_window(bool p_borderless) { ++ if (get_borderless_window() == p_borderless) { ++ return; ++ } ++ ++ current_videomode.borderless_window = p_borderless; ++ ++ Hints hints; ++ Atom property; ++ hints.flags = 2; ++ hints.decorations = current_videomode.borderless_window ? 0 : 1; ++ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); ++ if (property != None) { ++ XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); ++ } ++ ++ // Preserve window size ++ set_window_size(Size2(current_videomode.width, current_videomode.height)); ++} ++ ++bool OS_X11::get_borderless_window() { ++ bool borderless = current_videomode.borderless_window; ++ Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); ++ if (prop != None) { ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { ++ if (data && (format == 32) && (len >= 5)) { ++ borderless = !((Hints *)data)->decorations; ++ } ++ XFree(data); ++ } ++ } ++ return borderless; ++} ++ ++void OS_X11::request_attention() { ++ // Using EWMH -- Extended Window Manager Hints ++ // ++ // Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE ++ // Will be unset by the window manager after user react on the request for attention ++ ++ XEvent xev; ++ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); ++ Atom wm_attention = XInternAtom(x11_display, "_NET_WM_STATE_DEMANDS_ATTENTION", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = wm_state; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = _NET_WM_STATE_ADD; ++ xev.xclient.data.l[1] = wm_attention; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++ XFlush(x11_display); ++} ++ ++void *OS_X11::get_native_handle(int p_handle_type) { ++ switch (p_handle_type) { ++ case APPLICATION_HANDLE: ++ return nullptr; // Do we have a value to return here? ++ case DISPLAY_HANDLE: ++ return (void *)x11_display; ++ case WINDOW_HANDLE: ++ return (void *)x11_window; ++ case WINDOW_VIEW: ++ return nullptr; // Do we have a value to return here? ++ case OPENGL_CONTEXT: ++ return context_gl->get_glx_context(); ++ default: ++ return nullptr; ++ } ++} ++ ++void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref state) { ++ state->set_shift((p_x11_state & ShiftMask)); ++ state->set_control((p_x11_state & ControlMask)); ++ state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt ++ state->set_metakey((p_x11_state & Mod4Mask)); ++} ++ ++unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) { ++ unsigned int mask = 1 << (p_x11_button - 1); ++ ++ if (p_x11_type == ButtonPress) { ++ last_button_state |= mask; ++ } else { ++ last_button_state &= ~mask; ++ } ++ ++ return last_button_state; ++} ++ ++void OS_X11::_handle_key_event(XKeyEvent *p_event, LocalVector &p_events, uint32_t &p_event_index, bool p_echo) { ++ // X11 functions don't know what const is ++ XKeyEvent *xkeyevent = p_event; ++ ++ // This code was pretty difficult to write. ++ // The docs stink and every toolkit seems to ++ // do it in a different way. ++ ++ /* Phase 1, obtain a proper keysym */ ++ ++ // This was also very difficult to figure out. ++ // You'd expect you could just use Keysym provided by ++ // XKeycodeToKeysym to obtain internationalized ++ // input.. WRONG!! ++ // you must use XLookupString (???) which not only wastes ++ // cycles generating an unnecessary string, but also ++ // still works in half the cases. (won't handle deadkeys) ++ // For more complex input methods (deadkeys and more advanced) ++ // you have to use XmbLookupString (??). ++ // So.. then you have to choose which of both results ++ // you want to keep. ++ // This is a real bizarreness and cpu waster. ++ ++ KeySym keysym_keycode = 0; // keysym used to find a keycode ++ KeySym keysym_unicode = 0; // keysym used to find unicode ++ ++ // XLookupString returns keysyms usable as nice scancodes/ ++ char str[256 + 1]; ++ XKeyEvent xkeyevent_no_mod = *xkeyevent; ++ xkeyevent_no_mod.state &= ~ShiftMask; ++ xkeyevent_no_mod.state &= ~ControlMask; ++ XLookupString(xkeyevent, str, 256, &keysym_unicode, nullptr); ++ XLookupString(&xkeyevent_no_mod, nullptr, 0, &keysym_keycode, nullptr); ++ ++ // Meanwhile, XLookupString returns keysyms useful for unicode. ++ ++ if (!xmbstring) { ++ // keep a temporary buffer for the string ++ xmbstring = (char *)memalloc(sizeof(char) * 8); ++ xmblen = 8; ++ } ++ ++ if (xkeyevent->type == KeyPress && xic) { ++ Status status; ++#ifdef X_HAVE_UTF8_STRING ++ int utf8len = 8; ++ char *utf8string = (char *)memalloc(sizeof(char) * utf8len); ++ int utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string, ++ utf8len - 1, &keysym_unicode, &status); ++ if (status == XBufferOverflow) { ++ utf8len = utf8bytes + 1; ++ utf8string = (char *)memrealloc(utf8string, utf8len); ++ utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string, ++ utf8len - 1, &keysym_unicode, &status); ++ } ++ utf8string[utf8bytes] = '\0'; ++ ++ if (status == XLookupChars) { ++ bool keypress = xkeyevent->type == KeyPress; ++ unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); ++ unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode); ++ if (keycode >= 'a' && keycode <= 'z') { ++ keycode -= 'a' - 'A'; ++ } ++ ++ String tmp; ++ tmp.parse_utf8(utf8string, utf8bytes); ++ for (int i = 0; i < tmp.length(); i++) { ++ Ref k; ++ k.instance(); ++ if (physical_keycode == 0 && keycode == 0 && tmp[i] == 0) { ++ continue; ++ } ++ ++ if (keycode == 0) { ++ keycode = physical_keycode; ++ } ++ ++ get_key_modifier_state(xkeyevent->state, k); ++ ++ k->set_unicode(tmp[i]); ++ ++ k->set_pressed(keypress); ++ ++ k->set_scancode(keycode); ++ k->set_physical_scancode(physical_keycode); ++ ++ k->set_echo(false); ++ ++ if (k->get_scancode() == KEY_BACKTAB) { ++ //make it consistent across platforms. ++ k->set_scancode(KEY_TAB); ++ k->set_physical_scancode(KEY_TAB); ++ k->set_shift(true); ++ } ++ ++ input->parse_input_event(k); ++ } ++ memfree(utf8string); ++ return; ++ } ++ memfree(utf8string); ++#else ++ do { ++ int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); ++ xmbstring[mnbytes] = '\0'; ++ ++ if (status == XBufferOverflow) { ++ xmblen = mnbytes + 1; ++ xmbstring = (char *)memrealloc(xmbstring, xmblen); ++ } ++ } while (status == XBufferOverflow); ++#endif ++ } ++ ++ /* Phase 2, obtain a pigui keycode from the keysym */ ++ ++ // KeyMappingX11 just translated the X11 keysym to a PIGUI ++ // keysym, so it works in all platforms the same. ++ ++ unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); ++ unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode); ++ ++ /* Phase 3, obtain a unicode character from the keysym */ ++ ++ // KeyMappingX11 also translates keysym to unicode. ++ // It does a binary search on a table to translate ++ // most properly. ++ unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0; ++ ++ /* Phase 4, determine if event must be filtered */ ++ ++ // This seems to be a side-effect of using XIM. ++ // XFilterEvent looks like a core X11 function, ++ // but it's actually just used to see if we must ++ // ignore a deadkey, or events XIM determines ++ // must not reach the actual gui. ++ // Guess it was a design problem of the extension ++ ++ bool keypress = xkeyevent->type == KeyPress; ++ ++ if (physical_keycode == 0 && keycode == 0 && unicode == 0) { ++ return; ++ } ++ ++ if (keycode == 0) { ++ keycode = physical_keycode; ++ } ++ ++ /* Phase 5, determine modifier mask */ ++ ++ // No problems here, except I had no way to ++ // know Mod1 was ALT and Mod4 was META (applekey/winkey) ++ // just tried Mods until i found them. ++ ++ //print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); ++ ++ Ref k; ++ k.instance(); ++ ++ get_key_modifier_state(xkeyevent->state, k); ++ ++ /* Phase 6, determine echo character */ ++ ++ // Echo characters in X11 are a keyrelease and a keypress ++ // one after the other with the (almot) same timestamp. ++ // To detect them, i compare to the next event in list and ++ // check that their difference in time is below a threshold. ++ ++ if (xkeyevent->type != KeyPress) { ++ p_echo = false; ++ ++ // make sure there are events pending, ++ // so this call won't block. ++ if (p_event_index + 1 < p_events.size()) { ++ XEvent &peek_event = p_events[p_event_index + 1]; ++ ++ // I'm using a threshold of 5 msecs, ++ // since sometimes there seems to be a little ++ // jitter. I'm still not convinced that all this approach ++ // is correct, but the xorg developers are ++ // not very helpful today. ++ ++ ::Time tresh = ABSDIFF(peek_event.xkey.time, xkeyevent->time); ++ if (peek_event.type == KeyPress && tresh < 5) { ++ KeySym rk; ++ XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, nullptr); ++ if (rk == keysym_keycode) { ++ // Consume to next event. ++ ++p_event_index; ++ _handle_key_event((XKeyEvent *)&peek_event, p_events, p_event_index, true); ++ return; //ignore current, echo next ++ } ++ } ++ ++ // use the time from peek_event so it always works ++ } ++ ++ // save the time to check for echo when keypress happens ++ } ++ ++ /* Phase 7, send event to Window */ ++ ++ k->set_pressed(keypress); ++ ++ if (keycode >= 'a' && keycode <= 'z') { ++ keycode -= 'a' - 'A'; ++ } ++ ++ k->set_scancode(keycode); ++ k->set_physical_scancode(physical_keycode); ++ k->set_unicode(unicode); ++ k->set_echo(p_echo); ++ ++ if (k->get_scancode() == KEY_BACKTAB) { ++ //make it consistent across platforms. ++ k->set_scancode(KEY_TAB); ++ k->set_physical_scancode(KEY_TAB); ++ k->set_shift(true); ++ } ++ ++ //don't set mod state if modifier keys are released by themselves ++ //else event.is_action() will not work correctly here ++ if (!k->is_pressed()) { ++ if (k->get_scancode() == KEY_SHIFT) { ++ k->set_shift(false); ++ } else if (k->get_scancode() == KEY_CONTROL) { ++ k->set_control(false); ++ } else if (k->get_scancode() == KEY_ALT) { ++ k->set_alt(false); ++ } else if (k->get_scancode() == KEY_META) { ++ k->set_metakey(false); ++ } ++ } ++ ++ bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_scancode()); ++ if (k->is_pressed()) { ++ if (last_is_pressed) { ++ k->set_echo(true); ++ } ++ } ++ ++ //printf("key: %x\n",k->get_scancode()); ++ input->parse_input_event(k); ++} ++ ++Atom OS_X11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const { ++ if (p_target == XInternAtom(x11_display, "TARGETS", 0)) { ++ // Request to list all supported targets. ++ Atom data[9]; ++ data[0] = XInternAtom(x11_display, "TARGETS", 0); ++ data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0); ++ data[2] = XInternAtom(x11_display, "MULTIPLE", 0); ++ data[3] = XInternAtom(x11_display, "UTF8_STRING", 0); ++ data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0); ++ data[5] = XInternAtom(x11_display, "TEXT", 0); ++ data[6] = XA_STRING; ++ data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0); ++ data[8] = XInternAtom(x11_display, "text/plain", 0); ++ ++ XChangeProperty(x11_display, ++ p_requestor, ++ p_property, ++ XA_ATOM, ++ 32, ++ PropModeReplace, ++ (unsigned char *)&data, ++ sizeof(data) / sizeof(data[0])); ++ ++ return p_property; ++ } else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) { ++ // Request to check if SAVE_TARGETS is supported, nothing special to do. ++ XChangeProperty(x11_display, ++ p_requestor, ++ p_property, ++ XInternAtom(x11_display, "NULL", False), ++ 32, ++ PropModeReplace, ++ nullptr, ++ 0); ++ return p_property; ++ } else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) || ++ p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || ++ p_target == XInternAtom(x11_display, "TEXT", 0) || ++ p_target == XA_STRING || ++ p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || ++ p_target == XInternAtom(x11_display, "text/plain", 0)) { ++ // Directly using internal clipboard because we know our window ++ // is the owner during a selection request. ++ CharString clip; ++ static const char *target_type = "PRIMARY"; ++ if (p_selection != None && get_atom_name(x11_display, p_selection) == target_type) { ++ clip = OS::get_clipboard_primary().utf8(); ++ } else { ++ clip = OS::get_clipboard().utf8(); ++ } ++ XChangeProperty(x11_display, ++ p_requestor, ++ p_property, ++ p_target, ++ 8, ++ PropModeReplace, ++ (unsigned char *)clip.get_data(), ++ clip.length()); ++ return p_property; ++ } else { ++ char *target_name = XGetAtomName(x11_display, p_target); ++ printf("Target '%s' not supported.\n", target_name); ++ if (target_name) { ++ XFree(target_name); ++ } ++ return None; ++ } ++} ++ ++void OS_X11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const { ++ XEvent respond; ++ if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) { ++ // Request for multiple target conversions at once. ++ Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False); ++ respond.xselection.property = None; ++ ++ Atom type; ++ int format; ++ unsigned long len; ++ unsigned long remaining; ++ unsigned char *data = nullptr; ++ if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) { ++ if ((len >= 2) && data) { ++ Atom *targets = (Atom *)data; ++ for (uint64_t i = 0; i < len; i += 2) { ++ Atom target = targets[i]; ++ Atom &property = targets[i + 1]; ++ property = _process_selection_request_target(target, p_event->requestor, property, p_event->selection); ++ } ++ ++ XChangeProperty(x11_display, ++ p_event->requestor, ++ p_event->property, ++ atom_pair, ++ 32, ++ PropModeReplace, ++ (unsigned char *)targets, ++ len); ++ ++ respond.xselection.property = p_event->property; ++ } ++ XFree(data); ++ } ++ } else { ++ // Request for target conversion. ++ respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property, p_event->selection); ++ } ++ ++ respond.xselection.type = SelectionNotify; ++ respond.xselection.display = p_event->display; ++ respond.xselection.requestor = p_event->requestor; ++ respond.xselection.selection = p_event->selection; ++ respond.xselection.target = p_event->target; ++ respond.xselection.time = p_event->time; ++ ++ XSendEvent(x11_display, p_event->requestor, True, NoEventMask, &respond); ++ XFlush(x11_display); ++} ++ ++struct Property { ++ unsigned char *data; ++ int format, nitems; ++ Atom type; ++}; ++ ++static Property read_property(Display *p_display, Window p_window, Atom p_property) { ++ Atom actual_type = None; ++ int actual_format = 0; ++ unsigned long nitems = 0; ++ unsigned long bytes_after = 0; ++ unsigned char *ret = nullptr; ++ ++ int read_bytes = 1024; ++ ++ //Keep trying to read the property until there are no ++ //bytes unread. ++ if (p_property != None) { ++ do { ++ if (ret != nullptr) { ++ XFree(ret); ++ } ++ ++ XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType, ++ &actual_type, &actual_format, &nitems, &bytes_after, ++ &ret); ++ ++ read_bytes *= 2; ++ ++ } while (bytes_after != 0); ++ } ++ ++ Property p = { ret, actual_format, (int)nitems, actual_type }; ++ ++ return p; ++} ++ ++static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) { ++ static const char *target_type = "text/uri-list"; ++ ++ for (int i = 0; i < p_count; i++) { ++ Atom atom = p_list[i]; ++ ++ if (atom != None && get_atom_name(p_display, atom) == target_type) { ++ return atom; ++ } ++ } ++ return None; ++} ++ ++static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) { ++ static const char *target_type = "text/uri-list"; ++ if (p_t1 != None && get_atom_name(p_disp, p_t1) == target_type) { ++ return p_t1; ++ } ++ ++ if (p_t2 != None && get_atom_name(p_disp, p_t2) == target_type) { ++ return p_t2; ++ } ++ ++ if (p_t3 != None && get_atom_name(p_disp, p_t3) == target_type) { ++ return p_t3; ++ } ++ ++ return None; ++} ++ ++void OS_X11::_window_changed(XEvent *event) { ++ if (xic) { ++ // Not portable. ++ set_ime_position(Point2(0, 1)); ++ } ++ if ((event->xconfigure.width == current_videomode.width) && ++ (event->xconfigure.height == current_videomode.height)) { ++ return; ++ } ++ ++ current_videomode.width = event->xconfigure.width; ++ current_videomode.height = event->xconfigure.height; ++} ++ ++void OS_X11::_poll_events_thread(void *ud) { ++ OS_X11 *os = (OS_X11 *)ud; ++ os->_poll_events(); ++} ++ ++Bool OS_X11::_predicate_all_events(Display *display, XEvent *event, XPointer arg) { ++ // Just accept all events. ++ return True; ++} ++ ++bool OS_X11::_wait_for_events() const { ++ int x11_fd = ConnectionNumber(x11_display); ++ fd_set in_fds; ++ ++ XFlush(x11_display); ++ ++ FD_ZERO(&in_fds); ++ FD_SET(x11_fd, &in_fds); ++ ++ struct timeval tv; ++ tv.tv_usec = 0; ++ tv.tv_sec = 1; ++ ++ // Wait for next event or timeout. ++ int num_ready_fds = select(x11_fd + 1, &in_fds, nullptr, nullptr, &tv); ++ ++ if (num_ready_fds > 0) { ++ // Event received. ++ return true; ++ } else { ++ // Error or timeout. ++ if (num_ready_fds < 0) { ++ ERR_PRINT("_wait_for_events: select error: " + itos(errno)); ++ } ++ return false; ++ } ++} ++ ++void OS_X11::_poll_events() { ++ while (!events_thread_done) { ++ _wait_for_events(); ++ ++ // Process events from the queue. ++ { ++ MutexLock mutex_lock(events_mutex); ++ ++ _check_pending_events(polled_events); ++ } ++ } ++} ++ ++void OS_X11::_check_pending_events(LocalVector &r_events) { ++ // Flush to make sure to gather all pending events. ++ XFlush(x11_display); ++ ++ // Non-blocking wait for next event and remove it from the queue. ++ XEvent ev; ++ while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) { ++ // Check if the input manager wants to process the event. ++ if (XFilterEvent(&ev, None)) { ++ // Event has been filtered by the Input Manager, ++ // it has to be ignored and a new one will be received. ++ continue; ++ } ++ ++ // Handle selection request events directly in the event thread, because ++ // communication through the x server takes several events sent back and forth ++ // and we don't want to block other programs while processing only one each frame. ++ if (ev.type == SelectionRequest) { ++ _handle_selection_request_event(&(ev.xselectionrequest)); ++ continue; ++ } ++ ++ r_events.push_back(ev); ++ } ++} ++ ++void OS_X11::process_xevents() { ++ //printf("checking events %i\n", XPending(x11_display)); ++ ++#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED ++ static int frame = 0; ++ ++frame; ++#endif ++ ++ do_mouse_warp = false; ++ ++ // Is the current mouse mode one where it needs to be grabbed. ++ bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN; ++ ++ xi.pressure = 0; ++ xi.tilt = Vector2(); ++ xi.pressure_supported = false; ++ ++ LocalVector events; ++ { ++ // Block events polling while flushing events. ++ MutexLock mutex_lock(events_mutex); ++ events = polled_events; ++ polled_events.clear(); ++ ++ // Check for more pending events to avoid an extra frame delay. ++ _check_pending_events(events); ++ } ++ ++ for (uint32_t event_index = 0; event_index < events.size(); ++event_index) { ++ XEvent &event = events[event_index]; ++ ++ if (XGetEventData(x11_display, &event.xcookie)) { ++ if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) { ++ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; ++ int index = event_data->detail; ++ Vector2 pos = Vector2(event_data->event_x, event_data->event_y); ++ ++ switch (event_data->evtype) { ++ case XI_HierarchyChanged: ++ case XI_DeviceChanged: { ++ refresh_device_info(); ++ } break; ++ case XI_RawMotion: { ++ XIRawEvent *raw_event = (XIRawEvent *)event_data; ++ int device_id = raw_event->sourceid; ++ ++ // Determine the axis used (called valuators in XInput for some forsaken reason) ++ // Mask is a bitmask indicating which axes are involved. ++ // We are interested in the values of axes 0 and 1. ++ if (raw_event->valuators.mask_len <= 0) { ++ break; ++ } ++ ++ const double *values = raw_event->raw_values; ++ ++ double rel_x = 0.0; ++ double rel_y = 0.0; ++ ++ if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSX)) { ++ rel_x = *values; ++ values++; ++ } ++ ++ if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSY)) { ++ rel_y = *values; ++ values++; ++ } ++ ++ if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_PRESSURE)) { ++ Map::Element *pen_pressure = xi.pen_pressure_range.find(device_id); ++ if (pen_pressure) { ++ Vector2 pen_pressure_range = pen_pressure->value(); ++ if (pen_pressure_range != Vector2()) { ++ xi.pressure_supported = true; ++ xi.pressure = (*values - pen_pressure_range[0]) / ++ (pen_pressure_range[1] - pen_pressure_range[0]); ++ } ++ } ++ ++ values++; ++ } ++ ++ if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTX)) { ++ Map::Element *pen_tilt_x = xi.pen_tilt_x_range.find(device_id); ++ if (pen_tilt_x) { ++ Vector2 pen_tilt_x_range = pen_tilt_x->value(); ++ if (pen_tilt_x_range[0] != 0 && *values < 0) { ++ xi.tilt.x = *values / -pen_tilt_x_range[0]; ++ } else if (pen_tilt_x_range[1] != 0) { ++ xi.tilt.x = *values / pen_tilt_x_range[1]; ++ } ++ } ++ ++ values++; ++ } ++ ++ if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTY)) { ++ Map::Element *pen_tilt_y = xi.pen_tilt_y_range.find(device_id); ++ if (pen_tilt_y) { ++ Vector2 pen_tilt_y_range = pen_tilt_y->value(); ++ if (pen_tilt_y_range[0] != 0 && *values < 0) { ++ xi.tilt.y = *values / -pen_tilt_y_range[0]; ++ } else if (pen_tilt_y_range[1] != 0) { ++ xi.tilt.y = *values / pen_tilt_y_range[1]; ++ } ++ } ++ ++ values++; ++ } ++ ++ Map::Element *pen_inverted = xi.pen_inverted_devices.find(device_id); ++ if (pen_inverted) { ++ xi.pen_inverted = pen_inverted->value(); ++ } ++ ++ // https://bugs.freedesktop.org/show_bug.cgi?id=71609 ++ // http://lists.libsdl.org/pipermail/commits-libsdl.org/2015-June/000282.html ++ if (raw_event->time == xi.last_relative_time && rel_x == xi.relative_motion.x && rel_y == xi.relative_motion.y) { ++ break; // Flush duplicate to avoid overly fast motion ++ } ++ ++ xi.old_raw_pos.x = xi.raw_pos.x; ++ xi.old_raw_pos.y = xi.raw_pos.y; ++ xi.raw_pos.x = rel_x; ++ xi.raw_pos.y = rel_y; ++ ++ Map::Element *abs_info = xi.absolute_devices.find(device_id); ++ ++ if (abs_info) { ++ // Absolute mode device ++ Vector2 mult = abs_info->value(); ++ ++ xi.relative_motion.x += (xi.raw_pos.x - xi.old_raw_pos.x) * mult.x; ++ xi.relative_motion.y += (xi.raw_pos.y - xi.old_raw_pos.y) * mult.y; ++ } else { ++ // Relative mode device ++ xi.relative_motion.x = xi.raw_pos.x; ++ xi.relative_motion.y = xi.raw_pos.y; ++ } ++ ++ xi.last_relative_time = raw_event->time; ++ } break; ++#ifdef TOUCH_ENABLED ++ case XI_TouchBegin: ++ case XI_TouchEnd: { ++ bool is_begin = event_data->evtype == XI_TouchBegin; ++ ++ Ref st; ++ st.instance(); ++ st->set_index(index); ++ st->set_position(pos); ++ st->set_pressed(is_begin); ++ ++ if (is_begin) { ++ if (xi.state.has(index)) { // Defensive ++ break; ++ } ++ xi.state[index] = pos; ++ if (xi.state.size() == 1) { ++ // X11 may send a motion event when a touch gesture begins, that would result ++ // in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out ++ xi.mouse_pos_to_filter = pos; ++ } ++ input->parse_input_event(st); ++ } else { ++ if (!xi.state.has(index)) { // Defensive ++ break; ++ } ++ xi.state.erase(index); ++ input->parse_input_event(st); ++ } ++ } break; ++ ++ case XI_TouchUpdate: { ++ Map::Element *curr_pos_elem = xi.state.find(index); ++ if (!curr_pos_elem) { // Defensive ++ break; ++ } ++ ++ if (curr_pos_elem->value() != pos) { ++ Ref sd; ++ sd.instance(); ++ sd->set_index(index); ++ sd->set_position(pos); ++ sd->set_relative(pos - curr_pos_elem->value()); ++ input->parse_input_event(sd); ++ ++ curr_pos_elem->value() = pos; ++ } ++ } break; ++#endif ++ } ++ } ++ } ++ XFreeEventData(x11_display, &event.xcookie); ++ ++ switch (event.type) { ++ case Expose: { ++ DEBUG_LOG_X11("[%u] Expose window=%lu, count='%u' \n", frame, event.xexpose.window, event.xexpose.count); ++ current_videomode.fullscreen = window_fullscreen_check(); ++ ++ Main::force_redraw(); ++ } break; ++ ++ case NoExpose: { ++ DEBUG_LOG_X11("[%u] NoExpose drawable=%lu \n", frame, event.xnoexpose.drawable); ++ ++ minimized = true; ++ } break; ++ ++ case VisibilityNotify: { ++ DEBUG_LOG_X11("[%u] VisibilityNotify window=%lu, state=%u \n", frame, event.xvisibility.window, event.xvisibility.state); ++ ++ XVisibilityEvent *visibility = (XVisibilityEvent *)&event; ++ minimized = (visibility->state == VisibilityFullyObscured); ++ } break; ++ ++ case LeaveNotify: { ++ DEBUG_LOG_X11("[%u] LeaveNotify window=%lu, mode='%u' \n", frame, event.xcrossing.window, event.xcrossing.mode); ++ ++ if (main_loop && !mouse_mode_grab) { ++ main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); ++ } ++ ++ } break; ++ ++ case EnterNotify: { ++ DEBUG_LOG_X11("[%u] EnterNotify window=%lu, mode='%u' \n", frame, event.xcrossing.window, event.xcrossing.mode); ++ ++ if (main_loop && !mouse_mode_grab) { ++ main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); ++ } ++ } break; ++ ++ case FocusIn: { ++ DEBUG_LOG_X11("[%u] FocusIn window=%lu, mode='%u' \n", frame, event.xfocus.window, event.xfocus.mode); ++ ++ minimized = false; ++ window_has_focus = true; ++ main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); ++ window_focused = true; ++ ++ if (mouse_mode_grab) { ++ // Show and update the cursor if confined and the window regained focus. ++ if (mouse_mode == MOUSE_MODE_CONFINED) { ++ XUndefineCursor(x11_display, x11_window); ++ } else if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { // or re-hide it in captured mode ++ XDefineCursor(x11_display, x11_window, null_cursor); ++ } ++ ++ XGrabPointer( ++ x11_display, x11_window, True, ++ ButtonPressMask | ButtonReleaseMask | PointerMotionMask, ++ GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); ++ } ++#ifdef TOUCH_ENABLED ++ // Grab touch devices to avoid OS gesture interference ++ /*for (int i = 0; i < xi.touch_devices.size(); ++i) { ++ XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask); ++ }*/ ++#endif ++ if (xic) { ++ // Block events polling while changing input focus ++ // because it triggers some event polling internally. ++ MutexLock mutex_lock(events_mutex); ++ XSetICFocus(xic); ++ } ++ } break; ++ ++ case FocusOut: { ++ DEBUG_LOG_X11("[%u] FocusOut window=%lu, mode='%u' \n", frame, event.xfocus.window, event.xfocus.mode); ++ ++ window_has_focus = false; ++ input->release_pressed_events(); ++ main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); ++ window_focused = false; ++ ++ if (mouse_mode_grab) { ++ //dear X11, I try, I really try, but you never work, you do whatever you want. ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ // Show the cursor if we're in captured mode so it doesn't look weird. ++ XUndefineCursor(x11_display, x11_window); ++ } ++ XUngrabPointer(x11_display, CurrentTime); ++ } ++#ifdef TOUCH_ENABLED ++ // Ungrab touch devices so input works as usual while we are unfocused ++ /*for (int i = 0; i < xi.touch_devices.size(); ++i) { ++ XIUngrabDevice(x11_display, xi.touch_devices[i], CurrentTime); ++ }*/ ++ ++ // Release every pointer to avoid sticky points ++ for (Map::Element *E = xi.state.front(); E; E = E->next()) { ++ Ref st; ++ st.instance(); ++ st->set_index(E->key()); ++ st->set_position(E->get()); ++ input->parse_input_event(st); ++ } ++ xi.state.clear(); ++#endif ++ if (xic) { ++ // Block events polling while changing input focus ++ // because it triggers some event polling internally. ++ MutexLock mutex_lock(events_mutex); ++ XUnsetICFocus(xic); ++ } ++ } break; ++ ++ case ConfigureNotify: { ++ DEBUG_LOG_X11("[%u] ConfigureNotify window=%lu, event=%lu, above=%lu, override_redirect=%u \n", frame, event.xconfigure.window, event.xconfigure.event, event.xconfigure.above, event.xconfigure.override_redirect); ++ ++ _window_changed(&event); ++ } break; ++ ++ case ButtonPress: ++ case ButtonRelease: { ++ /* exit in case of a mouse button press */ ++ last_timestamp = event.xbutton.time; ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ event.xbutton.x = last_mouse_pos.x; ++ event.xbutton.y = last_mouse_pos.y; ++ } ++ ++ Ref mb; ++ mb.instance(); ++ ++ get_key_modifier_state(event.xbutton.state, mb); ++ mb->set_button_index(event.xbutton.button); ++ if (mb->get_button_index() == 2) { ++ mb->set_button_index(3); ++ } else if (mb->get_button_index() == 3) { ++ mb->set_button_index(2); ++ } ++ mb->set_button_mask(get_mouse_button_state(mb->get_button_index(), event.xbutton.type)); ++ mb->set_position(Vector2(event.xbutton.x, event.xbutton.y)); ++ mb->set_global_position(mb->get_position()); ++ ++ mb->set_pressed((event.type == ButtonPress)); ++ ++ if (event.type == ButtonPress) { ++ DEBUG_LOG_X11("[%u] ButtonPress window=%lu, button_index=%u \n", frame, event.xbutton.window, mb->get_button_index()); ++ ++ uint64_t diff = get_ticks_usec() / 1000 - last_click_ms; ++ ++ if (mb->get_button_index() == last_click_button_index) { ++ if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) { ++ last_click_ms = 0; ++ last_click_pos = Point2(-100, -100); ++ last_click_button_index = -1; ++ mb->set_doubleclick(true); ++ } ++ ++ } else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) { ++ last_click_button_index = mb->get_button_index(); ++ } ++ ++ if (!mb->is_doubleclick()) { ++ last_click_ms += diff; ++ last_click_pos = Point2(event.xbutton.x, event.xbutton.y); ++ } ++ } else { ++ DEBUG_LOG_X11("[%u] ButtonRelease window=%lu, button_index=%u \n", frame, event.xbutton.window, mb->get_button_index()); ++ } ++ ++ input->parse_input_event(mb); ++ ++ } break; ++ case MotionNotify: { ++ // The X11 API requires filtering one-by-one through the motion ++ // notify events, in order to figure out which event is the one ++ // generated by warping the mouse pointer. ++ ++ while (true) { ++ if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) { ++ //this is likely the warp event since it was warped here ++ center = Vector2(event.xmotion.x, event.xmotion.y); ++ break; ++ } ++ ++ if (event_index + 1 < events.size()) { ++ const XEvent &next_event = events[event_index + 1]; ++ if (next_event.type == MotionNotify) { ++ ++event_index; ++ event = next_event; ++ } else { ++ break; ++ } ++ } else { ++ break; ++ } ++ } ++ ++ last_timestamp = event.xmotion.time; ++ ++ // Motion is also simple. ++ // A little hack is in order ++ // to be able to send relative motion events. ++ Point2 pos(event.xmotion.x, event.xmotion.y); ++ ++ // Avoidance of spurious mouse motion (see handling of touch) ++ bool filter = false; ++ // Adding some tolerance to match better Point2i to Vector2 ++ if (xi.state.size() && Vector2(pos).distance_squared_to(xi.mouse_pos_to_filter) < 2) { ++ filter = true; ++ } ++ // Invalidate to avoid filtering a possible legitimate similar event coming later ++ xi.mouse_pos_to_filter = Vector2(1e10, 1e10); ++ if (filter) { ++ break; ++ } ++ ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) { ++ break; ++ } ++ ++ Point2i new_center = pos; ++ pos = last_mouse_pos + xi.relative_motion; ++ center = new_center; ++ do_mouse_warp = window_has_focus; // warp the cursor if we're focused in ++ } ++ ++ if (!last_mouse_pos_valid) { ++ last_mouse_pos = pos; ++ last_mouse_pos_valid = true; ++ } ++ ++ // Hackish but relative mouse motion is already handled in the RawMotion event. ++ // RawMotion does not provide the absolute mouse position (whereas MotionNotify does). ++ // Therefore, RawMotion cannot be the authority on absolute mouse position. ++ // RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion. ++ // Therefore, MotionNotify cannot be the authority on relative mouse motion. ++ // This means we need to take a combined approach... ++ Point2 rel; ++ ++ // Only use raw input if in capture mode. Otherwise use the classic behavior. ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ rel = xi.relative_motion; ++ } else { ++ rel = pos - last_mouse_pos; ++ } ++ ++ // Reset to prevent lingering motion ++ xi.relative_motion.x = 0; ++ xi.relative_motion.y = 0; ++ ++ if (mouse_mode == MOUSE_MODE_CAPTURED) { ++ pos = Point2i(current_videomode.width / 2, current_videomode.height / 2); ++ } ++ ++ Ref mm; ++ mm.instance(); ++ ++ if (xi.pressure_supported) { ++ mm->set_pressure(xi.pressure); ++ } else { ++ mm->set_pressure((get_mouse_button_state() & (1 << (BUTTON_LEFT - 1))) ? 1.0f : 0.0f); ++ } ++ mm->set_pen_inverted(xi.pen_inverted); ++ mm->set_tilt(xi.tilt); ++ ++ // Make the absolute position integral so it doesn't look _too_ weird :) ++ Point2i posi(pos); ++ ++ get_key_modifier_state(event.xmotion.state, mm); ++ mm->set_button_mask(get_mouse_button_state()); ++ mm->set_position(posi); ++ mm->set_global_position(posi); ++ mm->set_speed(input->get_last_mouse_speed()); ++ ++ mm->set_relative(rel); ++ ++ last_mouse_pos = pos; ++ ++ // printf("rel: %d,%d\n", rel.x, rel.y ); ++ // Don't propagate the motion event unless we have focus ++ // this is so that the relative motion doesn't get messed up ++ // after we regain focus. ++ if (window_has_focus || !mouse_mode_grab) { ++ input->parse_input_event(mm); ++ } ++ ++ } break; ++ ++ case KeyPress: ++ case KeyRelease: { ++#ifdef DISPLAY_SERVER_X11_DEBUG_LOGS_ENABLED ++ if (event.type == KeyPress) { ++ DEBUG_LOG_X11("[%u] KeyPress window=%lu, keycode=%u, time=%lu \n", frame, event.xkey.window, event.xkey.keycode, event.xkey.time); ++ } else { ++ DEBUG_LOG_X11("[%u] KeyRelease window=%lu, keycode=%u, time=%lu \n", frame, event.xkey.window, event.xkey.keycode, event.xkey.time); ++ } ++#endif ++ last_timestamp = event.xkey.time; ++ ++ // key event is a little complex, so ++ // it will be handled in its own function. ++ _handle_key_event(&event.xkey, events, event_index); ++ } break; ++ ++ case SelectionNotify: ++ ++ if (event.xselection.target == requested) { ++ Property p = read_property(x11_display, x11_window, XInternAtom(x11_display, "PRIMARY", 0)); ++ ++ Vector files = String((char *)p.data).split("\n", false); ++ XFree(p.data); ++ for (int i = 0; i < files.size(); i++) { ++ files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges(); ++ } ++ main_loop->drop_files(files); ++ ++ //Reply that all is well. ++ XClientMessageEvent m; ++ memset(&m, 0, sizeof(m)); ++ m.type = ClientMessage; ++ m.display = x11_display; ++ m.window = xdnd_source_window; ++ m.message_type = xdnd_finished; ++ m.format = 32; ++ m.data.l[0] = x11_window; ++ m.data.l[1] = 1; ++ m.data.l[2] = xdnd_action_copy; //We only ever copy. ++ ++ XSendEvent(x11_display, xdnd_source_window, False, NoEventMask, (XEvent *)&m); ++ } ++ break; ++ ++ case ClientMessage: ++ ++ if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete) { ++ main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); ++ ++ } else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) { ++ //File(s) have been dragged over the window, check for supported target (text/uri-list) ++ xdnd_version = (event.xclient.data.l[1] >> 24); ++ Window source = event.xclient.data.l[0]; ++ bool more_than_3 = event.xclient.data.l[1] & 1; ++ if (more_than_3) { ++ Property p = read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False)); ++ requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems); ++ XFree(p.data); ++ } else { ++ requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); ++ } ++ } else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_position) { ++ //xdnd position event, reply with an XDND status message ++ //just depending on type of data for now ++ XClientMessageEvent m; ++ memset(&m, 0, sizeof(m)); ++ m.type = ClientMessage; ++ m.display = event.xclient.display; ++ m.window = event.xclient.data.l[0]; ++ m.message_type = xdnd_status; ++ m.format = 32; ++ m.data.l[0] = x11_window; ++ m.data.l[1] = (requested != None); ++ m.data.l[2] = 0; //empty rectangle ++ m.data.l[3] = 0; ++ m.data.l[4] = xdnd_action_copy; ++ ++ XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m); ++ XFlush(x11_display); ++ } else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) { ++ if (requested != None) { ++ xdnd_source_window = event.xclient.data.l[0]; ++ if (xdnd_version >= 1) { ++ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, event.xclient.data.l[2]); ++ } else { ++ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime); ++ } ++ } else { ++ //Reply that we're not interested. ++ XClientMessageEvent m; ++ memset(&m, 0, sizeof(m)); ++ m.type = ClientMessage; ++ m.display = event.xclient.display; ++ m.window = event.xclient.data.l[0]; ++ m.message_type = xdnd_finished; ++ m.format = 32; ++ m.data.l[0] = x11_window; ++ m.data.l[1] = 0; ++ m.data.l[2] = None; //Failed. ++ XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m); ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ XFlush(x11_display); ++ ++ if (do_mouse_warp) { ++ XWarpPointer(x11_display, None, x11_window, ++ 0, 0, 0, 0, (int)current_videomode.width / 2, (int)current_videomode.height / 2); ++ ++ /* ++ Window root, child; ++ int root_x, root_y; ++ int win_x, win_y; ++ unsigned int mask; ++ XQueryPointer( x11_display, x11_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ); ++ ++ printf("Root: %d,%d\n", root_x, root_y); ++ printf("Win: %d,%d\n", win_x, win_y); ++ */ ++ } ++ ++ input->flush_buffered_events(); ++} ++ ++MainLoop *OS_X11::get_main_loop() const { ++ return main_loop; ++} ++ ++uint64_t OS_X11::get_embedded_pck_offset() const { ++ FileAccessRef f = FileAccess::open(get_executable_path(), FileAccess::READ); ++ if (!f) { ++ return 0; ++ } ++ ++ // Read and check ELF magic number. ++ { ++ uint32_t magic = f->get_32(); ++ if (magic != 0x464c457f) { // 0x7F + "ELF" ++ return 0; ++ } ++ } ++ ++ // Read program architecture bits from class field. ++ int bits = f->get_8() * 32; ++ ++ // Get info about the section header table. ++ int64_t section_table_pos; ++ int64_t section_header_size; ++ if (bits == 32) { ++ section_header_size = 40; ++ f->seek(0x20); ++ section_table_pos = f->get_32(); ++ f->seek(0x30); ++ } else { // 64 ++ section_header_size = 64; ++ f->seek(0x28); ++ section_table_pos = f->get_64(); ++ f->seek(0x3c); ++ } ++ int num_sections = f->get_16(); ++ int string_section_idx = f->get_16(); ++ ++ // Load the strings table. ++ uint8_t *strings; ++ { ++ // Jump to the strings section header. ++ f->seek(section_table_pos + string_section_idx * section_header_size); ++ ++ // Read strings data size and offset. ++ int64_t string_data_pos; ++ int64_t string_data_size; ++ if (bits == 32) { ++ f->seek(f->get_position() + 0x10); ++ string_data_pos = f->get_32(); ++ string_data_size = f->get_32(); ++ } else { // 64 ++ f->seek(f->get_position() + 0x18); ++ string_data_pos = f->get_64(); ++ string_data_size = f->get_64(); ++ } ++ ++ // Read strings data. ++ f->seek(string_data_pos); ++ strings = (uint8_t *)memalloc(string_data_size); ++ if (!strings) { ++ return 0; ++ } ++ f->get_buffer(strings, string_data_size); ++ } ++ ++ // Search for the "pck" section. ++ int64_t off = 0; ++ for (int i = 0; i < num_sections; ++i) { ++ int64_t section_header_pos = section_table_pos + i * section_header_size; ++ f->seek(section_header_pos); ++ ++ uint32_t name_offset = f->get_32(); ++ if (strcmp((char *)strings + name_offset, "pck") == 0) { ++ if (bits == 32) { ++ f->seek(section_header_pos + 0x10); ++ off = f->get_32(); ++ } else { // 64 ++ f->seek(section_header_pos + 0x18); ++ off = f->get_64(); ++ } ++ break; ++ } ++ } ++ memfree(strings); ++ ++ return off; ++} ++ ++void OS_X11::delete_main_loop() { ++ // Send owned clipboard data to clipboard manager before exit. ++ // This has to be done here because the clipboard data is cleared before finalize(). ++ _clipboard_transfer_ownership(XA_PRIMARY, x11_window); ++ _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window); ++ ++ if (main_loop) { ++ memdelete(main_loop); ++ } ++ main_loop = nullptr; ++} ++ ++void OS_X11::set_main_loop(MainLoop *p_main_loop) { ++ main_loop = p_main_loop; ++ input->set_main_loop(p_main_loop); ++} ++ ++bool OS_X11::can_draw() const { ++ return !minimized; ++}; ++ ++void OS_X11::set_clipboard(const String &p_text) { ++ { ++ // The clipboard content can be accessed while polling for events. ++ MutexLock mutex_lock(events_mutex); ++ OS::set_clipboard(p_text); ++ } ++ ++ XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime); ++ XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime); ++}; ++ ++Bool OS_X11::_predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg) { ++ if (event->type == SelectionNotify && event->xselection.requestor == *(Window *)arg) { ++ return True; ++ } else { ++ return False; ++ } ++} ++ ++Bool OS_X11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) { ++ if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) { ++ return True; ++ } else { ++ return False; ++ } ++} ++ ++String OS_X11::_get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const { ++ String ret; ++ ++ Window selection_owner = XGetSelectionOwner(x11_display, p_source); ++ if (selection_owner == x11_window) { ++ static const char *target_type = "PRIMARY"; ++ if (p_source != None && get_atom_name(x11_display, p_source) == target_type) { ++ return OS::get_clipboard_primary(); ++ } else { ++ return OS::get_clipboard(); ++ } ++ } ++ ++ if (selection_owner != None) { ++ // Block events polling while processing selection events. ++ MutexLock mutex_lock(events_mutex); ++ ++ Atom selection = XA_PRIMARY; ++ XConvertSelection(x11_display, p_source, target, selection, ++ x11_window, CurrentTime); ++ ++ XFlush(x11_display); ++ ++ // Blocking wait for predicate to be True and remove the event from the queue. ++ XEvent event; ++ XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window); ++ ++ // Do not get any data, see how much data is there. ++ Atom type; ++ int format, result; ++ unsigned long len, bytes_left, dummy; ++ unsigned char *data; ++ XGetWindowProperty(x11_display, x11_window, ++ selection, // Tricky.. ++ 0, 0, // offset - len ++ 0, // Delete 0==FALSE ++ AnyPropertyType, // flag ++ &type, // return type ++ &format, // return format ++ &len, &bytes_left, // data length ++ &data); ++ ++ if (data) { ++ XFree(data); ++ } ++ ++ if (type == XInternAtom(x11_display, "INCR", 0)) { ++ // Data is going to be received incrementally. ++ DEBUG_LOG_X11("INCR selection started.\n"); ++ ++ LocalVector incr_data; ++ uint32_t data_size = 0; ++ bool success = false; ++ ++ // Delete INCR property to notify the owner. ++ XDeleteProperty(x11_display, x11_window, type); ++ ++ // Process events from the queue. ++ bool done = false; ++ while (!done) { ++ if (!_wait_for_events()) { ++ // Error or timeout, abort. ++ break; ++ } ++ ++ // Non-blocking wait for next event and remove it from the queue. ++ XEvent ev; ++ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, nullptr)) { ++ result = XGetWindowProperty(x11_display, x11_window, ++ selection, // selection type ++ 0, LONG_MAX, // offset - len ++ True, // delete property to notify the owner ++ AnyPropertyType, // flag ++ &type, // return type ++ &format, // return format ++ &len, &bytes_left, // data length ++ &data); ++ ++ DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format); ++ ++ if (result == Success) { ++ if (data && (len > 0)) { ++ uint32_t prev_size = incr_data.size(); ++ if (prev_size == 0) { ++ // First property contains initial data size. ++ unsigned long initial_size = *(unsigned long *)data; ++ incr_data.resize(initial_size); ++ } else { ++ // New chunk, resize to be safe and append data. ++ incr_data.resize(MAX(data_size + len, prev_size)); ++ memcpy(incr_data.ptr() + data_size, data, len); ++ data_size += len; ++ } ++ } else { ++ // Last chunk, process finished. ++ done = true; ++ success = true; ++ } ++ } else { ++ printf("Failed to get selection data chunk.\n"); ++ done = true; ++ } ++ ++ if (data) { ++ XFree(data); ++ } ++ ++ if (done) { ++ break; ++ } ++ } ++ } ++ ++ if (success && (data_size > 0)) { ++ ret.parse_utf8((const char *)incr_data.ptr(), data_size); ++ } ++ } else if (bytes_left > 0) { ++ // Data is ready and can be processed all at once. ++ result = XGetWindowProperty(x11_display, x11_window, ++ selection, 0, bytes_left, 0, ++ AnyPropertyType, &type, &format, ++ &len, &dummy, &data); ++ ++ if (result == Success) { ++ ret.parse_utf8((const char *)data); ++ } else { ++ printf("Failed to get selection data.\n"); ++ } ++ ++ if (data) { ++ XFree(data); ++ } ++ } ++ } ++ ++ return ret; ++} ++ ++String OS_X11::_get_clipboard(Atom p_source, Window x11_window) const { ++ String ret; ++ Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True); ++ if (utf8_atom != None) { ++ ret = _get_clipboard_impl(p_source, x11_window, utf8_atom); ++ } ++ if (ret.empty()) { ++ ret = _get_clipboard_impl(p_source, x11_window, XA_STRING); ++ } ++ return ret; ++} ++ ++String OS_X11::get_clipboard() const { ++ String ret; ++ ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window); ++ ++ if (ret.empty()) { ++ ret = _get_clipboard(XA_PRIMARY, x11_window); ++ }; ++ ++ return ret; ++} ++ ++Bool OS_X11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) { ++ if (event->xany.window == *(Window *)arg) { ++ return (event->type == SelectionRequest) || ++ (event->type == SelectionNotify); ++ } else { ++ return False; ++ } ++} ++ ++void OS_X11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const { ++ Window selection_owner = XGetSelectionOwner(x11_display, p_source); ++ ++ if (selection_owner != x11_window) { ++ return; ++ } ++ ++ // Block events polling while processing selection events. ++ MutexLock mutex_lock(events_mutex); ++ ++ Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False); ++ Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False); ++ XConvertSelection(x11_display, clipboard_manager, save_targets, None, ++ x11_window, CurrentTime); ++ ++ // Process events from the queue. ++ while (true) { ++ if (!_wait_for_events()) { ++ // Error or timeout, abort. ++ break; ++ } ++ ++ // Non-blocking wait for next event and remove it from the queue. ++ XEvent ev; ++ while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) { ++ switch (ev.type) { ++ case SelectionRequest: ++ _handle_selection_request_event(&(ev.xselectionrequest)); ++ break; ++ ++ case SelectionNotify: { ++ if (ev.xselection.target == save_targets) { ++ // Once SelectionNotify is received, we're done whether it succeeded or not. ++ return; ++ } ++ ++ break; ++ } ++ } ++ } ++ } ++} ++ ++void OS_X11::set_clipboard_primary(const String &p_text) { ++ if (!p_text.empty()) { ++ { ++ // The clipboard content can be accessed while polling for events. ++ MutexLock mutex_lock(events_mutex); ++ OS::set_clipboard_primary(p_text); ++ } ++ ++ XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime); ++ XSetSelectionOwner(x11_display, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime); ++ } ++} ++ ++String OS_X11::get_clipboard_primary() const { ++ String ret; ++ ret = _get_clipboard(XInternAtom(x11_display, "PRIMARY", 0), x11_window); ++ ++ if (ret.empty()) { ++ ret = _get_clipboard(XA_PRIMARY, x11_window); ++ } ++ ++ return ret; ++} ++ ++String OS_X11::get_name() const { ++ return "X11"; ++} ++ ++Error OS_X11::shell_open(String p_uri) { ++ Error ok; ++ int err_code; ++ List args; ++ args.push_back(p_uri); ++ ++ // Agnostic ++ ok = execute("xdg-open", args, true, nullptr, nullptr, &err_code); ++ if (ok == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ // GNOME ++ args.push_front("open"); // The command is `gio open`, so we need to add it to args ++ ok = execute("gio", args, true, nullptr, nullptr, &err_code); ++ if (ok == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ args.pop_front(); ++ ok = execute("gvfs-open", args, true, nullptr, nullptr, &err_code); ++ if (ok == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ // KDE ++ ok = execute("kde-open5", args, true, nullptr, nullptr, &err_code); ++ if (ok == OK && !err_code) { ++ return OK; ++ } ++ ok = execute("kde-open", args, true, nullptr, nullptr, &err_code); ++ return !err_code ? ok : FAILED; ++} ++ ++bool OS_X11::_check_internal_feature_support(const String &p_feature) { ++ if (p_feature == "pc") { ++ return true; ++ } ++ ++ if (p_feature == "primary_clipboard") { ++ return true; ++ } ++ ++ return false; ++} ++ ++String OS_X11::get_config_path() const { ++ if (has_environment("XDG_CONFIG_HOME")) { ++ if (get_environment("XDG_CONFIG_HOME").is_abs_path()) { ++ return get_environment("XDG_CONFIG_HOME"); ++ } else { ++ WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.config` or `.` per the XDG Base Directory specification."); ++ } ++ } ++ if (has_environment("HOME")) { ++ return get_environment("HOME").plus_file(".config"); ++ } ++ return "."; ++} ++ ++String OS_X11::get_data_path() const { ++ if (has_environment("XDG_DATA_HOME")) { ++ if (get_environment("XDG_DATA_HOME").is_abs_path()) { ++ return get_environment("XDG_DATA_HOME"); ++ } else { ++ WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.local/share` or `get_config_path()` per the XDG Base Directory specification."); ++ } ++ } ++ if (has_environment("HOME")) { ++ return get_environment("HOME").plus_file(".local/share"); ++ } ++ return get_config_path(); ++} ++ ++String OS_X11::get_cache_path() const { ++ if (has_environment("XDG_CACHE_HOME")) { ++ if (get_environment("XDG_CACHE_HOME").is_abs_path()) { ++ return get_environment("XDG_CACHE_HOME"); ++ } else { ++ WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `$HOME/.cache` or `get_config_path()` per the XDG Base Directory specification."); ++ } ++ } ++ if (has_environment("HOME")) { ++ return get_environment("HOME").plus_file(".cache"); ++ } ++ return get_config_path(); ++} ++ ++String OS_X11::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { ++ String xdgparam; ++ ++ switch (p_dir) { ++ case SYSTEM_DIR_DESKTOP: { ++ xdgparam = "DESKTOP"; ++ } break; ++ case SYSTEM_DIR_DCIM: { ++ xdgparam = "PICTURES"; ++ ++ } break; ++ case SYSTEM_DIR_DOCUMENTS: { ++ xdgparam = "DOCUMENTS"; ++ ++ } break; ++ case SYSTEM_DIR_DOWNLOADS: { ++ xdgparam = "DOWNLOAD"; ++ ++ } break; ++ case SYSTEM_DIR_MOVIES: { ++ xdgparam = "VIDEOS"; ++ ++ } break; ++ case SYSTEM_DIR_MUSIC: { ++ xdgparam = "MUSIC"; ++ ++ } break; ++ case SYSTEM_DIR_PICTURES: { ++ xdgparam = "PICTURES"; ++ ++ } break; ++ case SYSTEM_DIR_RINGTONES: { ++ xdgparam = "MUSIC"; ++ ++ } break; ++ } ++ ++ String pipe; ++ List arg; ++ arg.push_back(xdgparam); ++ Error err = const_cast(this)->execute("xdg-user-dir", arg, true, nullptr, &pipe); ++ if (err != OK) { ++ return "."; ++ } ++ return pipe.strip_edges(); ++} ++ ++void OS_X11::move_window_to_foreground() { ++ XEvent xev; ++ Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False); ++ ++ memset(&xev, 0, sizeof(xev)); ++ xev.type = ClientMessage; ++ xev.xclient.window = x11_window; ++ xev.xclient.message_type = net_active_window; ++ xev.xclient.format = 32; ++ xev.xclient.data.l[0] = 1; ++ xev.xclient.data.l[1] = CurrentTime; ++ ++ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); ++ XFlush(x11_display); ++} ++ ++void OS_X11::set_cursor_shape(CursorShape p_shape) { ++ ERR_FAIL_INDEX(p_shape, CURSOR_MAX); ++ ++ if (p_shape == current_cursor) { ++ return; ++ } ++ ++ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { ++ if (cursors[p_shape] != None) { ++ XDefineCursor(x11_display, x11_window, cursors[p_shape]); ++ } else if (cursors[CURSOR_ARROW] != None) { ++ XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]); ++ } ++ } ++ ++ current_cursor = p_shape; ++} ++ ++OS::CursorShape OS_X11::get_cursor_shape() const { ++ return current_cursor; ++} ++ ++void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { ++ if (p_cursor.is_valid()) { ++ Map>::Element *cursor_c = cursors_cache.find(p_shape); ++ ++ if (cursor_c) { ++ if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { ++ set_cursor_shape(p_shape); ++ return; ++ } ++ ++ cursors_cache.erase(p_shape); ++ } ++ ++ Ref texture = p_cursor; ++ Ref atlas_texture = p_cursor; ++ Ref image; ++ Size2 texture_size; ++ Rect2 atlas_rect; ++ ++ if (texture.is_valid()) { ++ image = texture->get_data(); ++ } ++ ++ if (!image.is_valid() && atlas_texture.is_valid()) { ++ texture = atlas_texture->get_atlas(); ++ ++ atlas_rect.size.width = texture->get_width(); ++ atlas_rect.size.height = texture->get_height(); ++ atlas_rect.position.x = atlas_texture->get_region().position.x; ++ atlas_rect.position.y = atlas_texture->get_region().position.y; ++ ++ texture_size.width = atlas_texture->get_region().size.x; ++ texture_size.height = atlas_texture->get_region().size.y; ++ } else if (image.is_valid()) { ++ texture_size.width = texture->get_width(); ++ texture_size.height = texture->get_height(); ++ } ++ ++ ERR_FAIL_COND(!texture.is_valid()); ++ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); ++ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); ++ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); ++ ++ image = texture->get_data(); ++ ++ ERR_FAIL_COND(!image.is_valid()); ++ ++ // Create the cursor structure ++ XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height); ++ XcursorUInt image_size = texture_size.width * texture_size.height; ++ XcursorDim size = sizeof(XcursorPixel) * image_size; ++ ++ cursor_image->version = 1; ++ cursor_image->size = size; ++ cursor_image->xhot = p_hotspot.x; ++ cursor_image->yhot = p_hotspot.y; ++ ++ // allocate memory to contain the whole file ++ cursor_image->pixels = (XcursorPixel *)memalloc(size); ++ ++ image->lock(); ++ ++ for (XcursorPixel index = 0; index < image_size; index++) { ++ int row_index = floor(index / texture_size.width) + atlas_rect.position.y; ++ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; ++ ++ if (atlas_texture.is_valid()) { ++ column_index = MIN(column_index, atlas_rect.size.width - 1); ++ row_index = MIN(row_index, atlas_rect.size.height - 1); ++ } ++ ++ *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); ++ } ++ ++ image->unlock(); ++ ++ ERR_FAIL_COND(cursor_image->pixels == nullptr); ++ ++ // Save it for a further usage ++ cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image); ++ ++ Vector params; ++ params.push_back(p_cursor); ++ params.push_back(p_hotspot); ++ cursors_cache.insert(p_shape, params); ++ ++ if (p_shape == current_cursor) { ++ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { ++ XDefineCursor(x11_display, x11_window, cursors[p_shape]); ++ } ++ } ++ ++ memfree(cursor_image->pixels); ++ XcursorImageDestroy(cursor_image); ++ } else { ++ // Reset to default system cursor ++ if (img[p_shape]) { ++ cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]); ++ } ++ ++ CursorShape c = current_cursor; ++ current_cursor = CURSOR_MAX; ++ set_cursor_shape(c); ++ ++ cursors_cache.erase(p_shape); ++ } ++} ++ ++void OS_X11::release_rendering_thread() { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ context_gl->release_current(); ++#endif ++} ++ ++void OS_X11::make_rendering_thread() { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ context_gl->make_current(); ++#endif ++} ++ ++void OS_X11::swap_buffers() { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ context_gl->swap_buffers(); ++#endif ++} ++ ++void OS_X11::alert(const String &p_alert, const String &p_title) { ++ if (is_no_window_mode_enabled()) { ++ print_line("ALERT: " + p_title + ": " + p_alert); ++ return; ++ } ++ ++ const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" }; ++ ++ String path = get_environment("PATH"); ++ Vector path_elems = path.split(":", false); ++ String program; ++ ++ for (int i = 0; i < path_elems.size(); i++) { ++ for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) { ++ String tested_path = path_elems[i].plus_file(message_programs[k]); ++ ++ if (FileAccess::exists(tested_path)) { ++ program = tested_path; ++ break; ++ } ++ } ++ ++ if (program.length()) { ++ break; ++ } ++ } ++ ++ List args; ++ ++ if (program.ends_with("zenity")) { ++ args.push_back("--error"); ++ args.push_back("--width"); ++ args.push_back("500"); ++ args.push_back("--title"); ++ args.push_back(p_title); ++ args.push_back("--text"); ++ args.push_back(p_alert); ++ } ++ ++ if (program.ends_with("kdialog")) { ++ args.push_back("--error"); ++ args.push_back(p_alert); ++ args.push_back("--title"); ++ args.push_back(p_title); ++ } ++ ++ if (program.ends_with("Xdialog")) { ++ args.push_back("--title"); ++ args.push_back(p_title); ++ args.push_back("--msgbox"); ++ args.push_back(p_alert); ++ args.push_back("0"); ++ args.push_back("0"); ++ } ++ ++ if (program.ends_with("xmessage")) { ++ args.push_back("-center"); ++ args.push_back("-title"); ++ args.push_back(p_title); ++ args.push_back(p_alert); ++ } ++ ++ if (program.length()) { ++ execute(program, args, true); ++ } else { ++ print_line(p_alert); ++ } ++} ++ ++bool g_set_icon_error = false; ++int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) { ++ g_set_icon_error = true; ++ return 0; ++} ++ ++void OS_X11::set_icon(const Ref &p_icon) { ++ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler); ++ ++ Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False); ++ ++ if (p_icon.is_valid()) { ++ Ref img = p_icon->duplicate(); ++ img->convert(Image::FORMAT_RGBA8); ++ ++ while (true) { ++ int w = img->get_width(); ++ int h = img->get_height(); ++ ++ if (g_set_icon_error) { ++ g_set_icon_error = false; ++ ++ WARN_PRINT("Icon too large, attempting to resize icon."); ++ ++ int new_width, new_height; ++ if (w > h) { ++ new_width = w / 2; ++ new_height = h * new_width / w; ++ } else { ++ new_height = h / 2; ++ new_width = w * new_height / h; ++ } ++ ++ w = new_width; ++ h = new_height; ++ ++ if (!w || !h) { ++ WARN_PRINT("Unable to set icon."); ++ break; ++ } ++ ++ img->resize(w, h, Image::INTERPOLATE_CUBIC); ++ } ++ ++ // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits ++ Vector pd; ++ ++ pd.resize(2 + w * h); ++ ++ pd.write[0] = w; ++ pd.write[1] = h; ++ ++ PoolVector::Read r = img->get_data().read(); ++ ++ long *wr = &pd.write[2]; ++ uint8_t const *pr = r.ptr(); ++ ++ for (int i = 0; i < w * h; i++) { ++ long v = 0; ++ // A R G B ++ v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2]; ++ *wr++ = v; ++ pr += 4; ++ } ++ ++ if (net_wm_icon != None) { ++ XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size()); ++ } ++ ++ if (!g_set_icon_error) { ++ break; ++ } ++ } ++ } else { ++ XDeleteProperty(x11_display, x11_window, net_wm_icon); ++ } ++ ++ XFlush(x11_display); ++ XSetErrorHandler(oldHandler); ++} ++ ++void OS_X11::force_process_input() { ++ process_xevents(); // get rid of pending events ++#ifdef JOYDEV_ENABLED ++ joypad->process_joypads(); ++#endif ++} ++ ++void OS_X11::run() { ++ force_quit = false; ++ ++ if (!main_loop) { ++ return; ++ } ++ ++ main_loop->init(); ++ ++ //uint64_t last_ticks=get_ticks_usec(); ++ ++ //int frames=0; ++ //uint64_t frame=0; ++ ++ while (!force_quit) { ++ process_xevents(); // get rid of pending events ++#ifdef JOYDEV_ENABLED ++ joypad->process_joypads(); ++#endif ++ if (Main::iteration()) { ++ break; ++ } ++ }; ++ ++ main_loop->finish(); ++} ++ ++bool OS_X11::is_joy_known(int p_device) { ++ return input->is_joy_mapped(p_device); ++} ++ ++String OS_X11::get_joy_guid(int p_device) const { ++ return input->get_joy_guid_remapped(p_device); ++} ++ ++void OS_X11::_set_use_vsync(bool p_enable) { ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ if (context_gl) { ++ context_gl->set_use_vsync(p_enable); ++ } ++#endif ++} ++/* ++bool OS_X11::is_vsync_enabled() const { ++ ++ if (context_gl) ++ return context_gl->is_using_vsync(); ++ ++ return true; ++} ++*/ ++void OS_X11::set_context(int p_context) { ++ XClassHint *classHint = XAllocClassHint(); ++ ++ if (classHint) { ++ CharString name_str; ++ switch (p_context) { ++ case CONTEXT_EDITOR: ++ name_str = "Godot_Editor"; ++ break; ++ case CONTEXT_PROJECTMAN: ++ name_str = "Godot_ProjectList"; ++ break; ++ case CONTEXT_ENGINE: ++ name_str = "Godot_Engine"; ++ break; ++ } ++ ++ CharString class_str; ++ if (p_context == CONTEXT_ENGINE) { ++ String config_name = GLOBAL_GET("application/config/name"); ++ if (config_name.length() == 0) { ++ class_str = "Godot_Engine"; ++ } else { ++ class_str = config_name.utf8(); ++ } ++ } else { ++ class_str = "Godot"; ++ } ++ ++ classHint->res_class = class_str.ptrw(); ++ classHint->res_name = name_str.ptrw(); ++ ++ XSetClassHint(x11_display, x11_window, classHint); ++ XFree(classHint); ++ } ++} ++ ++OS::PowerState OS_X11::get_power_state() { ++ return power_manager->get_power_state(); ++} ++ ++int OS_X11::get_power_seconds_left() { ++ return power_manager->get_power_seconds_left(); ++} ++ ++int OS_X11::get_power_percent_left() { ++ return power_manager->get_power_percent_left(); ++} ++ ++void OS_X11::disable_crash_handler() { ++ crash_handler.disable(); ++} ++ ++bool OS_X11::is_disable_crash_handler() const { ++ return crash_handler.is_disabled(); ++} ++ ++static String get_mountpoint(const String &p_path) { ++ struct stat s; ++ if (stat(p_path.utf8().get_data(), &s)) { ++ return ""; ++ } ++ ++#ifdef HAVE_MNTENT ++ dev_t dev = s.st_dev; ++ FILE *fd = setmntent("/proc/mounts", "r"); ++ if (!fd) { ++ return ""; ++ } ++ ++ struct mntent mnt; ++ char buf[1024]; ++ size_t buflen = 1024; ++ while (getmntent_r(fd, &mnt, buf, buflen)) { ++ if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) { ++ endmntent(fd); ++ return String(mnt.mnt_dir); ++ } ++ } ++ ++ endmntent(fd); ++#endif ++ return ""; ++} ++ ++Error OS_X11::move_to_trash(const String &p_path) { ++ String path = p_path.rstrip("/"); // Strip trailing slash when path points to a directory ++ ++ int err_code; ++ List args; ++ args.push_back(path); ++ args.push_front("trash"); // The command is `gio trash ` so we need to add it to args. ++ Error result = execute("gio", args, true, nullptr, nullptr, &err_code); // For GNOME based machines. ++ if (result == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ ++ args.pop_front(); ++ args.push_front("move"); ++ args.push_back("trash:/"); // The command is `kioclient5 move trash:/`. ++ result = execute("kioclient5", args, true, nullptr, nullptr, &err_code); // For KDE based machines. ++ if (result == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ ++ args.pop_front(); ++ args.pop_back(); ++ result = execute("gvfs-trash", args, true, nullptr, nullptr, &err_code); // For older Linux machines. ++ if (result == OK && !err_code) { ++ return OK; ++ } else if (err_code == 2) { ++ return ERR_FILE_NOT_FOUND; ++ } ++ ++ // If the commands `kioclient5`, `gio` or `gvfs-trash` don't exist on the system we do it manually. ++ String trash_path = ""; ++ String mnt = get_mountpoint(path); ++ ++ // If there is a directory "[Mountpoint]/.Trash-[UID], use it as the trash can. ++ if (mnt != "") { ++ String mountpoint_trash_path(mnt + "/.Trash-" + itos(getuid())); ++ struct stat s; ++ if (!stat(mountpoint_trash_path.utf8().get_data(), &s)) { ++ trash_path = mountpoint_trash_path; ++ } ++ } ++ ++ // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash" as the trash can. ++ if (trash_path == "") { ++ char *dhome = getenv("XDG_DATA_HOME"); ++ if (dhome) { ++ trash_path = String::utf8(dhome) + "/Trash"; ++ } ++ } ++ ++ // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash" as the trash can. ++ if (trash_path == "") { ++ char *home = getenv("HOME"); ++ if (home) { ++ trash_path = String::utf8(home) + "/.local/share/Trash"; ++ } ++ } ++ ++ // Issue an error if none of the previous locations is appropriate for the trash can. ++ ERR_FAIL_COND_V_MSG(trash_path == "", FAILED, "Could not determine the trash can location"); ++ ++ // Create needed directories for decided trash can location. ++ { ++ DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ++ Error err = dir_access->make_dir_recursive(trash_path); ++ ++ // Issue an error if trash can is not created properly. ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "\""); ++ err = dir_access->make_dir_recursive(trash_path + "/files"); ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "/files\""); ++ err = dir_access->make_dir_recursive(trash_path + "/info"); ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not create the trash path \"" + trash_path + "/info\""); ++ } ++ ++ // The trash can is successfully created, now we check that we don't exceed our file name length limit. ++ // If the file name is too long trim it so we can add the identifying number and ".trashinfo". ++ // Assumes that the file name length limit is 255 characters. ++ String file_name = path.get_file(); ++ if (file_name.length() > 240) { ++ file_name = file_name.substr(0, file_name.length() - 15); ++ } ++ ++ String dest_path = trash_path + "/files/" + file_name; ++ struct stat buff; ++ int id_number = 0; ++ String fn = file_name; ++ ++ // Checks if a resource with the same name already exist in the trash can, ++ // if there is, add an identifying number to our resource's name. ++ while (stat(dest_path.utf8().get_data(), &buff) == 0) { ++ id_number++; ++ ++ // Added a limit to check for identically named files already on the trash can ++ // if there are too many it could make the editor unresponsive. ++ ERR_FAIL_COND_V_MSG(id_number > 99, FAILED, "Too many identically named resources already in the trash can."); ++ fn = file_name + "." + itos(id_number); ++ dest_path = trash_path + "/files/" + fn; ++ } ++ file_name = fn; ++ ++ String renamed_path = path.get_base_dir() + "/" + file_name; ++ ++ // Generates the .trashinfo file ++ OS::Date date = OS::get_singleton()->get_date(false); ++ OS::Time time = OS::get_singleton()->get_time(false); ++ String timestamp = vformat("%04d-%02d-%02dT%02d:%02d:", date.year, (int)date.month, date.day, time.hour, time.min); ++ timestamp = vformat("%s%02d", timestamp, time.sec); // vformat only supports up to 6 arguments. ++ String trash_info = "[Trash Info]\nPath=" + path.http_escape() + "\nDeletionDate=" + timestamp + "\n"; ++ { ++ Error err; ++ FileAccessRef file = FileAccess::open(trash_path + "/info/" + file_name + ".trashinfo", FileAccess::WRITE, &err); ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't create trashinfo file: \"" + trash_path + "/info/" + file_name + ".trashinfo\""); ++ file->store_string(trash_info); ++ file->close(); ++ ++ // Rename our resource before moving it to the trash can. ++ DirAccessRef dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ++ err = dir_access->rename(path, renamed_path); ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't rename file \"" + path + "\" to \"" + renamed_path + "\""); ++ } ++ ++ // Move the given resource to the trash can. ++ // Do not use DirAccess:rename() because it can't move files across multiple mountpoints. ++ List mv_args; ++ mv_args.push_back(renamed_path); ++ mv_args.push_back(trash_path + "/files"); ++ { ++ int retval; ++ Error err = execute("mv", mv_args, true, nullptr, nullptr, &retval); ++ ++ // Issue an error if "mv" failed to move the given resource to the trash can. ++ if (err != OK || retval != 0) { ++ ERR_PRINT("move_to_trash: Could not move the resource \"" + path + "\" to the trash can \"" + trash_path + "/files\""); ++ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ++ err = dir_access->rename(renamed_path, path); ++ memdelete(dir_access); ++ ERR_FAIL_COND_V_MSG(err != OK, err, "Could not rename \"" + renamed_path + "\" back to its original name: \"" + path + "\""); ++ return FAILED; ++ } ++ } ++ return OK; ++} ++ ++OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const { ++ XkbDescRec *xkbdesc = XkbAllocKeyboard(); ++ ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY); ++ ++ if (XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc) != Success) { ++ XkbFreeKeyboard(xkbdesc, 0, true); ++ ERR_FAIL_V(LATIN_KEYBOARD_QWERTY); ++ } ++ ++ Vector info = get_atom_name(x11_display, xkbdesc->names->symbols).split("+"); ++ XkbFreeKeyboard(xkbdesc, 0, true); ++ ++ ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY); ++ ++ if (info[1].find("colemak") != -1) { ++ return LATIN_KEYBOARD_COLEMAK; ++ } else if (info[1].find("qwertz") != -1) { ++ return LATIN_KEYBOARD_QWERTZ; ++ } else if (info[1].find("azerty") != -1) { ++ return LATIN_KEYBOARD_AZERTY; ++ } else if (info[1].find("qzerty") != -1) { ++ return LATIN_KEYBOARD_QZERTY; ++ } else if (info[1].find("dvorak") != -1) { ++ return LATIN_KEYBOARD_DVORAK; ++ } else if (info[1].find("neo") != -1) { ++ return LATIN_KEYBOARD_NEO; ++ } ++ ++ return LATIN_KEYBOARD_QWERTY; ++} ++ ++int OS_X11::keyboard_get_layout_count() const { ++ int _group_count = 0; ++ XkbDescRec *kbd = XkbAllocKeyboard(); ++ if (kbd) { ++ kbd->dpy = x11_display; ++ XkbGetControls(x11_display, XkbAllControlsMask, kbd); ++ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); ++ ++ const Atom *groups = kbd->names->groups; ++ if (kbd->ctrls != nullptr) { ++ _group_count = kbd->ctrls->num_groups; ++ } else { ++ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { ++ _group_count++; ++ } ++ } ++ XkbFreeKeyboard(kbd, 0, true); ++ } ++ return _group_count; ++} ++ ++int OS_X11::keyboard_get_current_layout() const { ++ XkbStateRec state; ++ XkbGetState(x11_display, XkbUseCoreKbd, &state); ++ return state.group; ++} ++ ++void OS_X11::keyboard_set_current_layout(int p_index) { ++ ERR_FAIL_INDEX(p_index, keyboard_get_layout_count()); ++ XkbLockGroup(x11_display, XkbUseCoreKbd, p_index); ++} ++ ++String OS_X11::keyboard_get_layout_language(int p_index) const { ++ String ret; ++ XkbDescRec *kbd = XkbAllocKeyboard(); ++ if (kbd) { ++ kbd->dpy = x11_display; ++ XkbGetControls(x11_display, XkbAllControlsMask, kbd); ++ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); ++ XkbGetNames(x11_display, XkbGroupNamesMask, kbd); ++ ++ int _group_count = 0; ++ const Atom *groups = kbd->names->groups; ++ if (kbd->ctrls != nullptr) { ++ _group_count = kbd->ctrls->num_groups; ++ } else { ++ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { ++ _group_count++; ++ } ++ } ++ ++ Atom names = kbd->names->symbols; ++ if (names != None) { ++ Vector info = get_atom_name(x11_display, names).split("+"); ++ if (p_index >= 0 && p_index < _group_count) { ++ if (p_index + 1 < info.size()) { ++ ret = info[p_index + 1]; // Skip "pc" at the start and "inet"/"group" at the end of symbols. ++ } else { ++ ret = "en"; // No symbol for layout fallback to "en". ++ } ++ } else { ++ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ")."); ++ } ++ } ++ XkbFreeKeyboard(kbd, 0, true); ++ } ++ return ret.substr(0, 2); ++} ++ ++String OS_X11::keyboard_get_layout_name(int p_index) const { ++ String ret; ++ XkbDescRec *kbd = XkbAllocKeyboard(); ++ if (kbd) { ++ kbd->dpy = x11_display; ++ XkbGetControls(x11_display, XkbAllControlsMask, kbd); ++ XkbGetNames(x11_display, XkbSymbolsNameMask, kbd); ++ XkbGetNames(x11_display, XkbGroupNamesMask, kbd); ++ ++ int _group_count = 0; ++ const Atom *groups = kbd->names->groups; ++ if (kbd->ctrls != nullptr) { ++ _group_count = kbd->ctrls->num_groups; ++ } else { ++ while (_group_count < XkbNumKbdGroups && groups[_group_count] != None) { ++ _group_count++; ++ } ++ } ++ ++ if (p_index >= 0 && p_index < _group_count) { ++ ret = get_atom_name(x11_display, groups[p_index]); ++ } else { ++ ERR_PRINT("Index " + itos(p_index) + "is out of bounds (" + itos(_group_count) + ")."); ++ } ++ XkbFreeKeyboard(kbd, 0, true); ++ } ++ return ret; ++} ++ ++uint32_t OS_X11::keyboard_get_scancode_from_physical(uint32_t p_scancode) const { ++ unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK; ++ unsigned int scancode_no_mod = p_scancode & KEY_CODE_MASK; ++ unsigned int xkeycode = KeyMappingX11::get_xlibcode((uint32_t)scancode_no_mod); ++ KeySym xkeysym = XkbKeycodeToKeysym(x11_display, xkeycode, keyboard_get_current_layout(), 0); ++ if (xkeysym >= 'a' && xkeysym <= 'z') { ++ xkeysym -= ('a' - 'A'); ++ } ++ ++ uint32_t key = KeyMappingX11::get_keycode(xkeysym); ++ // If not found, fallback to QWERTY. ++ // This should match the behavior of the event pump ++ if (key == 0) { ++ return p_scancode; ++ } ++ return (uint32_t)(key | modifiers); ++} ++ ++void OS_X11::update_real_mouse_position() { ++ Window root_return, child_return; ++ int root_x, root_y, win_x, win_y; ++ unsigned int mask_return; ++ ++ Bool xquerypointer_result = XQueryPointer(x11_display, x11_window, &root_return, &child_return, &root_x, &root_y, ++ &win_x, &win_y, &mask_return); ++ ++ if (xquerypointer_result) { ++ if (win_x > 0 && win_y > 0 && win_x <= current_videomode.width && win_y <= current_videomode.height) { ++ last_mouse_pos.x = win_x; ++ last_mouse_pos.y = win_y; ++ last_mouse_pos_valid = true; ++ input->set_mouse_position(last_mouse_pos); ++ } ++ } ++} ++ ++OS_X11::OS_X11() { ++#ifdef PULSEAUDIO_ENABLED ++ AudioDriverManager::add_driver(&driver_pulseaudio); ++#endif ++ ++#ifdef ALSA_ENABLED ++ AudioDriverManager::add_driver(&driver_alsa); ++#endif ++ ++ xi.opcode = 0; ++ xi.last_relative_time = 0; ++ layered_window = false; ++ minimized = false; ++ window_focused = true; ++ xim_style = 0L; ++ mouse_mode = MOUSE_MODE_VISIBLE; ++ last_position_before_fs = Vector2(); ++} +diff --git a/platform/x11es/os_x11.h b/platform/x11es/os_x11.h +new file mode 100644 +index 0000000000..744023b964 +--- /dev/null ++++ b/platform/x11es/os_x11.h +@@ -0,0 +1,398 @@ ++/**************************************************************************/ ++/* os_x11.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifndef OS_X11_H ++#define OS_X11_H ++ ++#include "context_gl_x11.h" ++#include "core/local_vector.h" ++#include "core/os/input.h" ++#include "crash_handler_x11.h" ++#include "drivers/alsa/audio_driver_alsa.h" ++#include "drivers/alsamidi/midi_driver_alsamidi.h" ++#include "drivers/pulseaudio/audio_driver_pulseaudio.h" ++#include "drivers/unix/os_unix.h" ++#include "joypad_linux.h" ++#include "main/input_default.h" ++#include "power_x11.h" ++#include "servers/audio_server.h" ++#include "servers/visual/rasterizer.h" ++#include "servers/visual_server.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if defined(SPEECHD_ENABLED) ++#include "tts_linux.h" ++#endif ++ ++// Hints for X11 fullscreen ++typedef struct { ++ unsigned long flags; ++ unsigned long functions; ++ unsigned long decorations; ++ long inputMode; ++ unsigned long status; ++} Hints; ++ ++typedef struct _xrr_monitor_info { ++ Atom name; ++ Bool primary; ++ Bool automatic; ++ int noutput; ++ int x; ++ int y; ++ int width; ++ int height; ++ int mwidth; ++ int mheight; ++ RROutput *outputs; ++} xrr_monitor_info; ++ ++#undef CursorShape ++ ++class OS_X11 : public OS_Unix { ++ Atom wm_delete; ++ Atom xdnd_enter; ++ Atom xdnd_position; ++ Atom xdnd_status; ++ Atom xdnd_action_copy; ++ Atom xdnd_drop; ++ Atom xdnd_finished; ++ Atom xdnd_selection; ++ Atom requested; ++ ++ int xdnd_version; ++ ++#if defined(OPENGL_ENABLED) || defined(GLES_ENABLED) ++ ContextGL_X11 *context_gl; ++#endif ++ //Rasterizer *rasterizer; ++ VisualServer *visual_server; ++ VideoMode current_videomode; ++ List args; ++ Window x11_window; ++ Window xdnd_source_window; ++ MainLoop *main_loop; ++ ::Display *x11_display; ++ char *xmbstring; ++ int xmblen; ++ unsigned long last_timestamp; ++ ::Time last_keyrelease_time; ++ ::XIC xic; ++ ::XIM xim; ++ ::XIMStyle xim_style; ++ static void xim_destroy_callback(::XIM im, ::XPointer client_data, ++ ::XPointer call_data); ++ ++ // IME ++ bool im_active; ++ Vector2 im_position; ++ Vector2 last_position_before_fs; ++ ++ Size2 min_size; ++ Size2 max_size; ++ ++ Point2 last_mouse_pos; ++ bool last_mouse_pos_valid; ++ Point2i last_click_pos; ++ uint64_t last_click_ms; ++ int last_click_button_index; ++ uint32_t last_button_state; ++ ++ struct { ++ int opcode; ++ Vector touch_devices; ++ Map absolute_devices; ++ Map pen_pressure_range; ++ Map pen_tilt_x_range; ++ Map pen_tilt_y_range; ++ Map pen_inverted_devices; ++ XIEventMask all_event_mask; ++ XIEventMask all_master_event_mask; ++ Map state; ++ double pressure; ++ bool pressure_supported; ++ bool pen_inverted; ++ Vector2 tilt; ++ Vector2 mouse_pos_to_filter; ++ Vector2 relative_motion; ++ Vector2 raw_pos; ++ Vector2 old_raw_pos; ++ ::Time last_relative_time; ++ } xi; ++ ++ bool refresh_device_info(); ++ ++ unsigned int get_mouse_button_state(unsigned int p_x11_button, int p_x11_type); ++ void get_key_modifier_state(unsigned int p_x11_state, Ref state); ++ void flush_mouse_motion(); ++ ++ MouseMode mouse_mode; ++ Point2i center; ++ ++ void _handle_key_event(XKeyEvent *p_event, LocalVector &p_events, uint32_t &p_event_index, bool p_echo = false); ++ ++ Atom _process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property, Atom p_selection) const; ++ void _handle_selection_request_event(XSelectionRequestEvent *p_event) const; ++ ++ String _get_clipboard_impl(Atom p_source, Window x11_window, Atom target) const; ++ String _get_clipboard(Atom p_source, Window x11_window) const; ++ void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const; ++ ++ mutable Mutex events_mutex; ++ Thread events_thread; ++ bool events_thread_done = false; ++ LocalVector polled_events; ++ static void _poll_events_thread(void *ud); ++ bool _wait_for_events() const; ++ void _poll_events(); ++ void _check_pending_events(LocalVector &r_events); ++ ++ static Bool _predicate_all_events(Display *display, XEvent *event, XPointer arg); ++ static Bool _predicate_clipboard_selection(Display *display, XEvent *event, XPointer arg); ++ static Bool _predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg); ++ static Bool _predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg); ++ ++ void process_xevents(); ++ virtual void delete_main_loop(); ++ ++ bool force_quit; ++ bool minimized; ++ bool window_has_focus; ++ bool do_mouse_warp; ++ ++ const char *cursor_theme; ++ int cursor_size; ++ XcursorImage *img[CURSOR_MAX]; ++ Cursor cursors[CURSOR_MAX]; ++ Cursor null_cursor; ++ CursorShape current_cursor; ++ Map> cursors_cache; ++ ++ InputDefault *input; ++ ++#ifdef JOYDEV_ENABLED ++ JoypadLinux *joypad; ++#endif ++ ++#ifdef ALSA_ENABLED ++ AudioDriverALSA driver_alsa; ++#endif ++ ++#ifdef ALSAMIDI_ENABLED ++ MIDIDriverALSAMidi driver_alsamidi; ++#endif ++ ++#ifdef PULSEAUDIO_ENABLED ++ AudioDriverPulseAudio driver_pulseaudio; ++#endif ++ ++#ifdef SPEECHD_ENABLED ++ TTS_Linux *tts = nullptr; ++#endif ++ ++ PowerX11 *power_manager; ++ ++ bool layered_window; ++ ++ CrashHandler crash_handler; ++ ++ int video_driver_index; ++ bool maximized; ++ bool window_focused; ++ //void set_wm_border(bool p_enabled); ++ void set_wm_fullscreen(bool p_enabled); ++ void set_wm_above(bool p_enabled); ++ ++ typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors); ++ typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors); ++ xrr_get_monitors_t xrr_get_monitors; ++ xrr_free_monitors_t xrr_free_monitors; ++ void *xrandr_handle; ++ Bool xrandr_ext_ok; ++ ++protected: ++ virtual int get_current_video_driver() const; ++ ++ virtual void initialize_core(); ++ virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); ++ virtual void finalize(); ++ ++ virtual bool is_offscreen_gl_available() const; ++ virtual void set_offscreen_gl_current(bool p_current); ++ ++ virtual void set_main_loop(MainLoop *p_main_loop); ++ ++ void _window_changed(XEvent *event); ++ ++ bool window_maximize_check(const char *p_atom_name) const; ++ bool window_fullscreen_check() const; ++ bool is_window_maximize_allowed() const; ++ ++public: ++ virtual String get_name() const; ++ ++#ifdef SPEECHD_ENABLED ++ virtual bool tts_is_speaking() const; ++ virtual bool tts_is_paused() const; ++ virtual Array tts_get_voices() const; ++ ++ virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false); ++ virtual void tts_pause(); ++ virtual void tts_resume(); ++ virtual void tts_stop(); ++#endif ++ ++ virtual void set_cursor_shape(CursorShape p_shape); ++ virtual CursorShape get_cursor_shape() const; ++ virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); ++ ++ void set_mouse_mode(MouseMode p_mode); ++ MouseMode get_mouse_mode() const; ++ ++ virtual void warp_mouse_position(const Point2 &p_to); ++ virtual Point2 get_mouse_position() const; ++ virtual int get_mouse_button_state() const; ++ virtual void set_window_title(const String &p_title); ++ virtual void set_window_mouse_passthrough(const PoolVector2Array &p_region); ++ ++ virtual void set_icon(const Ref &p_icon); ++ ++ virtual MainLoop *get_main_loop() const; ++ ++ virtual uint64_t get_embedded_pck_offset() const; ++ ++ virtual bool can_draw() const; ++ ++ virtual void set_clipboard(const String &p_text); ++ virtual String get_clipboard() const; ++ virtual void set_clipboard_primary(const String &p_text); ++ virtual String get_clipboard_primary() const; ++ ++ virtual void release_rendering_thread(); ++ virtual void make_rendering_thread(); ++ virtual void swap_buffers(); ++ ++ virtual String get_config_path() const; ++ virtual String get_data_path() const; ++ virtual String get_cache_path() const; ++ ++ virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const; ++ ++ virtual Error shell_open(String p_uri); ++ ++ virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); ++ virtual VideoMode get_video_mode(int p_screen = 0) const; ++ virtual void get_fullscreen_mode_list(List *p_list, int p_screen = 0) const; ++ ++ virtual int get_screen_count() const; ++ virtual int get_current_screen() const; ++ virtual void set_current_screen(int p_screen); ++ virtual Point2 get_screen_position(int p_screen = -1) const; ++ virtual Size2 get_screen_size(int p_screen = -1) const; ++ virtual int get_screen_dpi(int p_screen = -1) const; ++ virtual float get_screen_refresh_rate(int p_screen = -1) const; ++ virtual Point2 get_window_position() const; ++ virtual void set_window_position(const Point2 &p_position); ++ virtual Size2 get_window_size() const; ++ virtual Size2 get_real_window_size() const; ++ virtual Size2 get_max_window_size() const; ++ virtual Size2 get_min_window_size() const; ++ virtual void set_min_window_size(const Size2 p_size); ++ virtual void set_max_window_size(const Size2 p_size); ++ virtual void set_window_size(const Size2 p_size); ++ virtual void set_window_fullscreen(bool p_enabled); ++ virtual bool is_window_fullscreen() const; ++ virtual void set_window_resizable(bool p_enabled); ++ virtual bool is_window_resizable() const; ++ virtual void set_window_minimized(bool p_enabled); ++ virtual bool is_window_minimized() const; ++ virtual void set_window_maximized(bool p_enabled); ++ virtual bool is_window_maximized() const; ++ virtual void set_window_always_on_top(bool p_enabled); ++ virtual bool is_window_always_on_top() const; ++ virtual bool is_window_focused() const; ++ virtual void request_attention(); ++ virtual void *get_native_handle(int p_handle_type); ++ ++ virtual void set_borderless_window(bool p_borderless); ++ virtual bool get_borderless_window(); ++ ++ virtual bool get_window_per_pixel_transparency_enabled() const; ++ virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); ++ ++ virtual void set_ime_active(const bool p_active); ++ virtual void set_ime_position(const Point2 &p_pos); ++ ++ virtual String get_unique_id() const; ++ virtual String get_processor_name() const; ++ ++ virtual void move_window_to_foreground(); ++ virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); ++ ++ virtual bool is_joy_known(int p_device); ++ virtual String get_joy_guid(int p_device) const; ++ ++ virtual void set_context(int p_context); ++ ++ virtual void _set_use_vsync(bool p_enable); ++ //virtual bool is_vsync_enabled() const; ++ ++ virtual OS::PowerState get_power_state(); ++ virtual int get_power_seconds_left(); ++ virtual int get_power_percent_left(); ++ ++ virtual bool _check_internal_feature_support(const String &p_feature); ++ ++ virtual void force_process_input(); ++ void run(); ++ ++ void disable_crash_handler(); ++ bool is_disable_crash_handler() const; ++ ++ virtual Error move_to_trash(const String &p_path); ++ ++ virtual LatinKeyboardVariant get_latin_keyboard_variant() const; ++ virtual int keyboard_get_layout_count() const; ++ virtual int keyboard_get_current_layout() const; ++ virtual void keyboard_set_current_layout(int p_index); ++ virtual String keyboard_get_layout_language(int p_index) const; ++ virtual String keyboard_get_layout_name(int p_index) const; ++ virtual uint32_t keyboard_get_scancode_from_physical(uint32_t p_scancode) const; ++ ++ void update_real_mouse_position(); ++ OS_X11(); ++}; ++ ++#endif // OS_X11_H +diff --git a/platform/x11es/platform_config.h b/platform/x11es/platform_config.h +new file mode 100644 +index 0000000000..c6f41ae36d +--- /dev/null ++++ b/platform/x11es/platform_config.h +@@ -0,0 +1,49 @@ ++/**************************************************************************/ ++/* platform_config.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifdef __linux__ ++#include ++#endif ++ ++#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) ++#include // alloca ++// FreeBSD and OpenBSD use pthread_set_name_np, while other platforms, ++// include NetBSD, use pthread_setname_np. NetBSD's version however requires ++// a different format, we handle this directly in thread_posix. ++#ifdef __NetBSD__ ++#define PTHREAD_NETBSD_SET_NAME ++#else ++#define PTHREAD_BSD_SET_NAME ++#endif ++#endif ++ ++ ++// #define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" ++// #define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" +diff --git a/platform/x11es/platform_x11_builders.py b/platform/x11es/platform_x11_builders.py +new file mode 100644 +index 0000000000..632bafce02 +--- /dev/null ++++ b/platform/x11es/platform_x11_builders.py +@@ -0,0 +1,18 @@ ++"""Functions used to generate source files during build time ++ ++All such functions are invoked in a subprocess on Windows to prevent build flakiness. ++ ++""" ++ ++import os ++from platform_methods import subprocess_main ++ ++ ++def make_debug_x11(target, source, env): ++ os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) ++ os.system("strip --strip-debug --strip-unneeded {0}".format(target[0])) ++ os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) ++ ++ ++if __name__ == "__main__": ++ subprocess_main(globals()) +diff --git a/platform/x11es/power_x11.cpp b/platform/x11es/power_x11.cpp +new file mode 100644 +index 0000000000..0513c37d47 +--- /dev/null ++++ b/platform/x11es/power_x11.cpp +@@ -0,0 +1,571 @@ ++/**************************************************************************/ ++/* power_x11.cpp */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++/* ++Adapted from corresponding SDL 2.0 code. ++*/ ++ ++/* ++ * Simple DirectMedia Layer ++ * Copyright (C) 1997-2017 Sam Lantinga ++ * ++ * 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 "power_x11.h" ++ ++#include ++#include ++ ++#include "core/error_macros.h" ++#include ++#include ++#include ++#include ++ ++// CODE CHUNK IMPORTED FROM SDL 2.0 ++ ++static const char *proc_apm_path = "/proc/apm"; ++static const char *proc_acpi_battery_path = "/proc/acpi/battery"; ++static const char *proc_acpi_ac_adapter_path = "/proc/acpi/ac_adapter"; ++static const char *sys_class_power_supply_path = "/sys/class/power_supply"; ++ ++FileAccessRef PowerX11::open_power_file(const char *base, const char *node, const char *key) { ++ String path = String(base) + String("/") + String(node) + String("/") + String(key); ++ FileAccessRef f = FileAccess::open(path, FileAccess::READ); ++ return f; ++} ++ ++bool PowerX11::read_power_file(const char *base, const char *node, const char *key, char *buf, size_t buflen) { ++ FileAccessRef fd = open_power_file(base, node, key); ++ if (!fd) { ++ return false; ++ } ++ uint64_t br = fd->get_buffer(reinterpret_cast(buf), buflen - 1); ++ fd->close(); ++ buf[br] = '\0'; // null-terminate the string ++ return true; ++} ++ ++bool PowerX11::make_proc_acpi_key_val(char **_ptr, char **_key, char **_val) { ++ char *ptr = *_ptr; ++ ++ while (*ptr == ' ') { ++ ptr++; /* skip whitespace. */ ++ } ++ ++ if (*ptr == '\0') { ++ return false; /* EOF. */ ++ } ++ ++ *_key = ptr; ++ ++ while ((*ptr != ':') && (*ptr != '\0')) { ++ ptr++; ++ } ++ ++ if (*ptr == '\0') { ++ return false; /* (unexpected) EOF. */ ++ } ++ ++ *(ptr++) = '\0'; /* terminate the key. */ ++ ++ while (*ptr == ' ') { ++ ptr++; /* skip whitespace. */ ++ } ++ ++ if (*ptr == '\0') { ++ return false; /* (unexpected) EOF. */ ++ } ++ ++ *_val = ptr; ++ ++ while ((*ptr != '\n') && (*ptr != '\0')) { ++ ptr++; ++ } ++ ++ if (*ptr != '\0') { ++ *(ptr++) = '\0'; /* terminate the value. */ ++ } ++ ++ *_ptr = ptr; /* store for next time. */ ++ return true; ++} ++ ++void PowerX11::check_proc_acpi_battery(const char *node, bool *have_battery, bool *charging) { ++ const char *base = proc_acpi_battery_path; ++ char info[1024]; ++ char state[1024]; ++ char *ptr = nullptr; ++ char *key = nullptr; ++ char *val = nullptr; ++ bool charge = false; ++ bool choose = false; ++ int maximum = -1; ++ int remaining = -1; ++ int secs = -1; ++ int pct = -1; ++ ++ if (!read_power_file(base, node, "state", state, sizeof(state))) { ++ return; ++ } else { ++ if (!read_power_file(base, node, "info", info, sizeof(info))) { ++ return; ++ } ++ } ++ ++ ptr = &state[0]; ++ while (make_proc_acpi_key_val(&ptr, &key, &val)) { ++ if (String(key) == "present") { ++ if (String(val) == "yes") { ++ *have_battery = true; ++ } ++ } else if (String(key) == "charging state") { ++ /* !!! FIXME: what exactly _does_ charging/discharging mean? */ ++ if (String(val) == "charging/discharging") { ++ charge = true; ++ } else if (String(val) == "charging") { ++ charge = true; ++ } ++ } else if (String(key) == "remaining capacity") { ++ String sval = val; ++ const int cvt = sval.to_int(); ++ remaining = cvt; ++ } ++ } ++ ++ ptr = &info[0]; ++ while (make_proc_acpi_key_val(&ptr, &key, &val)) { ++ if (String(key) == "design capacity") { ++ String sval = val; ++ const int cvt = sval.to_int(); ++ maximum = cvt; ++ } ++ } ++ ++ if ((maximum >= 0) && (remaining >= 0)) { ++ pct = (int)((((float)remaining) / ((float)maximum)) * 100.0f); ++ if (pct < 0) { ++ pct = 0; ++ } else if (pct > 100) { ++ pct = 100; ++ } ++ } ++ ++ /* !!! FIXME: calculate (secs). */ ++ ++ /* ++ * We pick the battery that claims to have the most minutes left. ++ * (failing a report of minutes, we'll take the highest percent.) ++ */ ++ // -- GODOT start -- ++ //if ((secs < 0) && (this->nsecs_left < 0)) { ++ if (this->nsecs_left < 0) { ++ // -- GODOT end -- ++ if ((pct < 0) && (this->percent_left < 0)) { ++ choose = true; /* at least we know there's a battery. */ ++ } ++ if (pct > this->percent_left) { ++ choose = true; ++ } ++ } else if (secs > this->nsecs_left) { ++ choose = true; ++ } ++ ++ if (choose) { ++ this->nsecs_left = secs; ++ this->percent_left = pct; ++ *charging = charge; ++ } ++} ++ ++void PowerX11::check_proc_acpi_ac_adapter(const char *node, bool *have_ac) { ++ const char *base = proc_acpi_ac_adapter_path; ++ char state[256]; ++ char *ptr = nullptr; ++ char *key = nullptr; ++ char *val = nullptr; ++ ++ if (!read_power_file(base, node, "state", state, sizeof(state))) { ++ return; ++ } ++ ++ ptr = &state[0]; ++ while (make_proc_acpi_key_val(&ptr, &key, &val)) { ++ String skey = key; ++ if (skey == "state") { ++ String sval = val; ++ if (sval == "on-line") { ++ *have_ac = true; ++ } ++ } ++ } ++} ++ ++bool PowerX11::GetPowerInfo_Linux_proc_acpi() { ++ String node; ++ DirAccess *dirp = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ++ bool have_battery = false; ++ bool have_ac = false; ++ bool charging = false; ++ ++ this->nsecs_left = -1; ++ this->percent_left = -1; ++ this->power_state = OS::POWERSTATE_UNKNOWN; ++ ++ dirp->change_dir(proc_acpi_battery_path); ++ Error err = dirp->list_dir_begin(); ++ ++ if (err != OK) { ++ return false; /* can't use this interface. */ ++ } else { ++ node = dirp->get_next(); ++ while (node != "") { ++ check_proc_acpi_battery(node.utf8().get_data(), &have_battery, &charging /*, seconds, percent*/); ++ node = dirp->get_next(); ++ } ++ } ++ dirp->change_dir(proc_acpi_ac_adapter_path); ++ err = dirp->list_dir_begin(); ++ if (err != OK) { ++ return false; /* can't use this interface. */ ++ } else { ++ node = dirp->get_next(); ++ while (node != "") { ++ check_proc_acpi_ac_adapter(node.utf8().get_data(), &have_ac); ++ node = dirp->get_next(); ++ } ++ } ++ ++ if (!have_battery) { ++ this->power_state = OS::POWERSTATE_NO_BATTERY; ++ } else if (charging) { ++ this->power_state = OS::POWERSTATE_CHARGING; ++ } else if (have_ac) { ++ this->power_state = OS::POWERSTATE_CHARGED; ++ } else { ++ this->power_state = OS::POWERSTATE_ON_BATTERY; ++ } ++ ++ memdelete(dirp); ++ return true; /* definitive answer. */ ++} ++ ++bool PowerX11::next_string(char **_ptr, char **_str) { ++ char *ptr = *_ptr; ++ char *str = *_str; ++ ++ while (*ptr == ' ') { /* skip any spaces... */ ++ ptr++; ++ } ++ ++ if (*ptr == '\0') { ++ return false; ++ } ++ ++ str = ptr; ++ while ((*ptr != ' ') && (*ptr != '\n') && (*ptr != '\0')) { ++ ptr++; ++ } ++ ++ if (*ptr != '\0') { ++ *(ptr++) = '\0'; ++ } ++ ++ *_str = str; ++ *_ptr = ptr; ++ return true; ++} ++ ++bool PowerX11::int_string(char *str, int *val) { ++ String sval = str; ++ *val = sval.to_int(); ++ return (*str != '\0'); ++} ++ ++/* http://lxr.linux.no/linux+v2.6.29/drivers/char/apm-emulation.c */ ++bool PowerX11::GetPowerInfo_Linux_proc_apm() { ++ bool need_details = false; ++ int ac_status = 0; ++ int battery_status = 0; ++ int battery_flag = 0; ++ int battery_percent = 0; ++ int battery_time = 0; ++ FileAccessRef fd = FileAccess::open(proc_apm_path, FileAccess::READ); ++ char buf[128]; ++ char *ptr = &buf[0]; ++ char *str = nullptr; ++ ++ if (!fd) { ++ return false; /* can't use this interface. */ ++ } ++ ++ uint64_t br = fd->get_buffer(reinterpret_cast(buf), sizeof(buf) - 1); ++ fd->close(); ++ ++ buf[br] = '\0'; /* null-terminate the string. */ ++ if (!next_string(&ptr, &str)) { /* driver version */ ++ return false; ++ } ++ if (!next_string(&ptr, &str)) { /* BIOS version */ ++ return false; ++ } ++ if (!next_string(&ptr, &str)) { /* APM flags */ ++ return false; ++ } ++ ++ if (!next_string(&ptr, &str)) { /* AC line status */ ++ return false; ++ } else if (!int_string(str, &ac_status)) { ++ return false; ++ } ++ ++ if (!next_string(&ptr, &str)) { /* battery status */ ++ return false; ++ } else if (!int_string(str, &battery_status)) { ++ return false; ++ } ++ if (!next_string(&ptr, &str)) { /* battery flag */ ++ return false; ++ } else if (!int_string(str, &battery_flag)) { ++ return false; ++ } ++ if (!next_string(&ptr, &str)) { /* remaining battery life percent */ ++ return false; ++ } ++ String sstr = str; ++ if (sstr[sstr.length() - 1] == '%') { ++ sstr[sstr.length() - 1] = '\0'; ++ } ++ if (!int_string(str, &battery_percent)) { ++ return false; ++ } ++ ++ if (!next_string(&ptr, &str)) { /* remaining battery life time */ ++ return false; ++ } else if (!int_string(str, &battery_time)) { ++ return false; ++ } ++ ++ if (!next_string(&ptr, &str)) { /* remaining battery life time units */ ++ return false; ++ } else if (String(str) == "min") { ++ battery_time *= 60; ++ } ++ ++ if (battery_flag == 0xFF) { /* unknown state */ ++ this->power_state = OS::POWERSTATE_UNKNOWN; ++ } else if (battery_flag & (1 << 7)) { /* no battery */ ++ this->power_state = OS::POWERSTATE_NO_BATTERY; ++ } else if (battery_flag & (1 << 3)) { /* charging */ ++ this->power_state = OS::POWERSTATE_CHARGING; ++ need_details = true; ++ } else if (ac_status == 1) { ++ this->power_state = OS::POWERSTATE_CHARGED; /* on AC, not charging. */ ++ need_details = true; ++ } else { ++ this->power_state = OS::POWERSTATE_ON_BATTERY; ++ need_details = true; ++ } ++ ++ this->percent_left = -1; ++ this->nsecs_left = -1; ++ if (need_details) { ++ const int pct = battery_percent; ++ const int secs = battery_time; ++ ++ if (pct >= 0) { /* -1 == unknown */ ++ this->percent_left = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ ++ } ++ if (secs >= 0) { /* -1 == unknown */ ++ this->nsecs_left = secs; ++ } ++ } ++ ++ return true; ++} ++ ++/* !!! FIXME: implement d-bus queries to org.freedesktop.UPower. */ ++ ++bool PowerX11::GetPowerInfo_Linux_sys_class_power_supply(/*PowerState *state, int *seconds, int *percent*/) { ++ const char *base = sys_class_power_supply_path; ++ String name; ++ ++ DirAccess *dirp = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); ++ dirp->change_dir(base); ++ Error err = dirp->list_dir_begin(); ++ ++ if (err != OK) { ++ return false; ++ } ++ ++ this->power_state = OS::POWERSTATE_NO_BATTERY; /* assume we're just plugged in. */ ++ this->nsecs_left = -1; ++ this->percent_left = -1; ++ ++ name = dirp->get_next(); ++ ++ while (name != "") { ++ bool choose = false; ++ char str[64]; ++ OS::PowerState st; ++ int secs; ++ int pct; ++ ++ if ((name == ".") || (name == "..")) { ++ name = dirp->get_next(); ++ continue; //skip these, of course. ++ } else { ++ if (!read_power_file(base, name.utf8().get_data(), "type", str, sizeof(str))) { ++ name = dirp->get_next(); ++ continue; // Don't know _what_ we're looking at. Give up on it. ++ } else { ++ if (String(str) != "Battery\n") { ++ name = dirp->get_next(); ++ continue; // we don't care about UPS and such. ++ } ++ } ++ } ++ ++ /* some drivers don't offer this, so if it's not explicitly reported assume it's present. */ ++ if (read_power_file(base, name.utf8().get_data(), "present", str, sizeof(str)) && (String(str) == "0\n")) { ++ st = OS::POWERSTATE_NO_BATTERY; ++ } else if (!read_power_file(base, name.utf8().get_data(), "status", str, sizeof(str))) { ++ st = OS::POWERSTATE_UNKNOWN; /* uh oh */ ++ } else if (String(str) == "Charging\n") { ++ st = OS::POWERSTATE_CHARGING; ++ } else if (String(str) == "Discharging\n") { ++ st = OS::POWERSTATE_ON_BATTERY; ++ } else if ((String(str) == "Full\n") || (String(str) == "Not charging\n")) { ++ st = OS::POWERSTATE_CHARGED; ++ } else { ++ st = OS::POWERSTATE_UNKNOWN; /* uh oh */ ++ } ++ ++ if (!read_power_file(base, name.utf8().get_data(), "capacity", str, sizeof(str))) { ++ pct = -1; ++ } else { ++ pct = String(str).to_int(); ++ pct = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ ++ } ++ ++ if (!read_power_file(base, name.utf8().get_data(), "time_to_empty_now", str, sizeof(str))) { ++ secs = -1; ++ } else { ++ secs = String(str).to_int(); ++ secs = (secs <= 0) ? -1 : secs; /* 0 == unknown */ ++ } ++ ++ /* ++ * We pick the battery that claims to have the most minutes left. ++ * (failing a report of minutes, we'll take the highest percent.) ++ */ ++ if ((secs < 0) && (this->nsecs_left < 0)) { ++ if ((pct < 0) && (this->percent_left < 0)) { ++ choose = true; /* at least we know there's a battery. */ ++ } else if (pct > this->percent_left) { ++ choose = true; ++ } ++ } else if (secs > this->nsecs_left) { ++ choose = true; ++ } ++ ++ if (choose) { ++ this->nsecs_left = secs; ++ this->percent_left = pct; ++ this->power_state = st; ++ } ++ ++ name = dirp->get_next(); ++ } ++ ++ memdelete(dirp); ++ return true; /* don't look any further*/ ++} ++ ++bool PowerX11::UpdatePowerInfo() { ++ if (GetPowerInfo_Linux_sys_class_power_supply()) { // try method 1 ++ return true; ++ } ++ if (GetPowerInfo_Linux_proc_acpi()) { // try further ++ return true; ++ } ++ if (GetPowerInfo_Linux_proc_apm()) { // try even further ++ return true; ++ } ++ return false; ++} ++ ++PowerX11::PowerX11() : ++ nsecs_left(-1), ++ percent_left(-1), ++ power_state(OS::POWERSTATE_UNKNOWN) { ++} ++ ++PowerX11::~PowerX11() { ++} ++ ++OS::PowerState PowerX11::get_power_state() { ++ if (UpdatePowerInfo()) { ++ return power_state; ++ } else { ++ return OS::POWERSTATE_UNKNOWN; ++ } ++} ++ ++int PowerX11::get_power_seconds_left() { ++ if (UpdatePowerInfo()) { ++ return nsecs_left; ++ } else { ++ return -1; ++ } ++} ++ ++int PowerX11::get_power_percent_left() { ++ if (UpdatePowerInfo()) { ++ return percent_left; ++ } else { ++ return -1; ++ } ++} +diff --git a/platform/x11es/power_x11.h b/platform/x11es/power_x11.h +new file mode 100644 +index 0000000000..aa820112d6 +--- /dev/null ++++ b/platform/x11es/power_x11.h +@@ -0,0 +1,65 @@ ++/**************************************************************************/ ++/* power_x11.h */ ++/**************************************************************************/ ++/* This file is part of: */ ++/* GODOT ENGINE */ ++/* https://godotengine.org */ ++/**************************************************************************/ ++/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ ++/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ ++/* */ ++/* 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 the rights to use, copy, modify, merge, publish, */ ++/* distribute, sublicense, 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 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 NONINFRINGEMENT. */ ++/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 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. */ ++/**************************************************************************/ ++ ++#ifndef POWER_X11_H ++#define POWER_X11_H ++ ++#include "core/os/dir_access.h" ++#include "core/os/file_access.h" ++#include "core/os/os.h" ++ ++class PowerX11 { ++private: ++ int nsecs_left; ++ int percent_left; ++ OS::PowerState power_state; ++ ++ FileAccessRef open_power_file(const char *base, const char *node, const char *key); ++ bool read_power_file(const char *base, const char *node, const char *key, char *buf, size_t buflen); ++ bool make_proc_acpi_key_val(char **_ptr, char **_key, char **_val); ++ void check_proc_acpi_battery(const char *node, bool *have_battery, bool *charging); ++ void check_proc_acpi_ac_adapter(const char *node, bool *have_ac); ++ bool GetPowerInfo_Linux_proc_acpi(); ++ bool next_string(char **_ptr, char **_str); ++ bool int_string(char *str, int *val); ++ bool GetPowerInfo_Linux_proc_apm(); ++ bool GetPowerInfo_Linux_sys_class_power_supply(); ++ bool UpdatePowerInfo(); ++ ++public: ++ PowerX11(); ++ virtual ~PowerX11(); ++ ++ OS::PowerState get_power_state(); ++ int get_power_seconds_left(); ++ int get_power_percent_left(); ++}; ++ ++#endif // POWER_X11_H +-- +2.34.1 + diff --git a/src/patches/0005-tmp-do-not-use-GL_PIXEL_PACK_BUFFER.patch b/src/patches/0005-tmp-do-not-use-GL_PIXEL_PACK_BUFFER.patch deleted file mode 100644 index 4aeab68..0000000 --- a/src/patches/0005-tmp-do-not-use-GL_PIXEL_PACK_BUFFER.patch +++ /dev/null @@ -1,132 +0,0 @@ -From c6782c2ba842c47b6e35299b31d35663645647c7 Mon Sep 17 00:00:00 2001 -From: Sergey Lapin -Date: Thu, 20 Mar 2025 12:33:50 +0300 -Subject: [PATCH] tmp: do not use GL_PIXEL_PACK_BUFFER - ---- - drivers/gles2/rasterizer_storage_gles2.cpp | 26 +++++++------------- - drivers/gles3/rasterizer_storage_gles3.cpp | 28 ++++++++-------------- - 2 files changed, 19 insertions(+), 35 deletions(-) - -diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp -index 5caae7cf2c..a649925d75 100644 ---- a/drivers/gles2/rasterizer_storage_gles2.cpp -+++ b/drivers/gles2/rasterizer_storage_gles2.cpp -@@ -805,22 +805,29 @@ Ref RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) - return texture->images[p_layer]; - } - --#ifdef GLES_OVER_GL -- - Image::Format real_format; - GLenum gl_format; - GLenum gl_internal_format; - GLenum gl_type; - bool compressed; -+#ifdef GLES_OVER_GL - _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, false); -+#else -+ _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, texture->resize_to_po2); -+#endif - - PoolVector data; - -+#ifdef GLES_OVER_GL - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); -+#else -+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); -+#endif - - data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers - PoolVector::Write wb = data.write(); - -+#if 0 - gl_wrapper.gl_active_texture(GL_TEXTURE0); - - glBindTexture(texture->target, texture->tex_id); -@@ -847,21 +854,6 @@ Ref RasterizerStorageGLES2::texture_get_data(RID p_texture, int p_layer) - - return Ref(img); - #else -- -- Image::Format real_format; -- GLenum gl_format; -- GLenum gl_internal_format; -- GLenum gl_type; -- bool compressed; -- _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, texture->resize_to_po2); -- -- PoolVector data; -- -- int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); -- -- data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers -- PoolVector::Write wb = data.write(); -- - GLuint temp_framebuffer; - glGenFramebuffers(1, &temp_framebuffer); - -diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp -index 6fdd8c466d..42ca35746f 100644 ---- a/drivers/gles3/rasterizer_storage_gles3.cpp -+++ b/drivers/gles3/rasterizer_storage_gles3.cpp -@@ -1165,23 +1165,31 @@ Ref RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) - return Ref(img); - } - --#ifdef GLES_OVER_GL -- - Image::Format real_format; - GLenum gl_format; - GLenum gl_internal_format; - GLenum gl_type; - bool compressed; - bool srgb; -+#ifdef GLES_OVER_GL -+ - _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb, false); -+#else -+ _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb, texture->is_npot_repeat_mipmap); -+#endif - - PoolVector data; - -+#ifdef GLES_OVER_GL - int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, real_format, texture->mipmaps > 1); -+#else -+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); -+#endif - - data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers - PoolVector::Write wb = data.write(); - -+#if 0 - gl_wrapper.gl_active_texture(GL_TEXTURE0); - - glBindTexture(texture->target, texture->tex_id); -@@ -1232,22 +1240,6 @@ Ref RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer) - - return Ref(img); - #else -- -- Image::Format real_format; -- GLenum gl_format; -- GLenum gl_internal_format; -- GLenum gl_type; -- bool compressed; -- bool srgb; -- _get_gl_image_and_format(Ref(), texture->format, texture->flags, real_format, gl_format, gl_internal_format, gl_type, compressed, srgb, texture->is_npot_repeat_mipmap); -- -- PoolVector data; -- -- int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); -- -- data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers -- PoolVector::Write wb = data.write(); -- - GLuint temp_framebuffer; - glGenFramebuffers(1, &temp_framebuffer); - --- -2.34.1 -