2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* os_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* 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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "os_x11.h"
2019-01-27 23:31:38 +01:00
# include "detect_prime.h"
2018-09-11 18:13:45 +02:00
# include "core/os/dir_access.h"
# include "core/print_string.h"
2019-06-07 18:07:57 +02:00
//#include "drivers/gles2/rasterizer_gles2.h"
//#include "drivers/gles3/rasterizer_gles3.h"
# include "drivers/dummy/rasterizer_dummy.h"
2017-03-05 16:44:50 +01:00
# include "errno.h"
2014-02-10 02:10:30 +01:00
# include "key_mapping_x11.h"
2017-03-05 16:44:50 +01:00
# include "servers/visual/visual_server_raster.h"
2017-06-09 05:23:50 +02:00
# include "servers/visual/visual_server_wrap_mt.h"
2017-10-17 18:50:41 +02:00
# ifdef HAVE_MNTENT
2017-09-25 15:15:11 +02:00
# include <mntent.h>
2017-10-17 18:50:41 +02:00
# endif
2014-02-10 02:10:30 +01:00
# include <stdio.h>
# include <stdlib.h>
2014-11-19 22:16:00 +01:00
# include <string.h>
2015-05-26 06:05:08 +02:00
2014-02-10 02:10:30 +01:00
# include "X11/Xutil.h"
2015-03-22 23:00:50 +01:00
2015-01-16 16:18:45 +01:00
# include "X11/Xatom.h"
2015-01-13 14:01:24 +01:00
# include "X11/extensions/Xinerama.h"
2015-01-16 16:18:45 +01:00
// ICCCM
2017-03-05 16:44:50 +01:00
# define WM_NormalState 1L // window normal state
# define WM_IconicState 3L // window minimized
2015-01-16 16:18:45 +01:00
// EWMH
2017-03-05 16:44:50 +01:00
# 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
2015-03-22 23:00:50 +01:00
2014-02-10 02:10:30 +01:00
# include "main/main.h"
2017-03-05 16:44:50 +01:00
# include <dlfcn.h>
2014-02-10 02:10:30 +01:00
# include <fcntl.h>
2017-03-05 16:44:50 +01:00
# include <sys/stat.h>
# include <sys/types.h>
2014-02-10 02:10:30 +01:00
# include <unistd.h>
2014-02-23 21:20:27 +01:00
2014-02-10 02:10:30 +01:00
//stupid linux.h
# ifdef KEY_TAB
# undef KEY_TAB
# endif
# include <X11/Xatom.h>
# undef CursorShape
2017-06-27 11:26:43 +02:00
# include <X11/XKBlib.h>
2018-08-18 00:59:26 +02:00
// 2.2 is the first release with multitouch
# define XINPUT_CLIENT_VERSION_MAJOR 2
# define XINPUT_CLIENT_VERSION_MINOR 2
2018-12-13 21:32:11 +01:00
# define VALUATOR_ABSX 0
# define VALUATOR_ABSY 1
# define VALUATOR_PRESSURE 2
# define VALUATOR_TILTX 3
# define VALUATOR_TILTY 4
2018-08-18 00:59:26 +02:00
static const double abs_resolution_mult = 10000.0 ;
static const double abs_resolution_range_mult = 10.0 ;
2017-09-08 03:01:49 +02:00
void OS_X11 : : initialize_core ( ) {
crash_handler . initialize ( ) ;
OS_Unix : : initialize_core ( ) ;
}
2018-07-19 23:58:15 +02:00
int OS_X11 : : get_current_video_driver ( ) const {
return video_driver_index ;
}
2019-06-07 18:07:57 +02:00
static RenderingDevice : : ID test_pipeline = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_index_array = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_vertex_array = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_uniform_set = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_framebuffer_pipeline = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_framebuffer_uniform_set = RenderingDevice : : INVALID_ID ;
static RenderingDevice : : ID test_framebuffer = RenderingDevice : : INVALID_ID ;
2018-01-03 18:26:44 +01:00
Error OS_X11 : : initialize ( const VideoMode & p_desired , int p_video_driver , int p_audio_driver ) {
2014-02-10 02:10:30 +01:00
2017-06-27 11:26:43 +02:00
long im_event_mask = 0 ;
2017-03-05 16:44:50 +01:00
last_button_state = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
xmbstring = NULL ;
x11_window = 0 ;
last_click_ms = 0 ;
2018-07-20 18:16:09 +02:00
last_click_button_index = - 1 ;
last_click_pos = Point2 ( - 100 , - 100 ) ;
2017-03-05 16:44:50 +01:00
args = OS : : get_singleton ( ) - > get_cmdline_args ( ) ;
current_videomode = p_desired ;
main_loop = NULL ;
last_timestamp = 0 ;
last_mouse_pos_valid = false ;
last_keyrelease_time = 0 ;
2016-05-28 21:35:42 +02:00
xdnd_version = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( get_render_thread_mode ( ) = = RENDER_SEPARATE_THREAD ) {
2014-02-10 02:10:30 +01:00
XInitThreads ( ) ;
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/** XLIB INITIALIZATION **/
x11_display = XOpenDisplay ( NULL ) ;
2016-03-09 00:00:52 +01:00
2018-01-03 18:26:44 +01:00
if ( ! x11_display ) {
ERR_PRINT ( " X11 Display is not available " ) ;
return ERR_UNAVAILABLE ;
}
2017-08-25 17:14:33 +02:00
char * modifiers = NULL ;
2017-06-27 11:26:43 +02:00
Bool xkb_dar = False ;
2018-01-03 18:26:44 +01:00
XAutoRepeatOn ( x11_display ) ;
xkb_dar = XkbSetDetectableAutoRepeat ( x11_display , True , NULL ) ;
2017-06-27 11:26:43 +02:00
2018-01-03 18:26:44 +01:00
// Try to support IME if detectable auto-repeat is supported
if ( xkb_dar = = True ) {
2017-06-27 11:26:43 +02:00
# ifdef X_HAVE_UTF8_STRING
2018-01-03 18:26:44 +01:00
// Xutf8LookupString will be used later instead of XmbLookupString before
// the multibyte sequences can be converted to unicode string.
modifiers = XSetLocaleModifiers ( " " ) ;
2017-06-27 11:26:43 +02:00
# endif
}
if ( modifiers = = NULL ) {
if ( is_stdout_verbose ( ) ) {
WARN_PRINT ( " IME is disabled " ) ;
}
2019-06-26 15:08:25 +02:00
XSetLocaleModifiers ( " @im=none " ) ;
2016-07-08 16:26:48 +02:00
WARN_PRINT ( " Error setting locale modifiers " ) ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
const char * err ;
2016-06-09 18:54:06 +02:00
xrr_get_monitors = NULL ;
2016-06-12 15:29:02 +02:00
xrr_free_monitors = NULL ;
int xrandr_major = 0 ;
int xrandr_minor = 0 ;
int event_base , error_base ;
2017-03-05 16:44:50 +01:00
xrandr_ext_ok = XRRQueryExtension ( x11_display , & event_base , & error_base ) ;
2016-08-11 17:25:13 +02:00
xrandr_handle = dlopen ( " libXrandr.so.2 " , RTLD_LAZY ) ;
2016-06-09 18:54:06 +02:00
if ( ! xrandr_handle ) {
2016-08-11 17:25:13 +02:00
err = dlerror ( ) ;
fprintf ( stderr , " could not load libXrandr.so.2, Error: %s \n " , err ) ;
2017-03-05 16:44:50 +01:00
} else {
2016-06-12 15:29:02 +02:00
XRRQueryVersion ( x11_display , & xrandr_major , & xrandr_minor ) ;
if ( ( ( xrandr_major < < 8 ) | xrandr_minor ) > = 0x0105 ) {
2017-03-05 16:44:50 +01:00
xrr_get_monitors = ( xrr_get_monitors_t ) dlsym ( xrandr_handle , " XRRGetMonitors " ) ;
2016-06-12 15:29:02 +02:00
if ( ! xrr_get_monitors ) {
err = dlerror ( ) ;
fprintf ( stderr , " could not find symbol XRRGetMonitors \n Error: %s \n " , err ) ;
2017-03-05 16:44:50 +01:00
} else {
xrr_free_monitors = ( xrr_free_monitors_t ) dlsym ( xrandr_handle , " XRRFreeMonitors " ) ;
2016-06-12 15:29:02 +02:00
if ( ! xrr_free_monitors ) {
err = dlerror ( ) ;
fprintf ( stderr , " could not find XRRFreeMonitors \n Error: %s \n " , err ) ;
xrr_get_monitors = NULL ;
}
}
}
2016-06-09 18:54:06 +02:00
}
2018-08-18 00:59:26 +02:00
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 ;
2017-11-29 21:01:26 +01:00
}
2017-03-05 16:44:50 +01:00
xim = XOpenIM ( x11_display , NULL , NULL , NULL ) ;
2014-02-10 02:10:30 +01:00
if ( xim = = NULL ) {
WARN_PRINT ( " XOpenIM failed " ) ;
2017-03-05 16:44:50 +01:00
xim_style = 0L ;
2014-02-10 02:10:30 +01:00
} else {
2017-06-27 11:26:43 +02:00
: : 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 ) ! = NULL ) {
WARN_PRINT ( " Error setting XIM destroy callback " ) ;
}
2017-03-05 16:44:50 +01:00
: : XIMStyles * xim_styles = NULL ;
xim_style = 0L ;
2017-08-21 21:15:36 +02:00
char * imvalret = XGetIMValues ( xim , XNQueryInputStyle , & xim_styles , NULL ) ;
2014-02-10 02:10:30 +01:00
if ( imvalret ! = NULL | | xim_styles = = NULL ) {
2017-03-05 16:44:50 +01:00
fprintf ( stderr , " Input method doesn't support any styles \n " ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( xim_styles ) {
2015-01-16 06:44:41 +01:00
xim_style = 0L ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < xim_styles - > count_styles ; i + + ) {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( xim_styles - > supported_styles [ i ] = =
2017-03-05 16:44:50 +01:00
( XIMPreeditNothing | XIMStatusNothing ) ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
xim_style = xim_styles - > supported_styles [ i ] ;
break ;
}
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
XFree ( xim_styles ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
XFree ( imvalret ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
/*
2014-02-10 02:10:30 +01:00
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 ;
} ;
*/
2017-03-05 16:44:50 +01:00
// maybe contextgl wants to be in charge of creating the window
2017-10-13 00:18:04 +02:00
# if defined(OPENGL_ENABLED)
2019-01-27 23:31:38 +01:00
if ( getenv ( " DRI_PRIME " ) = = NULL ) {
2019-02-14 16:25:37 +01:00
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 ;
}
if ( getenv ( " LD_LIBRARY_PATH " ) ) {
String ld_library_path ( getenv ( " LD_LIBRARY_PATH " ) ) ;
Vector < String > 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 ( ) ;
}
2019-01-27 23:31:38 +01:00
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 ) ;
2018-12-20 11:51:26 +01:00
}
}
2015-01-10 18:07:23 +01:00
2017-12-04 13:41:34 +01:00
ContextGL_X11 : : ContextType opengl_api_type = ContextGL_X11 : : GLES_3_0_COMPATIBLE ;
2014-02-10 02:10:30 +01:00
2018-03-01 22:17:34 +01:00
if ( p_video_driver = = VIDEO_DRIVER_GLES2 ) {
2017-12-04 13:41:34 +01:00
opengl_api_type = ContextGL_X11 : : GLES_2_0_COMPATIBLE ;
}
2018-08-25 00:04:25 +02:00
bool editor = Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
bool gl_initialization_error = false ;
context_gl = NULL ;
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 = NULL ;
2019-03-05 13:46:51 +01:00
if ( GLOBAL_GET ( " rendering/quality/driver/fallback_to_gles2 " ) | | editor ) {
2018-08-25 00:04:25 +02:00
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 {
2019-03-05 13:46:51 +01:00
if ( GLOBAL_GET ( " rendering/quality/driver/fallback_to_gles2 " ) | | editor ) {
2018-08-25 00:04:25 +02:00
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 ;
}
}
2017-12-04 13:41:34 +01:00
}
2014-02-10 02:10:30 +01:00
2018-08-25 00:04:25 +02:00
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. " ,
" Unable to initialize Video driver " ) ;
return ERR_UNAVAILABLE ;
}
video_driver_index = p_video_driver ;
2018-07-19 23:58:15 +02:00
2017-12-08 17:58:28 +01:00
context_gl - > set_use_vsync ( current_videomode . use_vsync ) ;
2019-06-07 18:07:57 +02:00
# else
long visualMask = VisualScreenMask ;
int numberOfVisuals ;
XVisualInfo vInfoTemplate = { } ;
vInfoTemplate . screen = DefaultScreen ( x11_display ) ;
XVisualInfo * visualInfo = XGetVisualInfo ( x11_display , visualMask , & vInfoTemplate , & numberOfVisuals ) ;
Colormap colormap = XCreateColormap ( x11_display , RootWindow ( x11_display , vInfoTemplate . screen ) , visualInfo - > visual , AllocNone ) ;
XSetWindowAttributes windowAttributes = { } ;
windowAttributes . colormap = colormap ;
windowAttributes . background_pixel = 0xFFFFFFFF ;
windowAttributes . border_pixel = 0 ;
windowAttributes . event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask ;
/*
window = XCreateWindow ( demo - > display , RootWindow ( display , vInfoTemplate . screen ) , 0 , 0 , demo - > width ,
demo - > height , 0 , visualInfo - > depth , InputOutput , visualInfo - > visual ,
CWBackPixel | CWBorderPixel | CWEventMask | CWColormap , & windowAttributes ) ;
*/
unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask ;
x11_window = XCreateWindow ( x11_display , RootWindow ( x11_display , visualInfo - > screen ) , 0 , 0 , OS : : get_singleton ( ) - > get_video_mode ( ) . width , OS : : get_singleton ( ) - > get_video_mode ( ) . height , 0 , visualInfo - > depth , InputOutput , visualInfo - > visual , valuemask , & windowAttributes ) ;
//set_class_hint(x11_display, x11_window);
XMapWindow ( x11_display , x11_window ) ;
XFlush ( x11_display ) ;
XSync ( x11_display , False ) ;
//XSetErrorHandler(oldHandler);
XFree ( visualInfo ) ;
context_vulkan = memnew ( VulkanContextX11 ( x11_window , x11_display ) ) ;
context_vulkan - > initialize ( OS : : get_singleton ( ) - > get_video_mode ( ) . width , OS : : get_singleton ( ) - > get_video_mode ( ) . height , false ) ;
//temporary
rendering_device = memnew ( RenderingDeviceVulkan ) ;
rendering_device - > initialize ( context_vulkan ) ;
RasterizerDummy : : make_current ( ) ;
// test shader
RenderingDevice : : ID shader ;
{
RenderingDevice : : ShaderStageSource vert ;
vert . shader_stage = RenderingDevice : : SHADER_STAGE_VERTEX ;
vert . shader_source = " #version 450 \n "
" layout(location = 0) in vec4 vertex_pos; \n "
" layout(location = 1) in vec2 uv_pos; \n "
" layout(location = 0) out vec2 uv_interp; \n "
" void main() { gl_Position = vertex_pos; uv_interp=uv_pos; \n } " ;
//"void main() { if (gl_VertexIndex==0) gl_Position=vec4(-0.8,-0.8,0.0,1.0); if (gl_VertexIndex==1) gl_Position=vec4(-0.8,-0.2,0.0,1.0); if (gl_VertexIndex==2) gl_Position=vec4(-0.2,-0.2,0.0,1.0); if (gl_VertexIndex==3) gl_Position=vec4(-0.2,-0.8,0.0,1.0);\n }";
RenderingDevice : : ShaderStageSource frag ;
frag . shader_stage = RenderingDevice : : SHADER_STAGE_FRAGMENT ;
frag . shader_source = " #version 450 \n "
" layout (location = 0) in vec2 uv_interp; \n "
" layout (location = 0) out vec4 uFragColor; \n "
" layout (binding = 0) uniform sampler2D t; \n "
2019-06-08 22:10:52 +02:00
" layout (push_constant, binding=1) uniform ColorMultiplier { vec4 color_mult; } color_multiplier; \n "
" void main() { uFragColor=texture(t,uv_interp) * color_multiplier.color_mult; } \n " ;
2019-06-07 18:07:57 +02:00
Vector < RenderingDevice : : ShaderStageSource > source ;
source . push_back ( vert ) ;
source . push_back ( frag ) ;
String error ;
shader = rendering_device - > shader_create_from_source ( source , & error ) ;
if ( shader = = RenderingDevice : : INVALID_ID ) {
print_line ( " failed compilation: " + error ) ;
} else {
print_line ( " compilation success " ) ;
}
}
RenderingDevice : : ID vertex_desc ;
{
PoolVector < uint8_t > pv ;
pv . resize ( 24 * 4 ) ;
{
PoolVector < uint8_t > : : Write w = pv . write ( ) ;
float * p32 = ( float * ) w . ptr ( ) ;
p32 [ 0 ] = - 0.8 ;
p32 [ 1 ] = - 0.8 ;
p32 [ 2 ] = 0.0 ;
p32 [ 3 ] = 1.0 ;
p32 [ 4 ] = 0.0 ;
p32 [ 5 ] = 0.0 ;
p32 [ 6 ] = - 0.8 ;
p32 [ 7 ] = - 0.2 ;
p32 [ 8 ] = 0.0 ;
p32 [ 9 ] = 1.0 ;
p32 [ 10 ] = 0.0 ;
p32 [ 11 ] = 1.0 ;
p32 [ 12 ] = - 0.2 ;
p32 [ 13 ] = - 0.2 ;
p32 [ 14 ] = 0.0 ;
p32 [ 15 ] = 1.0 ;
p32 [ 16 ] = 1.0 ;
p32 [ 17 ] = 1.0 ;
p32 [ 18 ] = - 0.2 ;
p32 [ 19 ] = - 0.8 ;
p32 [ 20 ] = 0.0 ;
p32 [ 21 ] = 1.0 ;
p32 [ 22 ] = 1.0 ;
p32 [ 23 ] = 0.0 ;
}
RenderingDevice : : ID vertex_buffer = rendering_device - > vertex_buffer_create ( pv . size ( ) , pv ) ;
Vector < RenderingDevice : : VertexDescription > vdarr ;
RenderingDevice : : VertexDescription vd ;
vd . format = RenderingDevice : : DATA_FORMAT_R32G32B32A32_SFLOAT ;
vd . stride = 4 * 6 ; //vertex/uv
vd . offset = 0 ;
vd . location = 0 ;
vdarr . push_back ( vd ) ;
vd . format = RenderingDevice : : DATA_FORMAT_R32G32_SFLOAT ;
vd . stride = 4 * 6 ; //vertex/uv
vd . offset = 4 * 4 ; //offset to UV
vd . location = 1 ;
vdarr . push_back ( vd ) ;
vertex_desc = rendering_device - > vertex_description_create ( vdarr ) ;
Vector < RenderingDevice : : ID > buffers ;
buffers . push_back ( vertex_buffer ) ;
buffers . push_back ( vertex_buffer ) ;
test_vertex_array = rendering_device - > vertex_array_create ( 4 , vertex_desc , buffers ) ;
}
RenderingDevice : : ID test_framebuffer_tex_id ;
{
RenderingDevice : : TextureFormat tex_format ;
tex_format . format = RenderingDevice : : DATA_FORMAT_R8G8B8A8_UNORM ; //RenderingDevice::DATA_FORMAT_A8B8G8R8_UNORM_PACK32;
tex_format . width = 256 ;
tex_format . height = 256 ;
tex_format . mipmaps = 1 ;
tex_format . type = RenderingDevice : : TEXTURE_TYPE_2D ;
tex_format . usage_bits = RenderingDevice : : TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice : : TEXTURE_USAGE_COLOR_ATTACHMENT_BIT ;
test_framebuffer_tex_id = rendering_device - > texture_create ( tex_format , RenderingDevice : : TextureView ( ) ) ;
Vector < RenderingDevice : : ID > ids ;
ids . push_back ( test_framebuffer_tex_id ) ;
test_framebuffer = rendering_device - > framebuffer_create ( ids ) ;
}
test_pipeline = rendering_device - > render_pipeline_create ( shader , rendering_device - > framebuffer_get_format ( test_framebuffer ) , vertex_desc , RenderingDevice : : RENDER_PRIMITIVE_TRIANGLES , RenderingDevice : : PipelineRasterizationState ( ) , RenderingDevice : : PipelineMultisampleState ( ) , RenderingDevice : : PipelineDepthStencilState ( ) , RenderingDevice : : PipelineColorBlendState : : create_disabled ( ) ) ;
{
Ref < Image > img ;
img . instance ( ) ;
Error terr = img - > load ( " ../logo.png " ) ;
if ( terr ! = OK ) {
print_line ( " Cant load logo? " ) ;
}
img - > convert ( Image : : FORMAT_RGBA8 ) ;
RenderingDevice : : TextureFormat tex_format ;
tex_format . format = RenderingDevice : : DATA_FORMAT_R8G8B8A8_UNORM ; //RenderingDevice::DATA_FORMAT_A8B8G8R8_UNORM_PACK32;
tex_format . width = img - > get_width ( ) ;
tex_format . height = img - > get_height ( ) ;
print_line ( " imgsize: " + Vector2 ( img - > get_width ( ) , img - > get_height ( ) ) ) ;
tex_format . mipmaps = 1 ;
tex_format . type = RenderingDevice : : TEXTURE_TYPE_2D ;
tex_format . usage_bits = RenderingDevice : : TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice : : TEXTURE_USAGE_CAN_UPDATE_BIT ;
Vector < PoolVector < uint8_t > > initial_data ;
initial_data . push_back ( img - > get_data ( ) ) ;
RenderingDevice : : ID tex_id = rendering_device - > texture_create ( tex_format , RenderingDevice : : TextureView ( ) , initial_data ) ;
RenderingDevice : : ID sampler = rendering_device - > sampler_create ( RenderingDevice : : SamplerState ( ) ) ;
Vector < RenderingDevice : : Uniform > uniform_description ;
RenderingDevice : : Uniform u ;
u . type = RenderingDevice : : UNIFORM_TYPE_SAMPLER_WITH_TEXTURE ;
u . binding = 0 ;
u . ids . push_back ( sampler ) ;
u . ids . push_back ( tex_id ) ;
uniform_description . push_back ( u ) ;
test_uniform_set = rendering_device - > uniform_set_create ( uniform_description , shader , 0 ) ;
}
{
PoolVector < uint8_t > pv ;
pv . resize ( 6 * 4 ) ;
{
PoolVector < uint8_t > : : Write w = pv . write ( ) ;
int * p32 = ( int * ) w . ptr ( ) ;
p32 [ 0 ] = 0 ;
p32 [ 1 ] = 1 ;
p32 [ 2 ] = 2 ;
p32 [ 3 ] = 0 ;
p32 [ 4 ] = 2 ;
p32 [ 5 ] = 3 ;
}
RenderingDevice : : ID index_buffer = rendering_device - > index_buffer_create ( 6 , RenderingDevice : : INDEX_BUFFER_FORMAT_UINT32 , pv ) ;
test_index_array = rendering_device - > index_array_create ( index_buffer , 0 , 6 ) ;
}
{
RenderingDevice : : ID sampler = rendering_device - > sampler_create ( RenderingDevice : : SamplerState ( ) ) ;
Vector < RenderingDevice : : Uniform > uniform_description ;
RenderingDevice : : Uniform u ;
u . type = RenderingDevice : : UNIFORM_TYPE_SAMPLER_WITH_TEXTURE ;
u . binding = 0 ;
u . ids . push_back ( sampler ) ;
u . ids . push_back ( test_framebuffer_tex_id ) ;
uniform_description . push_back ( u ) ;
test_framebuffer_uniform_set = rendering_device - > uniform_set_create ( uniform_description , shader , 0 ) ;
test_framebuffer_pipeline = rendering_device - > render_pipeline_create ( shader , rendering_device - > screen_get_framebuffer_format ( ) , vertex_desc , RenderingDevice : : RENDER_PRIMITIVE_TRIANGLES , RenderingDevice : : PipelineRasterizationState ( ) , RenderingDevice : : PipelineMultisampleState ( ) , RenderingDevice : : PipelineDepthStencilState ( ) , RenderingDevice : : PipelineColorBlendState : : create_disabled ( ) ) ;
}
#if 0
Vector < RenderingDevice : : ShaderStageSource > source ;
RenderingDevice : : ShaderStageSource frag ;
frag . shader_stage = RenderingDevice : : SHADER_STAGE_FRAGMENT ;
frag . shader_source = " "
" #version 450 \n "
" #extension GL_ARB_separate_shader_objects : enable \n "
" #extension GL_ARB_shading_language_420pack : enable \n "
" layout (set =2, binding = 3) uniform sampler2D sampie; \n "
" layout (set =2, binding = 4) uniform texture2D texie; \n "
" layout (set =2, binding = 5) uniform sampler sampieonly; \n "
" layout (set =2, binding = 6) uniform sampler2D sampiearr[2]; \n "
" layout (set =2, binding = 7) uniform texture2D texiearr[2]; \n "
" layout (set =2, binding = 8) uniform sampler sampieonlyarr[2]; \n "
" layout (set =2, binding = 9) uniform samplerBuffer sabufsa; \n "
" layout (set =2, binding = 9) uniform textureBuffer texbufsa; \n "
" layout (set=3,binding=1,rgba32f) uniform image2D img1; \n "
" layout(std140, set=1,binding = 0) uniform buf { \n "
" mat4 MVP; \n "
" vec4 position[12*3]; \n "
" vec4 attr[12*3]; \n "
" } ubuf; \n "
" layout(std140, set=1,binding = 1) buffer popis { \n "
" int popitos; \n "
" } popibuf; \n "
" layout (location = 0) out vec4 uFragColor; \n "
" \n "
" const vec3 lightDir= vec3(0.424, 0.566, 0.707); \n "
" \n "
" void main() { \n "
" uFragColor = texture(sampie, vec2(ubuf.attr[0].x)); \n "
" uFragColor+= texture(sampler2D(texie,sampieonly), vec2(ubuf.attr[0].x)); \n "
" uFragColor+= texture(sampiearr[1], vec2(ubuf.attr[0].x)); \n "
" uFragColor+= texture(sampler2D(texiearr[1],sampieonlyarr[1]), vec2(ubuf.attr[0].x)); \n "
" uFragColor+= texelFetch(sabufsa,0); \n "
" uFragColor+= texelFetch(samplerBuffer(texbufsa,sampieonly),0); \n "
" uFragColor+= texelFetch(texbufsa,0); \n "
" uFragColor.xy+= imageSize(img1); \n "
" uFragColor.x+= float(popibuf.popitos); \n "
" } \n " ;
source . push_back ( frag ) ;
String error ;
RenderingDevice : : ID shader = rendering_device - > shader_create_from_source ( source , & error ) ;
if ( shader = = RenderingDevice : : INVALID_ID ) {
print_line ( " failed compilation: " + error ) ;
} else {
print_line ( " compilation success " ) ;
}
# endif
2016-10-03 21:33:42 +02:00
# endif
2014-02-10 02:10:30 +01:00
2018-11-01 19:22:15 +01:00
visual_server = memnew ( VisualServerRaster ) ;
2017-06-09 05:23:50 +02:00
if ( get_render_thread_mode ( ) ! = RENDER_THREAD_UNSAFE ) {
visual_server = memnew ( VisualServerWrapMT ( visual_server , get_render_thread_mode ( ) = = RENDER_SEPARATE_THREAD ) ) ;
2014-02-10 02:10:30 +01:00
}
2018-11-01 19:22:15 +01:00
2017-10-24 21:35:28 +02:00
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 ) ;
2017-07-05 17:19:24 +02:00
} else if ( current_videomode . borderless_window ) {
Hints hints ;
Atom property ;
hints . flags = 2 ;
hints . decorations = 0 ;
property = XInternAtom ( x11_display , " _MOTIF_WM_HINTS " , True ) ;
XChangeProperty ( x11_display , x11_window , property , property , 32 , PropModeReplace , ( unsigned char * ) & hints , 5 ) ;
2014-08-25 08:54:10 +02:00
}
2019-09-11 10:13:48 +02:00
// make PID known to X11
{
const long pid = this - > get_process_id ( ) ;
Atom net_wm_pid = XInternAtom ( x11_display , " _NET_WM_PID " , False ) ;
XChangeProperty ( x11_display , x11_window , net_wm_pid , XA_CARDINAL , 32 , PropModeReplace , ( unsigned char * ) & pid , 1 ) ;
}
2015-01-15 14:50:23 +01:00
// disable resizable window
2018-11-19 03:03:54 +01:00
if ( ! current_videomode . resizable & & ! current_videomode . fullscreen ) {
2014-08-25 08:54:10 +02:00
XSizeHints * xsh ;
xsh = XAllocSizeHints ( ) ;
xsh - > flags = PMinSize | PMaxSize ;
XWindowAttributes xwa ;
if ( current_videomode . fullscreen ) {
2017-03-05 16:44:50 +01:00
XGetWindowAttributes ( x11_display , DefaultRootWindow ( x11_display ) , & xwa ) ;
2014-09-16 08:49:31 +02:00
} else {
2017-03-05 16:44:50 +01:00
XGetWindowAttributes ( x11_display , x11_window , & xwa ) ;
2014-08-25 08:54:10 +02:00
}
2016-03-09 00:00:52 +01:00
xsh - > min_width = xwa . width ;
2014-08-25 08:54:10 +02:00
xsh - > max_width = xwa . width ;
xsh - > min_height = xwa . height ;
xsh - > max_height = xwa . height ;
XSetWMNormalHints ( x11_display , x11_window , xsh ) ;
2015-01-15 14:50:23 +01:00
XFree ( xsh ) ;
2014-08-25 08:54:10 +02:00
}
2015-01-17 16:28:04 +01:00
2017-12-27 20:51:19 +01:00
if ( current_videomode . always_on_top ) {
current_videomode . always_on_top = false ;
set_window_always_on_top ( true ) ;
}
2018-01-03 18:26:44 +01:00
ERR_FAIL_COND_V ( ! visual_server , ERR_UNAVAILABLE ) ;
ERR_FAIL_COND_V ( x11_window = = 0 , ERR_UNAVAILABLE ) ;
2014-02-10 02:10:30 +01:00
XSetWindowAttributes new_attr ;
2017-03-05 16:44:50 +01:00
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 |
2017-06-27 11:26:43 +02:00
ColormapChangeMask | OwnerGrabButtonMask |
im_event_mask ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
XChangeWindowAttributes ( x11_display , x11_window , CWEventMask , & new_attr ) ;
2016-03-09 00:00:52 +01:00
2018-08-18 00:59:26 +02:00
static unsigned char all_mask_data [ XIMaskLen ( XI_LASTEVENT ) ] = { } ;
static unsigned char all_master_mask_data [ XIMaskLen ( XI_LASTEVENT ) ] = { } ;
2017-11-29 21:01:26 +01:00
2018-08-18 00:59:26 +02:00
xi . all_event_mask . deviceid = XIAllDevices ;
xi . all_event_mask . mask_len = sizeof ( all_mask_data ) ;
xi . all_event_mask . mask = all_mask_data ;
2017-11-29 21:01:26 +01:00
2018-08-18 00:59:26 +02:00
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 ;
2017-11-29 21:01:26 +01:00
2018-08-18 00:59:26 +02:00
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 ) ;
2017-11-29 21:01:26 +01:00
2018-08-18 00:59:26 +02:00
# 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 ) ;
2017-11-29 21:01:26 +01:00
}
# endif
2018-08-18 00:59:26 +02:00
XISelectEvents ( x11_display , x11_window , & xi . all_event_mask , 1 ) ;
XISelectEvents ( x11_display , DefaultRootWindow ( x11_display ) , & xi . all_master_event_mask , 1 ) ;
// Disabled by now since grabbing also blocks mouse events
// (they are received as extended events instead of standard events)
/*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
// 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 ) ;
} */
2015-01-15 14:50:23 +01:00
/* set the titlebar name */
XStoreName ( x11_display , x11_window , " Godot " ) ;
2014-05-22 08:05:05 +02:00
2016-03-09 00:00:52 +01:00
wm_delete = XInternAtom ( x11_display , " WM_DELETE_WINDOW " , true ) ;
2014-02-10 02:10:30 +01:00
XSetWMProtocols ( x11_display , x11_window , & wm_delete , 1 ) ;
2016-03-09 00:00:52 +01:00
2018-06-07 22:16:57 +02:00
im_active = false ;
im_position = Vector2 ( ) ;
2014-02-10 02:10:30 +01:00
if ( xim & & xim_style ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
xic = XCreateIC ( xim , XNInputStyle , xim_style , XNClientWindow , x11_window , XNFocusWindow , x11_window , ( char * ) NULL ) ;
2017-06-27 11:26:43 +02:00
if ( XGetICValues ( xic , XNFilterEvents , & im_event_mask , NULL ) ! = NULL ) {
WARN_PRINT ( " XGetICValues couldn't obtain XNFilterEvents value " ) ;
XDestroyIC ( xic ) ;
xic = NULL ;
}
if ( xic ) {
2018-06-07 22:16:57 +02:00
XUnsetICFocus ( xic ) ;
2017-06-27 11:26:43 +02:00
} else {
WARN_PRINT ( " XCreateIC couldn't create xic " ) ;
}
2014-02-10 02:10:30 +01:00
} else {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
xic = NULL ;
2014-02-10 02:10:30 +01:00
WARN_PRINT ( " XCreateIC couldn't create xic " ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
cursor_size = XcursorGetDefaultSize ( x11_display ) ;
cursor_theme = XcursorGetTheme ( x11_display ) ;
if ( ! cursor_theme ) {
2018-08-24 08:47:34 +02:00
print_verbose ( " XcursorGetTheme could not get cursor theme " ) ;
2017-03-05 16:44:50 +01:00
cursor_theme = " default " ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < CURSOR_MAX ; i + + ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
cursors [ i ] = None ;
img [ i ] = NULL ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
current_cursor = CURSOR_ARROW ;
2014-02-10 02:10:30 +01:00
2019-04-04 22:00:16 +02:00
for ( int i = 0 ; i < CURSOR_MAX ; i + + ) {
static const char * cursor_file [ ] = {
" left_ptr " ,
" xterm " ,
" hand2 " ,
" cross " ,
" watch " ,
" left_ptr_watch " ,
" fleur " ,
2020-02-10 01:52:19 +01:00
" dnd-move " ,
" crossed_circle " ,
" v_double_arrow " ,
" h_double_arrow " ,
2019-04-04 22:00:16 +02:00
" size_bdiag " ,
" size_fdiag " ,
2020-02-10 01:52:19 +01:00
" move " ,
" row_resize " ,
" col_resize " ,
2019-04-04 22:00:16 +02:00
" question_arrow "
} ;
img [ i ] = XcursorLibraryLoadImage ( cursor_file [ i ] , cursor_theme , cursor_size ) ;
2020-02-10 01:52:19 +01:00
if ( ! img [ i ] ) {
const char * fallback = NULL ;
switch ( i ) {
case CURSOR_POINTING_HAND :
fallback = " pointer " ;
break ;
case CURSOR_CROSS :
fallback = " crosshair " ;
break ;
case CURSOR_WAIT :
fallback = " wait " ;
break ;
case CURSOR_BUSY :
fallback = " progress " ;
break ;
case CURSOR_DRAG :
fallback = " grabbing " ;
break ;
case CURSOR_CAN_DROP :
fallback = " hand1 " ;
break ;
case CURSOR_FORBIDDEN :
fallback = " forbidden " ;
break ;
case CURSOR_VSIZE :
fallback = " ns-resize " ;
break ;
case CURSOR_HSIZE :
fallback = " ew-resize " ;
break ;
case CURSOR_BDIAGSIZE :
fallback = " fd_double_arrow " ;
break ;
case CURSOR_FDIAGSIZE :
fallback = " bd_double_arrow " ;
break ;
case CURSOR_MOVE :
img [ i ] = img [ CURSOR_DRAG ] ;
break ;
case CURSOR_VSPLIT :
fallback = " sb_v_double_arrow " ;
break ;
case CURSOR_HSPLIT :
fallback = " sb_h_double_arrow " ;
break ;
case CURSOR_HELP :
fallback = " help " ;
break ;
}
if ( fallback ! = NULL ) {
img [ i ] = XcursorLibraryLoadImage ( fallback , cursor_theme , cursor_size ) ;
}
}
2019-04-04 22:00:16 +02:00
if ( img [ i ] ) {
cursors [ i ] = XcursorImageLoadCursor ( x11_display , img [ i ] ) ;
} else {
print_verbose ( " Failed loading custom cursor: " + String ( cursor_file [ i ] ) ) ;
2014-02-10 02:10:30 +01:00
}
}
{
2019-05-23 12:47:28 +02:00
// Creating an empty/transparent cursor
// Create 1x1 bitmap
Pixmap cursormask = XCreatePixmap ( x11_display ,
RootWindow ( x11_display , DefaultScreen ( x11_display ) ) , 1 , 1 , 1 ) ;
2014-02-10 02:10:30 +01:00
2019-05-23 12:47:28 +02:00
// Fill with zero
XGCValues xgc ;
2015-10-25 15:15:56 +01:00
xgc . function = GXclear ;
2019-05-23 12:47:28 +02:00
GC gc = XCreateGC ( x11_display , cursormask , GCFunction , & xgc ) ;
2014-02-10 02:10:30 +01:00
XFillRectangle ( x11_display , cursormask , gc , 0 , 0 , 1 , 1 ) ;
2019-05-23 12:47:28 +02:00
// 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
2017-03-05 16:44:50 +01:00
& col , & col , 0 , 0 ) ;
2019-05-23 12:47:28 +02:00
2017-03-05 16:44:50 +01:00
XFreePixmap ( x11_display , cursormask ) ;
XFreeGC ( x11_display , gc ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor = = None ) {
ERR_PRINT ( " FAILED CREATING CURSOR " ) ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
null_cursor = cursor ;
2014-02-10 02:10:30 +01:00
}
set_cursor_shape ( CURSOR_BUSY ) ;
2016-05-28 21:35:42 +02:00
//Set Xdnd (drag & drop) support
Atom XdndAware = XInternAtom ( x11_display , " XdndAware " , False ) ;
2017-03-05 16:44:50 +01:00
Atom version = 5 ;
XChangeProperty ( x11_display , x11_window , XdndAware , XA_ATOM , 32 , PropModeReplace , ( unsigned char * ) & version , 1 ) ;
2016-05-28 21:35:42 +02:00
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 ;
2014-02-10 02:10:30 +01:00
visual_server - > init ( ) ;
2018-08-25 00:04:25 +02:00
AudioDriverManager : : initialize ( p_audio_driver ) ;
2017-03-05 16:44:50 +01:00
input = memnew ( InputDefault ) ;
2017-01-25 19:21:41 +01:00
window_has_focus = true ; // Set focus to true at init
2015-12-21 22:39:03 +01:00
# ifdef JOYDEV_ENABLED
2017-03-05 16:44:50 +01:00
joypad = memnew ( JoypadLinux ( input ) ) ;
2015-12-18 06:12:53 +01:00
# endif
2017-11-17 15:25:22 +01:00
_ensure_user_data_dir ( ) ;
2017-07-26 23:29:51 +02:00
power_manager = memnew ( PowerX11 ) ;
2017-10-24 21:35:28 +02:00
2019-06-04 10:21:29 +02:00
if ( p_desired . layered ) {
2017-12-10 19:38:26 +01:00
set_window_per_pixel_transparency_enabled ( true ) ;
}
2017-10-24 21:35:28 +02:00
XEvent xevent ;
2017-12-09 23:21:34 +01:00
while ( XPending ( x11_display ) > 0 ) {
XNextEvent ( x11_display , & xevent ) ;
if ( xevent . type = = ConfigureNotify ) {
_window_changed ( & xevent ) ;
}
2017-10-24 21:35:28 +02:00
}
2018-01-05 12:10:47 +01:00
2018-09-09 22:13:50 +02:00
update_real_mouse_position ( ) ;
2018-01-05 12:10:47 +01:00
return OK ;
2014-02-10 02:10:30 +01:00
}
2015-10-25 15:15:56 +01:00
2018-08-18 00:59:26 +02:00
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 ( ) ;
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 = = XIMasterPointer | | dev - > use = = XIFloatingSlave ) )
continue ;
bool direct_touch = false ;
bool absolute_mode = false ;
int resolution_x = 0 ;
int resolution_y = 0 ;
int range_min_x = 0 ;
int range_min_y = 0 ;
int range_max_x = 0 ;
int range_max_y = 0 ;
2018-12-13 21:32:11 +01:00
int pressure_resolution = 0 ;
int tilt_resolution_x = 0 ;
int tilt_resolution_y = 0 ;
2018-08-18 00:59:26 +02:00
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 ] ;
2018-12-13 21:32:11 +01:00
if ( class_info - > number = = VALUATOR_ABSX & & class_info - > mode = = XIModeAbsolute ) {
2018-08-18 00:59:26 +02:00
resolution_x = class_info - > resolution ;
range_min_x = class_info - > min ;
range_max_x = class_info - > max ;
absolute_mode = true ;
2018-12-13 21:32:11 +01:00
} else if ( class_info - > number = = VALUATOR_ABSY & & class_info - > mode = = XIModeAbsolute ) {
2018-08-18 00:59:26 +02:00
resolution_y = class_info - > resolution ;
range_min_y = class_info - > min ;
range_max_y = class_info - > max ;
absolute_mode = true ;
2018-12-13 21:32:11 +01:00
} else if ( class_info - > number = = VALUATOR_PRESSURE & & class_info - > mode = = XIModeAbsolute ) {
2020-01-31 21:40:14 +01:00
pressure_resolution = ( class_info - > max - class_info - > min ) ;
if ( pressure_resolution = = 0 ) pressure_resolution = 1 ;
2018-12-13 21:32:11 +01:00
} else if ( class_info - > number = = VALUATOR_TILTX & & class_info - > mode = = XIModeAbsolute ) {
2020-01-31 21:40:14 +01:00
tilt_resolution_x = ( class_info - > max - class_info - > min ) ;
if ( tilt_resolution_x = = 0 ) tilt_resolution_x = 1 ;
2018-12-13 21:32:11 +01:00
} else if ( class_info - > number = = VALUATOR_TILTY & & class_info - > mode = = XIModeAbsolute ) {
2020-01-31 21:40:14 +01:00
tilt_resolution_y = ( class_info - > max - class_info - > min ) ;
if ( tilt_resolution_y = = 0 ) tilt_resolution_y = 1 ;
2018-08-18 00:59:26 +02:00
}
}
}
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 = ( range_max_x - range_min_x ) * abs_resolution_range_mult ;
}
if ( resolution_y < = 0 ) {
resolution_y = ( range_max_y - range_min_y ) * 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 ) ) ;
}
2018-12-13 21:32:11 +01:00
xi . pressure = 0 ;
xi . pen_devices [ dev - > deviceid ] = Vector3 ( pressure_resolution , tilt_resolution_x , tilt_resolution_y ) ;
2018-08-18 00:59:26 +02:00
}
XIFreeDeviceInfo ( info ) ;
# ifdef TOUCH_ENABLED
if ( ! xi . touch_devices . size ( ) ) {
print_verbose ( " XInput: No touch devices found. " ) ;
}
# endif
return true ;
}
2017-06-27 11:26:43 +02:00
void OS_X11 : : xim_destroy_callback ( : : XIM im , : : XPointer client_data ,
: : XPointer call_data ) {
WARN_PRINT ( " Input method stopped " ) ;
OS_X11 * os = reinterpret_cast < OS_X11 * > ( client_data ) ;
os - > xim = NULL ;
os - > xic = NULL ;
}
2018-06-07 22:16:57 +02:00
void OS_X11 : : set_ime_active ( const bool p_active ) {
im_active = p_active ;
if ( ! xic )
return ;
if ( p_active ) {
XSetICFocus ( xic ) ;
set_ime_position ( im_position ) ;
} else {
XUnsetICFocus ( xic ) ;
}
}
2017-06-25 17:50:45 +02:00
void OS_X11 : : set_ime_position ( const Point2 & p_pos ) {
2017-06-27 11:26:43 +02:00
2018-06-07 22:16:57 +02:00
im_position = p_pos ;
2017-06-25 17:50:45 +02:00
if ( ! xic )
2017-06-27 11:26:43 +02:00
return ;
2017-06-25 17:50:45 +02:00
2017-06-27 11:26:43 +02:00
: : XPoint spot ;
2017-06-25 17:50:45 +02:00
spot . x = short ( p_pos . x ) ;
spot . y = short ( p_pos . y ) ;
XVaNestedList preedit_attr = XVaCreateNestedList ( 0 , XNSpotLocation , & spot , NULL ) ;
XSetICValues ( xic , XNPreeditAttributes , preedit_attr , NULL ) ;
2017-06-27 11:26:43 +02:00
XFree ( preedit_attr ) ;
}
2018-01-20 14:42:19 +01:00
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 ;
}
2014-02-10 02:10:30 +01:00
void OS_X11 : : finalize ( ) {
2017-03-05 16:44:50 +01:00
if ( main_loop )
2014-02-10 02:10:30 +01:00
memdelete ( main_loop ) ;
2017-03-05 16:44:50 +01:00
main_loop = NULL ;
2014-02-10 02:10:30 +01:00
2017-12-06 21:36:34 +01:00
/*
2017-01-14 12:26:56 +01:00
if ( debugger_connection_console ) {
memdelete ( debugger_connection_console ) ;
}
*/
2018-07-14 14:11:28 +02:00
# ifdef ALSAMIDI_ENABLED
driver_alsamidi . close ( ) ;
# endif
2014-02-10 02:10:30 +01:00
2016-01-05 23:35:54 +01:00
# ifdef JOYDEV_ENABLED
2017-01-08 21:05:51 +01:00
memdelete ( joypad ) ;
2017-11-29 21:01:26 +01:00
# endif
2018-08-18 00:59:26 +02:00
xi . touch_devices . clear ( ) ;
xi . state . clear ( ) ;
2015-12-31 14:25:21 +01:00
memdelete ( input ) ;
2019-07-24 19:57:59 +02:00
cursors_cache . clear ( ) ;
2014-02-10 02:10:30 +01:00
visual_server - > finish ( ) ;
memdelete ( visual_server ) ;
2016-10-03 21:33:42 +02:00
//memdelete(rasterizer);
2016-03-09 00:00:52 +01:00
2017-08-24 18:51:28 +02:00
memdelete ( power_manager ) ;
2016-06-09 18:54:06 +02:00
if ( xrandr_handle )
dlclose ( xrandr_handle ) ;
2017-03-05 16:44:50 +01:00
XUnmapWindow ( x11_display , x11_window ) ;
XDestroyWindow ( x11_display , x11_window ) ;
2015-10-25 15:15:56 +01:00
2017-10-13 00:18:04 +02:00
# if defined(OPENGL_ENABLED)
2014-02-10 02:10:30 +01:00
memdelete ( context_gl ) ;
# endif
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < CURSOR_MAX ; i + + ) {
if ( cursors [ i ] ! = None )
XFreeCursor ( x11_display , cursors [ i ] ) ;
if ( img [ i ] ! = NULL )
XcursorImageDestroy ( img [ i ] ) ;
2016-03-09 00:00:52 +01:00
} ;
2015-10-25 15:15:56 +01:00
2017-06-27 11:26:43 +02:00
if ( xic ) {
XDestroyIC ( xic ) ;
}
if ( xim ) {
XCloseIM ( xim ) ;
}
2014-02-10 02:10:30 +01:00
XCloseDisplay ( x11_display ) ;
if ( xmbstring )
memfree ( xmbstring ) ;
2015-10-25 15:15:56 +01:00
2014-02-10 02:10:30 +01:00
args . clear ( ) ;
}
void OS_X11 : : set_mouse_mode ( MouseMode p_mode ) {
2017-03-05 16:44:50 +01:00
if ( p_mode = = mouse_mode )
2014-02-10 02:10:30 +01:00
return ;
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED )
2014-02-10 02:10:30 +01:00
XUngrabPointer ( x11_display , CurrentTime ) ;
2017-01-25 19:21:41 +01:00
// 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 ) {
2018-04-09 02:59:51 +02:00
XDefineCursor ( x11_display , x11_window , cursors [ current_cursor ] ) ; // show cursor
2017-01-25 19:21:41 +01:00
} else {
2017-03-05 16:44:50 +01:00
XDefineCursor ( x11_display , x11_window , null_cursor ) ; // hide cursor
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
mouse_mode = p_mode ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED ) {
2015-08-29 06:43:21 +02:00
2018-08-18 00:59:26 +02:00
//flush pending motion events
flush_mouse_motion ( ) ;
2015-08-29 06:43:21 +02:00
2017-01-15 22:15:47 +01:00
if ( XGrabPointer (
2017-03-05 16:44:50 +01:00
x11_display , x11_window , True ,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask ,
GrabModeAsync , GrabModeAsync , x11_window , None , CurrentTime ) ! = GrabSuccess ) {
2014-02-10 02:10:30 +01:00
ERR_PRINT ( " NO GRAB " ) ;
}
2018-09-04 15:11:37 +02:00
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 ) ;
2014-02-10 02:10:30 +01:00
2018-09-04 15:11:37 +02:00
input - > set_mouse_position ( center ) ;
}
2015-08-29 06:43:21 +02:00
} else {
2017-03-05 16:44:50 +01:00
do_mouse_warp = false ;
2015-02-12 15:58:00 +01:00
}
2016-04-12 16:20:28 +02:00
XFlush ( x11_display ) ;
2014-02-10 02:10:30 +01:00
}
2017-09-10 15:37:49 +02:00
void OS_X11 : : warp_mouse_position ( const Point2 & p_to ) {
2014-09-20 02:01:41 +02:00
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
2014-09-20 02:01:41 +02:00
2017-03-05 16:44:50 +01:00
last_mouse_pos = p_to ;
2014-09-20 02:01:41 +02:00
} else {
2015-02-14 23:22:06 +01:00
/*XWindowAttributes xwa;
XGetWindowAttributes ( x11_display , x11_window , & xwa ) ;
printf ( " %d %d \n " , xwa . x , xwa . y ) ; needed ? */
2014-09-20 02:01:41 +02:00
XWarpPointer ( x11_display , None , x11_window ,
2017-03-05 16:44:50 +01:00
0 , 0 , 0 , 0 , ( int ) p_to . x , ( int ) p_to . y ) ;
2014-09-20 02:01:41 +02:00
}
}
2018-08-18 00:59:26 +02:00
void OS_X11 : : flush_mouse_motion ( ) {
while ( true ) {
if ( XPending ( x11_display ) > 0 ) {
XEvent event ;
XPeekEvent ( x11_display , & event ) ;
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 ) {
XNextEvent ( x11_display , & event ) ;
} else {
break ;
}
} else {
break ;
}
} else {
break ;
}
}
xi . relative_motion . x = 0 ;
xi . relative_motion . y = 0 ;
}
2014-02-10 02:10:30 +01:00
OS : : MouseMode OS_X11 : : get_mouse_mode ( ) const {
return mouse_mode ;
}
int OS_X11 : : get_mouse_button_state ( ) const {
return last_button_state ;
}
2017-03-29 17:29:38 +02:00
Point2 OS_X11 : : get_mouse_position ( ) const {
2014-02-10 02:10:30 +01:00
return last_mouse_pos ;
}
2017-12-10 19:38:26 +01:00
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 ) {
set_borderless_window ( true ) ;
layered_window = true ;
} else {
layered_window = false ;
}
}
}
2017-03-05 16:44:50 +01:00
void OS_X11 : : set_window_title ( const String & p_title ) {
XStoreName ( x11_display , x11_window , p_title . utf8 ( ) . get_data ( ) ) ;
2016-02-04 00:39:53 +01:00
Atom _net_wm_name = XInternAtom ( x11_display , " _NET_WM_NAME " , false ) ;
Atom utf8_string = XInternAtom ( x11_display , " UTF8_STRING " , false ) ;
2017-03-05 16:44:50 +01:00
XChangeProperty ( x11_display , x11_window , _net_wm_name , utf8_string , 8 , PropModeReplace , ( unsigned char * ) p_title . utf8 ( ) . get_data ( ) , p_title . utf8 ( ) . length ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_X11 : : set_video_mode ( const VideoMode & p_video_mode , int p_screen ) {
2014-02-10 02:10:30 +01:00
}
2015-10-25 18:36:27 +01:00
OS : : VideoMode OS_X11 : : get_video_mode ( int p_screen ) const {
2014-02-10 02:10:30 +01:00
return current_videomode ;
}
2017-03-05 16:44:50 +01:00
void OS_X11 : : get_fullscreen_mode_list ( List < VideoMode > * p_list , int p_screen ) const {
2014-02-10 02:10:30 +01:00
}
2015-01-10 14:50:31 +01:00
void OS_X11 : : set_wm_fullscreen ( bool p_enabled ) {
2018-02-16 21:49:17 +01:00
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 ) ;
XChangeProperty ( x11_display , x11_window , property , property , 32 , PropModeReplace , ( unsigned char * ) & hints , 5 ) ;
}
2017-07-12 19:20:29 +02:00
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 ) ;
}
2018-02-21 17:30:55 +01:00
// Using EWMH -- Extended Window Manager Hints
2015-01-10 08:47:34 +01:00
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 ;
2015-01-16 16:18:45 +01:00
xev . xclient . data . l [ 0 ] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE ;
2015-01-10 08:47:34 +01:00
xev . xclient . data . l [ 1 ] = wm_fullscreen ;
xev . xclient . data . l [ 2 ] = 0 ;
2015-01-17 11:43:12 +01:00
XSendEvent ( x11_display , DefaultRootWindow ( x11_display ) , False , SubstructureRedirectMask | SubstructureNotifyMask , & xev ) ;
2017-09-09 23:24:19 +02:00
// set bypass compositor hint
Atom bypass_compositor = XInternAtom ( x11_display , " _NET_WM_BYPASS_COMPOSITOR " , False ) ;
unsigned long compositing_disable_on = p_enabled ? 1 : 0 ;
XChangeProperty ( x11_display , x11_window , bypass_compositor , XA_CARDINAL , 32 , PropModeReplace , ( unsigned char * ) & compositing_disable_on , 1 ) ;
2017-07-12 19:20:29 +02:00
XFlush ( x11_display ) ;
2019-06-13 14:31:08 +02:00
if ( ! p_enabled ) {
2017-07-12 19:20:29 +02:00
// Reset the non-resizable flags if we un-set these before.
Size2 size = get_window_size ( ) ;
XSizeHints * xsh ;
xsh = XAllocSizeHints ( ) ;
2019-06-13 14:31:08 +02:00
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 ;
}
}
2017-07-12 19:20:29 +02:00
XSetWMNormalHints ( x11_display , x11_window , xsh ) ;
XFree ( xsh ) ;
2017-09-10 06:41:34 +02:00
2019-06-11 11:07:48 +02:00
// put back or remove decorations according to the last set borderless state
2017-09-10 06:41:34 +02:00
Hints hints ;
Atom property ;
hints . flags = 2 ;
2019-06-11 11:07:48 +02:00
hints . decorations = current_videomode . borderless_window ? 0 : 1 ;
2017-09-10 06:41:34 +02:00
property = XInternAtom ( x11_display , " _MOTIF_WM_HINTS " , True ) ;
XChangeProperty ( x11_display , x11_window , property , property , 32 , PropModeReplace , ( unsigned char * ) & hints , 5 ) ;
}
2015-01-10 14:50:31 +01:00
}
2017-12-27 20:51:19 +01:00
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 ) ;
}
2015-01-11 11:52:42 +01:00
int OS_X11 : : get_screen_count ( ) const {
2015-01-16 16:18:45 +01:00
// Using Xinerama Extension
2015-01-13 14:01:24 +01:00
int event_base , error_base ;
const Bool ext_okay = XineramaQueryExtension ( x11_display , & event_base , & error_base ) ;
2017-03-05 16:44:50 +01:00
if ( ! ext_okay ) return 0 ;
2016-03-09 00:00:52 +01:00
2015-01-13 14:01:24 +01:00
int count ;
2017-03-05 16:44:50 +01:00
XineramaScreenInfo * xsi = XineramaQueryScreens ( x11_display , & count ) ;
2015-01-13 14:01:24 +01:00
XFree ( xsi ) ;
return count ;
2015-01-11 11:52:42 +01:00
}
2015-03-22 23:00:50 +01:00
int OS_X11 : : get_current_screen ( ) const {
2017-03-05 16:44:50 +01:00
int x , y ;
Window child ;
XTranslateCoordinates ( x11_display , x11_window , DefaultRootWindow ( x11_display ) , 0 , 0 , & x , & y , & child ) ;
2015-01-14 05:02:59 +01:00
int count = get_screen_count ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < count ; i + + ) {
2015-01-14 05:02:59 +01:00
Point2i pos = get_screen_position ( i ) ;
Size2i size = get_screen_size ( i ) ;
2017-03-05 16:44:50 +01:00
if ( ( x > = pos . x & & x < pos . x + size . width ) & & ( y > = pos . y & & y < pos . y + size . height ) )
2016-03-09 00:00:52 +01:00
return i ;
2015-01-14 05:02:59 +01:00
}
return 0 ;
}
2015-03-22 23:00:50 +01:00
void OS_X11 : : set_current_screen ( int p_screen ) {
2015-01-14 05:02:59 +01:00
int count = get_screen_count ( ) ;
2017-03-05 16:44:50 +01:00
if ( p_screen > = count ) return ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( current_videomode . fullscreen ) {
2015-01-14 05:02:59 +01:00
Point2i position = get_screen_position ( p_screen ) ;
Size2i size = get_screen_size ( p_screen ) ;
2017-03-05 16:44:50 +01:00
XMoveResizeWindow ( x11_display , x11_window , position . x , position . y , size . x , size . y ) ;
} else {
if ( p_screen ! = get_current_screen ( ) ) {
2015-01-14 05:02:59 +01:00
Point2i position = get_screen_position ( p_screen ) ;
XMoveWindow ( x11_display , x11_window , position . x , position . y ) ;
}
}
}
2015-01-13 10:25:50 +01:00
Point2 OS_X11 : : get_screen_position ( int p_screen ) const {
2017-08-20 21:12:29 +02:00
if ( p_screen = = - 1 ) {
p_screen = get_current_screen ( ) ;
}
2015-01-16 16:18:45 +01:00
// Using Xinerama Extension
2015-01-13 14:01:24 +01:00
int event_base , error_base ;
const Bool ext_okay = XineramaQueryExtension ( x11_display , & event_base , & error_base ) ;
2017-03-05 16:44:50 +01:00
if ( ! ext_okay ) {
return Point2i ( 0 , 0 ) ;
2015-08-31 04:36:46 +02:00
}
2016-03-09 00:00:52 +01:00
2015-01-13 14:01:24 +01:00
int count ;
2017-03-05 16:44:50 +01:00
XineramaScreenInfo * xsi = XineramaQueryScreens ( x11_display , & count ) ;
if ( p_screen > = count ) {
return Point2i ( 0 , 0 ) ;
2015-08-31 04:36:46 +02:00
}
2016-03-09 00:00:52 +01:00
2015-01-13 14:01:24 +01:00
Point2i position = Point2i ( xsi [ p_screen ] . x_org , xsi [ p_screen ] . y_org ) ;
2015-08-31 04:36:46 +02:00
2015-01-13 14:01:24 +01:00
XFree ( xsi ) ;
2015-08-31 04:36:46 +02:00
2015-01-13 14:01:24 +01:00
return position ;
2015-01-13 10:25:50 +01:00
}
2015-01-11 12:35:53 +01:00
Size2 OS_X11 : : get_screen_size ( int p_screen ) const {
2017-08-20 21:12:29 +02:00
if ( p_screen = = - 1 ) {
p_screen = get_current_screen ( ) ;
}
2015-01-16 16:18:45 +01:00
// Using Xinerama Extension
2015-01-13 14:01:24 +01:00
int event_base , error_base ;
const Bool ext_okay = XineramaQueryExtension ( x11_display , & event_base , & error_base ) ;
2017-03-05 16:44:50 +01:00
if ( ! ext_okay ) return Size2i ( 0 , 0 ) ;
2016-03-09 00:00:52 +01:00
2015-01-13 14:01:24 +01:00
int count ;
2017-03-05 16:44:50 +01:00
XineramaScreenInfo * xsi = XineramaQueryScreens ( x11_display , & count ) ;
if ( p_screen > = count ) return Size2i ( 0 , 0 ) ;
2016-03-09 00:00:52 +01:00
2015-01-13 14:01:24 +01:00
Size2i size = Point2i ( xsi [ p_screen ] . width , xsi [ p_screen ] . height ) ;
XFree ( xsi ) ;
return size ;
2015-01-11 12:35:53 +01:00
}
2016-06-09 18:54:06 +02:00
int OS_X11 : : get_screen_dpi ( int p_screen ) const {
2017-08-20 21:12:29 +02:00
if ( p_screen = = - 1 ) {
p_screen = get_current_screen ( ) ;
}
2016-06-09 18:54:06 +02:00
//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 ) ;
2016-06-12 15:29:02 +02:00
if ( xrandr_ext_ok ) {
2016-06-09 18:54:06 +02:00
int count = 0 ;
if ( xrr_get_monitors ) {
xrr_monitor_info * monitors = xrr_get_monitors ( x11_display , x11_window , true , & count ) ;
if ( p_screen < count ) {
2017-03-05 16:44:50 +01:00
double xdpi = sc . width / ( double ) monitors [ p_screen ] . mwidth * 25.4 ;
double ydpi = sc . height / ( double ) monitors [ p_screen ] . mheight * 25.4 ;
2016-06-12 15:29:02 +02:00
xrr_free_monitors ( monitors ) ;
2016-06-09 18:54:06 +02:00
return ( xdpi + ydpi ) / 2 ;
}
2016-06-12 15:29:02 +02:00
xrr_free_monitors ( monitors ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_screen = = 0 ) {
2016-06-09 18:54:06 +02:00
XRRScreenSize * sizes = XRRSizes ( x11_display , 0 , & count ) ;
if ( sizes ) {
2017-03-05 16:44:50 +01:00
double xdpi = sc . width / ( double ) sizes [ 0 ] . mwidth * 25.4 ;
double ydpi = sc . height / ( double ) sizes [ 0 ] . mheight * 25.4 ;
2016-06-09 18:54:06 +02:00
return ( xdpi + ydpi ) / 2 ;
}
}
}
2017-03-05 16:44:50 +01:00
int width_mm = DisplayWidthMM ( x11_display , p_screen ) ;
2016-06-09 18:54:06 +02:00
int height_mm = DisplayHeightMM ( x11_display , p_screen ) ;
2017-03-05 16:44:50 +01:00
double xdpi = ( width_mm ? sc . width / ( double ) width_mm * 25.4 : 0 ) ;
double ydpi = ( height_mm ? sc . height / ( double ) height_mm * 25.4 : 0 ) ;
2019-04-08 11:03:37 +02:00
if ( xdpi | | ydpi )
2017-03-05 16:44:50 +01:00
return ( xdpi + ydpi ) / ( xdpi & & ydpi ? 2 : 1 ) ;
2016-06-09 18:54:06 +02:00
//could not get dpi
return 96 ;
}
2015-01-11 08:47:27 +01:00
Point2 OS_X11 : : get_window_position ( ) const {
2017-03-05 16:44:50 +01:00
int x , y ;
2015-01-11 08:47:27 +01:00
Window child ;
2017-03-05 16:44:50 +01:00
XTranslateCoordinates ( x11_display , x11_window , DefaultRootWindow ( x11_display ) , 0 , 0 , & x , & y , & child ) ;
2019-03-12 18:09:16 +01:00
return Point2i ( x , y ) ;
2015-01-11 08:47:27 +01:00
}
2015-01-10 18:07:23 +01:00
2017-03-05 16:44:50 +01:00
void OS_X11 : : set_window_position ( const Point2 & p_position ) {
2019-03-12 18:09:16 +01:00
int x = 0 ;
int y = 0 ;
2019-06-26 15:08:25 +02:00
if ( ! get_borderless_window ( ) ) {
2019-03-12 18:09:16 +01:00
//exclude window decorations
XSync ( x11_display , False ) ;
Atom prop = XInternAtom ( x11_display , " _NET_FRAME_EXTENTS " , True ) ;
2019-05-09 18:12:31 +02:00
if ( prop ! = None ) {
Atom type ;
int format ;
unsigned long len ;
unsigned long remaining ;
unsigned char * data = NULL ;
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 ) ;
2019-05-01 09:49:10 +02:00
}
2019-03-12 18:09:16 +01:00
}
}
XMoveWindow ( x11_display , x11_window , p_position . x - x , p_position . y - y ) ;
2018-09-09 22:13:50 +02:00
update_real_mouse_position ( ) ;
2015-01-11 08:47:27 +01:00
}
2015-01-10 14:50:31 +01:00
2015-01-11 10:36:56 +01:00
Size2 OS_X11 : : get_window_size ( ) const {
2017-01-30 17:25:48 +01:00
// 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 ) ;
2015-01-11 10:36:56 +01:00
}
2018-02-12 18:17:29 +01:00
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 ) ;
2019-05-09 18:12:31 +02:00
if ( prop ! = None ) {
Atom type ;
int format ;
unsigned long len ;
unsigned long remaining ;
unsigned char * data = NULL ;
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 ) ;
2019-05-01 09:49:10 +02:00
}
2018-02-12 18:17:29 +01:00
}
return Size2 ( w , h ) ;
}
2019-06-13 14:31:08 +02:00
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 ) ) ) {
2019-07-30 14:50:52 +02:00
ERR_PRINT ( " Minimum window size can't be larger than maximum window size! " ) ;
2019-06-13 14:31:08 +02:00
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 ) ) ) {
2019-07-30 14:50:52 +02:00
ERR_PRINT ( " Maximum window size can't be smaller than minimum window size! " ) ;
2019-06-13 14:31:08 +02:00
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 ) ;
}
}
2015-01-11 10:36:56 +01:00
void OS_X11 : : set_window_size ( const Size2 p_size ) {
2018-09-10 00:08:21 +02:00
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 ;
2017-01-30 17:25:48 +01:00
// If window resizable is disabled we need to update the attributes first
2019-06-13 14:31:08 +02:00
XSizeHints * xsh ;
xsh = XAllocSizeHints ( ) ;
2018-10-06 22:20:41 +02:00
if ( ! is_window_resizable ( ) ) {
2017-01-30 17:25:48 +01:00
xsh - > flags = PMinSize | PMaxSize ;
xsh - > min_width = p_size . x ;
xsh - > max_width = p_size . x ;
xsh - > min_height = p_size . y ;
xsh - > max_height = p_size . y ;
2019-06-13 14:31:08 +02:00
} 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 ;
}
2017-01-30 17:25:48 +01:00
}
2019-06-13 14:31:08 +02:00
XSetWMNormalHints ( x11_display , x11_window , xsh ) ;
XFree ( xsh ) ;
2017-01-30 17:25:48 +01:00
// Resize the window
2015-01-11 10:36:56 +01:00
XResizeWindow ( x11_display , x11_window , p_size . x , p_size . y ) ;
2017-01-30 17:25:48 +01:00
// Update our videomode width and height
current_videomode . width = p_size . x ;
current_videomode . height = p_size . y ;
2018-09-10 00:08:21 +02:00
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 ) ;
}
2015-01-11 10:36:56 +01:00
}
2015-03-22 23:00:50 +01:00
void OS_X11 : : set_window_fullscreen ( bool p_enabled ) {
2017-12-10 19:38:26 +01:00
2017-12-27 20:51:19 +01:00
if ( current_videomode . fullscreen = = p_enabled )
return ;
2017-12-10 19:38:26 +01:00
if ( layered_window )
set_window_per_pixel_transparency_enabled ( false ) ;
2017-12-27 20:51:19 +01:00
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 ) ;
}
2015-01-17 11:43:12 +01:00
set_wm_fullscreen ( p_enabled ) ;
2020-02-03 15:15:24 +01:00
if ( ! p_enabled & & current_videomode . always_on_top ) {
2017-12-27 20:51:19 +01:00
// Restore
set_window_maximized ( false ) ;
}
2020-02-03 16:14:41 +01:00
if ( ! p_enabled ) {
set_window_position ( last_position_before_fs ) ;
} else {
last_position_before_fs = get_window_position ( ) ;
}
2015-01-17 11:43:12 +01:00
current_videomode . fullscreen = p_enabled ;
2015-01-10 08:47:34 +01:00
}
2015-03-22 23:00:50 +01:00
bool OS_X11 : : is_window_fullscreen ( ) const {
2015-01-10 09:01:01 +01:00
return current_videomode . fullscreen ;
2015-01-10 08:47:34 +01:00
}
2015-01-15 14:50:23 +01:00
2015-03-22 23:00:50 +01:00
void OS_X11 : : set_window_resizable ( bool p_enabled ) {
2017-01-30 17:25:48 +01:00
2019-06-13 14:31:08 +02:00
XSizeHints * xsh ;
2015-01-17 11:43:12 +01:00
xsh = XAllocSizeHints ( ) ;
2017-03-05 16:44:50 +01:00
if ( ! p_enabled ) {
2019-06-13 14:31:08 +02:00
Size2 size = get_window_size ( ) ;
xsh - > flags = PMinSize | PMaxSize ;
2017-01-30 17:25:48 +01:00
xsh - > min_width = size . x ;
xsh - > max_width = size . x ;
xsh - > min_height = size . y ;
xsh - > max_height = size . y ;
2019-06-13 14:31:08 +02:00
} 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 ;
}
2015-01-15 14:50:23 +01:00
}
2019-06-13 14:31:08 +02:00
2015-01-17 11:43:12 +01:00
XSetWMNormalHints ( x11_display , x11_window , xsh ) ;
XFree ( xsh ) ;
2019-06-13 14:31:08 +02:00
2015-01-17 11:43:12 +01:00
current_videomode . resizable = p_enabled ;
2019-06-13 14:31:08 +02:00
XFlush ( x11_display ) ;
2015-01-15 14:50:23 +01:00
}
2015-03-22 23:00:50 +01:00
bool OS_X11 : : is_window_resizable ( ) const {
2015-01-15 14:50:23 +01:00
return current_videomode . resizable ;
}
2015-01-16 16:18:45 +01:00
2015-03-22 23:00:50 +01:00
void OS_X11 : : set_window_minimized ( bool p_enabled ) {
2016-07-21 21:11:34 +02:00
// Using ICCCM -- Inter-Client Communication Conventions Manual
XEvent xev ;
Atom wm_change = XInternAtom ( x11_display , " WM_CHANGE_STATE " , False ) ;
2015-01-16 16:18:45 +01:00
2016-07-21 21:11:34 +02:00
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 ;
2015-01-16 16:18:45 +01:00
2016-07-21 21:11:34 +02:00
XSendEvent ( x11_display , DefaultRootWindow ( x11_display ) , False , SubstructureRedirectMask | SubstructureNotifyMask , & xev ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
Atom wm_state = XInternAtom ( x11_display , " _NET_WM_STATE " , False ) ;
Atom wm_hidden = XInternAtom ( x11_display , " _NET_WM_STATE_HIDDEN " , False ) ;
2015-01-16 18:36:07 +01:00
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 ;
2016-03-09 00:00:52 +01:00
XSendEvent ( x11_display , DefaultRootWindow ( x11_display ) , False , SubstructureRedirectMask | SubstructureNotifyMask , & xev ) ;
2015-01-16 16:18:45 +01:00
}
2015-03-22 23:00:50 +01:00
bool OS_X11 : : is_window_minimized ( ) const {
2015-01-16 16:18:45 +01:00
// Using ICCCM -- Inter-Client Communication Conventions Manual
2017-03-05 16:44:50 +01:00
Atom property = XInternAtom ( x11_display , " WM_STATE " , True ) ;
Atom type ;
int format ;
unsigned long len ;
unsigned long remaining ;
unsigned char * data = NULL ;
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 )
2015-01-16 16:18:45 +01:00
return true ;
}
return false ;
}
2015-03-22 23:00:50 +01:00
void OS_X11 : : set_window_maximized ( bool p_enabled ) {
2017-10-24 21:35:28 +02:00
if ( is_window_maximized ( ) = = p_enabled )
return ;
2016-03-09 00:00:52 +01:00
// Using EWMH -- Extended Window Manager Hints
2015-01-16 16:18:45 +01:00
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 ;
2015-01-16 18:36:07 +01:00
XSendEvent ( x11_display , DefaultRootWindow ( x11_display ) , False , SubstructureRedirectMask | SubstructureNotifyMask , & xev ) ;
2016-01-03 22:52:42 +01:00
2019-08-10 21:09:21 +02:00
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 ) ;
2017-12-27 11:25:57 +01:00
}
2017-12-24 00:59:51 +01:00
}
2015-01-16 16:18:45 +01:00
maximized = p_enabled ;
}
2017-12-27 11:25:57 +01:00
bool OS_X11 : : is_window_maximize_allowed ( ) {
Atom property = XInternAtom ( x11_display , " _NET_WM_ALLOWED_ACTIONS " , False ) ;
Atom type ;
int format ;
unsigned long len ;
unsigned long remaining ;
unsigned char * data = NULL ;
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 = XInternAtom ( x11_display , " _NET_WM_ACTION_MAXIMIZE_HORZ " , False ) ;
Atom 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 ;
2019-09-22 18:45:08 +02:00
for ( uint64_t i = 0 ; i < len ; i + + ) {
2017-12-27 11:25:57 +01:00
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 )
return true ;
}
XFree ( atoms ) ;
}
return false ;
}
2015-03-22 23:00:50 +01:00
bool OS_X11 : : is_window_maximized ( ) const {
2015-01-16 16:18:45 +01:00
// Using EWMH -- Extended Window Manager Hints
2017-03-05 16:44:50 +01:00
Atom property = XInternAtom ( x11_display , " _NET_WM_STATE " , False ) ;
2016-07-21 21:11:34 +02:00
Atom type ;
int format ;
unsigned long len ;
unsigned long remaining ;
unsigned char * data = NULL ;
2018-02-28 21:55:13 +01:00
bool retval = false ;
2015-01-16 16:18:45 +01:00
2016-07-21 21:11:34 +02:00
int result = XGetWindowProperty (
x11_display ,
x11_window ,
property ,
0 ,
1024 ,
False ,
XA_ATOM ,
& type ,
& format ,
& len ,
& remaining ,
2017-03-05 16:44:50 +01:00
& data ) ;
2015-01-16 16:18:45 +01:00
2017-03-05 16:44:50 +01:00
if ( result = = Success ) {
Atom * atoms = ( Atom * ) data ;
2015-01-16 16:18:45 +01:00
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 ) ;
bool found_wm_max_horz = false ;
bool found_wm_max_vert = false ;
2019-09-22 18:45:08 +02:00
for ( uint64_t i = 0 ; i < len ; i + + ) {
2017-03-05 16:44:50 +01:00
if ( atoms [ i ] = = wm_max_horz )
2015-01-16 16:18:45 +01:00
found_wm_max_horz = true ;
2017-03-05 16:44:50 +01:00
if ( atoms [ i ] = = wm_max_vert )
2015-01-16 16:18:45 +01:00
found_wm_max_vert = true ;
2018-02-28 21:55:13 +01:00
if ( found_wm_max_horz & & found_wm_max_vert ) {
retval = true ;
break ;
}
2015-01-16 16:18:45 +01:00
}
}
2018-02-28 21:55:13 +01:00
XFree ( data ) ;
return retval ;
2015-01-16 16:18:45 +01:00
}
2015-03-22 23:00:50 +01:00
2017-12-27 20:51:19 +01:00
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 ;
}
2019-11-28 13:41:07 +01:00
bool OS_X11 : : is_window_focused ( ) const {
return window_focused ;
}
2017-12-14 19:15:46 +01:00
void OS_X11 : : set_borderless_window ( bool p_borderless ) {
2017-07-05 17:19:24 +02:00
2019-06-11 11:07:48 +02:00
if ( get_borderless_window ( ) = = p_borderless )
2017-07-05 17:19:24 +02:00
return ;
2017-12-10 19:38:26 +01:00
if ( ! p_borderless & & layered_window )
set_window_per_pixel_transparency_enabled ( false ) ;
2017-07-05 17:19:24 +02:00
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 ) ;
XChangeProperty ( x11_display , x11_window , property , property , 32 , PropModeReplace , ( unsigned char * ) & hints , 5 ) ;
2018-05-23 21:25:41 +02:00
// Preserve window size
set_window_size ( Size2 ( current_videomode . width , current_videomode . height ) ) ;
2017-07-05 17:19:24 +02:00
}
bool OS_X11 : : get_borderless_window ( ) {
2019-06-11 11:07:48 +02:00
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 = NULL ;
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 ;
2017-07-05 17:19:24 +02:00
}
2016-07-21 19:40:36 +02:00
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
2018-08-16 21:36:37 +02:00
2016-07-21 19:40:36 +02:00
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 ) ;
2018-08-16 21:36:37 +02:00
XFlush ( x11_display ) ;
2016-07-21 19:40:36 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
void OS_X11 : : get_key_modifier_state ( unsigned int p_x11_state , Ref < InputEventWithModifiers > state ) {
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
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 ) ) ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 15:12:13 +02:00
unsigned int OS_X11 : : get_mouse_button_state ( unsigned int p_x11_button , int p_x11_type ) {
2014-02-10 02:10:30 +01:00
2018-07-08 15:12:13 +02:00
unsigned int mask = 1 < < ( p_x11_button - 1 ) ;
2016-03-09 00:00:52 +01:00
2018-07-08 15:12:13 +02:00
if ( p_x11_type = = ButtonPress ) {
last_button_state | = mask ;
} else {
last_button_state & = ~ mask ;
2014-02-10 02:10:30 +01:00
}
2018-07-08 15:12:13 +02:00
return last_button_state ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2014-05-24 06:35:47 +02:00
void OS_X11 : : handle_key_event ( XKeyEvent * p_event , bool p_echo ) {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// X11 functions don't know what const is
XKeyEvent * xkeyevent = p_event ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// This code was pretty difficult to write.
// The docs stink and every toolkit seems to
2016-03-09 00:00:52 +01:00
// do it in a different way.
2014-02-10 02:10:30 +01:00
/* Phase 1, obtain a proper keysym */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// This was also very difficult to figure out.
// You'd expect you could just use Keysym provided by
2016-03-09 00:00:52 +01:00
// XKeycodeToKeysym to obtain internationalized
// input.. WRONG!!
2014-02-10 02:10:30 +01:00
// you must use XLookupString (???) which not only wastes
2017-03-24 21:45:31 +01:00
// cycles generating an unnecessary string, but also
2014-02-10 02:10:30 +01:00
// 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 chosse which of both results
// you want to keep.
// This is a real bizarreness and cpu waster.
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
KeySym keysym_keycode = 0 ; // keysym used to find a keycode
KeySym keysym_unicode = 0 ; // keysym used to find unicode
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// XLookupString returns keysyms usable as nice scancodes/
2017-03-05 16:44:50 +01:00
char str [ 256 + 1 ] ;
2019-08-28 12:27:13 +02:00
XKeyEvent xkeyevent_no_mod = * xkeyevent ;
xkeyevent_no_mod . state & = ~ ShiftMask ;
xkeyevent_no_mod . state & = ~ ControlMask ;
2019-10-11 23:09:14 +02:00
XLookupString ( xkeyevent , str , 256 , & keysym_unicode , NULL ) ;
XLookupString ( & xkeyevent_no_mod , NULL , 0 , & keysym_keycode , NULL ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
// Meanwhile, XLookupString returns keysyms useful for unicode.
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( ! xmbstring ) {
// keep a temporary buffer for the string
2017-03-05 16:44:50 +01:00
xmbstring = ( char * ) memalloc ( sizeof ( char ) * 8 ) ;
xmblen = 8 ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
if ( xkeyevent - > type = = KeyPress & & xic ) {
Status status ;
2017-06-27 11:26:43 +02:00
# 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 ) ;
if ( keycode > = ' a ' & & keycode < = ' z ' )
keycode - = ' a ' - ' A ' ;
String tmp ;
tmp . parse_utf8 ( utf8string , utf8bytes ) ;
for ( int i = 0 ; i < tmp . length ( ) ; i + + ) {
Ref < InputEventKey > k ;
k . instance ( ) ;
if ( keycode = = 0 & & tmp [ i ] = = 0 ) {
continue ;
}
get_key_modifier_state ( xkeyevent - > state , k ) ;
k - > set_unicode ( tmp [ i ] ) ;
k - > set_pressed ( keypress ) ;
k - > set_scancode ( keycode ) ;
k - > set_echo ( false ) ;
if ( k - > get_scancode ( ) = = KEY_BACKTAB ) {
//make it consistent across platforms.
k - > set_scancode ( KEY_TAB ) ;
k - > set_shift ( true ) ;
}
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( k ) ;
2017-06-27 11:26:43 +02:00
}
2019-11-12 07:51:51 +01:00
memfree ( utf8string ) ;
2017-06-27 11:26:43 +02:00
return ;
}
memfree ( utf8string ) ;
# else
2017-06-24 15:09:39 +02:00
do {
2017-06-26 01:09:16 +02:00
2017-03-05 16:44:50 +01:00
int mnbytes = XmbLookupString ( xic , xkeyevent , xmbstring , xmblen - 1 , & keysym_unicode , & status ) ;
2014-02-10 02:10:30 +01:00
xmbstring [ mnbytes ] = ' \0 ' ;
if ( status = = XBufferOverflow ) {
xmblen = mnbytes + 1 ;
2017-03-05 16:44:50 +01:00
xmbstring = ( char * ) memrealloc ( xmbstring , xmblen ) ;
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
} while ( status = = XBufferOverflow ) ;
2017-06-27 11:26:43 +02:00
# endif
2016-03-09 00:00:52 +01:00
}
2014-02-10 02:10:30 +01:00
/* Phase 2, obtain a pigui keycode from the keysym */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// 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 ) ;
2016-03-09 00:00:52 +01:00
2017-09-02 16:19:06 +02:00
/* Phase 3, obtain a unicode character from the keysym */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// KeyMappingX11 also translates keysym to unicode.
// It does a binary search on a table to translate
2016-03-09 00:00:52 +01:00
// most properly.
2017-03-05 16:44:50 +01:00
unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11 : : get_unicode_from_keysym ( keysym_unicode ) : 0 ;
2014-02-10 02:10:30 +01:00
/* Phase 4, determine if event must be filtered */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// This seems to be a side-effect of using XIM.
2017-03-24 21:45:31 +01:00
// XEventFilter looks like a core X11 function,
2014-02-10 02:10:30 +01:00
// 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 ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( keycode = = 0 & & unicode = = 0 )
2014-02-10 02:10:30 +01:00
return ;
/* Phase 5, determine modifier mask */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// 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.
2018-08-24 09:35:07 +02:00
//print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask));
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > k ;
k . instance ( ) ;
get_key_modifier_state ( xkeyevent - > state , k ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Phase 6, determine echo character */
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// Echo characters in X11 are a keyrelease and a keypress
// one after the other with the (almot) same timestamp.
// To detect them, i use XPeekEvent and check that their
2017-07-08 17:12:18 +02:00
// difference in time is below a threshold.
2016-03-09 00:00:52 +01:00
2014-05-24 06:35:47 +02:00
if ( xkeyevent - > type ! = KeyPress ) {
2016-03-09 00:00:52 +01:00
2017-06-27 11:26:43 +02:00
p_echo = false ;
2014-02-10 02:10:30 +01:00
// make sure there are events pending,
// so this call won't block.
2017-03-05 16:44:50 +01:00
if ( XPending ( x11_display ) > 0 ) {
2014-02-10 02:10:30 +01:00
XEvent peek_event ;
XPeekEvent ( x11_display , & peek_event ) ;
2016-03-09 00:00:52 +01:00
2017-07-08 17:12:18 +02:00
// I'm using a threshold of 5 msecs,
2014-02-10 02:10:30 +01:00
// 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.
2016-03-09 00:00:52 +01:00
2019-02-20 21:59:03 +01:00
: : Time tresh = ABSDIFF ( peek_event . xkey . time , xkeyevent - > time ) ;
2017-03-05 16:44:50 +01:00
if ( peek_event . type = = KeyPress & & tresh < 5 ) {
2014-05-24 06:35:47 +02:00
KeySym rk ;
2017-03-05 16:44:50 +01:00
XLookupString ( ( XKeyEvent * ) & peek_event , str , 256 , & rk , NULL ) ;
if ( rk = = keysym_keycode ) {
2014-05-24 06:35:47 +02:00
XEvent event ;
XNextEvent ( x11_display , & event ) ; //erase next event
2017-03-05 16:44:50 +01:00
handle_key_event ( ( XKeyEvent * ) & event , true ) ;
2014-05-24 06:35:47 +02:00
return ; //ignore current, echo next
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
// use the time from peek_event so it always works
}
2016-03-09 00:00:52 +01:00
// save the time to check for echo when keypress happens
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
/* Phase 7, send event to Window */
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
k - > set_pressed ( keypress ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( keycode > = ' a ' & & keycode < = ' z ' )
keycode - = ' a ' - ' A ' ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
k - > set_scancode ( keycode ) ;
k - > set_unicode ( unicode ) ;
k - > set_echo ( p_echo ) ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_scancode ( ) = = KEY_BACKTAB ) {
2017-03-24 21:45:31 +01:00
//make it consistent across platforms.
2017-05-20 17:38:03 +02:00
k - > set_scancode ( KEY_TAB ) ;
k - > set_shift ( true ) ;
2014-02-10 02:10:30 +01:00
}
2016-09-22 12:24:44 +02:00
//don't set mod state if modifier keys are released by themselves
//else event.is_action() will not work correctly here
2017-05-20 17:38:03 +02:00
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 ) ;
2016-09-22 12:24:44 +02:00
}
2017-06-27 11:26:43 +02:00
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 ) ;
}
}
2017-05-20 17:38:03 +02:00
//printf("key: %x\n",k->get_scancode());
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( k ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
struct Property {
2016-05-29 17:40:08 +02:00
unsigned char * data ;
int format , nitems ;
Atom type ;
} ;
2017-03-05 16:44:50 +01:00
static Property read_property ( Display * p_display , Window p_window , Atom p_property ) {
2016-05-29 17:40:08 +02:00
Atom actual_type ;
int actual_format ;
unsigned long nitems ;
unsigned long bytes_after ;
2017-03-05 16:44:50 +01:00
unsigned char * ret = 0 ;
2016-05-29 17:40:08 +02:00
int read_bytes = 1024 ;
//Keep trying to read the property until there are no
//bytes unread.
2017-03-05 16:44:50 +01:00
do {
if ( ret ! = 0 )
2016-05-29 17:40:08 +02:00
XFree ( ret ) ;
XGetWindowProperty ( p_display , p_window , p_property , 0 , read_bytes , False , AnyPropertyType ,
2017-03-05 16:44:50 +01:00
& actual_type , & actual_format , & nitems , & bytes_after ,
& ret ) ;
2016-05-29 17:40:08 +02:00
read_bytes * = 2 ;
2017-03-05 16:44:50 +01:00
} while ( bytes_after ! = 0 ) ;
2016-05-29 17:40:08 +02:00
2017-03-05 16:44:50 +01:00
Property p = { ret , actual_format , ( int ) nitems , actual_type } ;
2016-05-29 17:40:08 +02:00
return p ;
}
2017-03-05 16:44:50 +01:00
static Atom pick_target_from_list ( Display * p_display , Atom * p_list , int p_count ) {
2016-05-29 17:40:08 +02:00
2017-03-05 16:44:50 +01:00
static const char * target_type = " text/uri-list " ;
2016-05-29 17:40:08 +02:00
for ( int i = 0 ; i < p_count ; i + + ) {
Atom atom = p_list [ i ] ;
if ( atom ! = None & & String ( XGetAtomName ( p_display , atom ) ) = = target_type )
2017-03-05 16:44:50 +01:00
return atom ;
2016-05-29 17:40:08 +02:00
}
return None ;
}
2017-03-05 16:44:50 +01:00
static Atom pick_target_from_atoms ( Display * p_disp , Atom p_t1 , Atom p_t2 , Atom p_t3 ) {
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
static const char * target_type = " text/uri-list " ;
2016-05-28 21:35:42 +02:00
if ( p_t1 ! = None & & String ( XGetAtomName ( p_disp , p_t1 ) ) = = target_type )
return p_t1 ;
if ( p_t2 ! = None & & String ( XGetAtomName ( p_disp , p_t2 ) ) = = target_type )
return p_t2 ;
if ( p_t3 ! = None & & String ( XGetAtomName ( p_disp , p_t3 ) ) = = target_type )
return p_t3 ;
return None ;
}
2017-10-24 21:35:28 +02:00
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 ;
}
2014-02-10 02:10:30 +01:00
void OS_X11 : : process_xevents ( ) {
//printf("checking events %i\n", XPending(x11_display));
2017-03-05 16:44:50 +01:00
do_mouse_warp = false ;
2017-01-25 19:21:41 +01:00
// Is the current mouse mode one where it needs to be grabbed.
2017-03-05 16:44:50 +01:00
bool mouse_mode_grab = mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED ;
2017-01-25 19:21:41 +01:00
2014-02-10 02:10:30 +01:00
while ( XPending ( x11_display ) > 0 ) {
XEvent event ;
XNextEvent ( x11_display , & event ) ;
2017-06-27 11:26:43 +02:00
if ( XFilterEvent ( & event , None ) ) {
continue ;
}
2017-11-29 21:01:26 +01:00
if ( XGetEventData ( x11_display , & event . xcookie ) ) {
2018-08-18 00:59:26 +02:00
if ( event . xcookie . type = = GenericEvent & & event . xcookie . extension = = xi . opcode ) {
2017-11-29 21:01:26 +01:00
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 ) {
2018-08-18 00:59:26 +02:00
case XI_HierarchyChanged :
case XI_DeviceChanged : {
refresh_device_info ( ) ;
} break ;
case XI_RawMotion : {
XIRawEvent * raw_event = ( XIRawEvent * ) event_data ;
int device_id = raw_event - > deviceid ;
// 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.
2018-12-19 18:19:05 +01:00
if ( raw_event - > valuators . mask_len < = 0 ) {
2018-08-18 00:59:26 +02:00
break ;
}
2018-12-19 18:19:05 +01:00
const double * values = raw_event - > raw_values ;
double rel_x = 0.0 ;
double rel_y = 0.0 ;
2018-12-13 21:32:11 +01:00
double pressure = 0.0 ;
double tilt_x = 0.0 ;
double tilt_y = 0.0 ;
2018-12-19 18:19:05 +01:00
2018-12-13 21:32:11 +01:00
if ( XIMaskIsSet ( raw_event - > valuators . mask , VALUATOR_ABSX ) ) {
2018-12-19 18:19:05 +01:00
rel_x = * values ;
values + + ;
}
2018-12-13 21:32:11 +01:00
if ( XIMaskIsSet ( raw_event - > valuators . mask , VALUATOR_ABSY ) ) {
2018-12-19 18:19:05 +01:00
rel_y = * values ;
2018-12-13 21:32:11 +01:00
values + + ;
}
if ( XIMaskIsSet ( raw_event - > valuators . mask , VALUATOR_PRESSURE ) ) {
pressure = * values ;
values + + ;
}
if ( XIMaskIsSet ( raw_event - > valuators . mask , VALUATOR_TILTX ) ) {
tilt_x = * values ;
values + + ;
}
if ( XIMaskIsSet ( raw_event - > valuators . mask , VALUATOR_TILTY ) ) {
tilt_y = * values ;
}
Map < int , Vector3 > : : Element * pen_info = xi . pen_devices . find ( device_id ) ;
if ( pen_info ) {
Vector3 mult = pen_info - > value ( ) ;
if ( mult . x ! = 0.0 ) xi . pressure = pressure / mult . x ;
if ( ( mult . y ! = 0.0 ) & & ( mult . z ! = 0.0 ) ) xi . tilt = Vector2 ( tilt_x / mult . y , tilt_y / mult . z ) ;
2018-12-19 18:19:05 +01:00
}
2018-08-18 00:59:26 +02:00
// 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 < int , Vector2 > : : 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 ;
}
2017-11-29 21:01:26 +01:00
2018-08-18 00:59:26 +02:00
xi . last_relative_time = raw_event - > time ;
} break ;
# ifdef TOUCH_ENABLED
2017-11-29 21:01:26 +01:00
case XI_TouchBegin : // Fall-through
2017-12-10 07:05:17 +01:00
// Disabled hand-in-hand with the grabbing
//XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch);
2017-11-29 21:01:26 +01:00
case XI_TouchEnd : {
bool is_begin = event_data - > evtype = = XI_TouchBegin ;
Ref < InputEventScreenTouch > st ;
st . instance ( ) ;
st - > set_index ( index ) ;
st - > set_position ( pos ) ;
st - > set_pressed ( is_begin ) ;
if ( is_begin ) {
2018-08-18 00:59:26 +02:00
if ( xi . state . has ( index ) ) // Defensive
2017-11-29 21:01:26 +01:00
break ;
2018-08-18 00:59:26 +02:00
xi . state [ index ] = pos ;
if ( xi . state . size ( ) = = 1 ) {
2018-02-24 03:04:30 +01:00
// 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
2018-08-18 00:59:26 +02:00
xi . mouse_pos_to_filter = pos ;
2018-02-24 03:04:30 +01:00
}
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( st ) ;
2017-11-29 21:01:26 +01:00
} else {
2018-08-18 00:59:26 +02:00
if ( ! xi . state . has ( index ) ) // Defensive
2017-11-29 21:01:26 +01:00
break ;
2018-08-18 00:59:26 +02:00
xi . state . erase ( index ) ;
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( st ) ;
2017-11-29 21:01:26 +01:00
}
} break ;
case XI_TouchUpdate : {
2018-08-18 00:59:26 +02:00
Map < int , Vector2 > : : Element * curr_pos_elem = xi . state . find ( index ) ;
2017-11-29 21:01:26 +01:00
if ( ! curr_pos_elem ) { // Defensive
break ;
}
if ( curr_pos_elem - > value ( ) ! = pos ) {
Ref < InputEventScreenDrag > sd ;
sd . instance ( ) ;
sd - > set_index ( index ) ;
sd - > set_position ( pos ) ;
sd - > set_relative ( pos - curr_pos_elem - > value ( ) ) ;
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( sd ) ;
2017-11-29 21:01:26 +01:00
curr_pos_elem - > value ( ) = pos ;
}
} break ;
2018-08-18 00:59:26 +02:00
# endif
2017-11-29 21:01:26 +01:00
}
}
}
2017-12-10 07:05:17 +01:00
XFreeEventData ( x11_display , & event . xcookie ) ;
2017-11-29 21:01:26 +01:00
2014-02-10 02:10:30 +01:00
switch ( event . type ) {
2017-03-05 16:44:50 +01:00
case Expose :
Main : : force_redraw ( ) ;
break ;
2015-09-24 23:06:15 +02:00
2017-03-05 16:44:50 +01:00
case NoExpose :
minimized = true ;
break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case VisibilityNotify : {
XVisibilityEvent * visibility = ( XVisibilityEvent * ) & event ;
minimized = ( visibility - > state = = VisibilityFullyObscured ) ;
} break ;
case LeaveNotify : {
if ( main_loop & & ! mouse_mode_grab )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_EXIT ) ;
} break ;
case EnterNotify : {
if ( main_loop & & ! mouse_mode_grab )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_ENTER ) ;
} break ;
case FocusIn :
minimized = false ;
window_has_focus = true ;
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_FOCUS_IN ) ;
2019-11-28 13:41:07 +01:00
window_focused = true ;
2017-03-05 16:44:50 +01:00
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 ) // 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 ) ;
2017-01-25 19:21:41 +01:00
}
2017-11-29 21:01:26 +01:00
# ifdef TOUCH_ENABLED
2018-07-25 03:11:03 +02:00
// Grab touch devices to avoid OS gesture interference
2018-08-18 00:59:26 +02:00
/*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 ) ;
2017-12-10 07:05:17 +01:00
} */
2017-11-29 21:01:26 +01:00
# endif
2017-06-27 11:26:43 +02:00
if ( xic ) {
XSetICFocus ( xic ) ;
}
2014-02-10 02:10:30 +01:00
break ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
case FocusOut :
window_has_focus = false ;
2019-04-15 19:41:44 +02:00
input - > release_pressed_events ( ) ;
2017-03-05 16:44:50 +01:00
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_FOCUS_OUT ) ;
2019-11-28 13:41:07 +01:00
window_focused = false ;
2019-04-15 19:41:44 +02:00
2017-03-05 16:44:50 +01:00
if ( mouse_mode_grab ) {
//dear X11, I try, I really try, but you never work, you do whathever 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 ) ;
}
2017-11-29 21:01:26 +01:00
# ifdef TOUCH_ENABLED
// Ungrab touch devices so input works as usual while we are unfocused
2018-08-18 00:59:26 +02:00
/*for (int i = 0; i < xi.touch_devices.size(); ++i) {
XIUngrabDevice ( x11_display , xi . touch_devices [ i ] , CurrentTime ) ;
2017-12-10 07:05:17 +01:00
} */
2017-11-29 21:01:26 +01:00
// Release every pointer to avoid sticky points
2018-08-18 00:59:26 +02:00
for ( Map < int , Vector2 > : : Element * E = xi . state . front ( ) ; E ; E = E - > next ( ) ) {
2017-11-29 21:01:26 +01:00
Ref < InputEventScreenTouch > st ;
st . instance ( ) ;
st - > set_index ( E - > key ( ) ) ;
st - > set_position ( E - > get ( ) ) ;
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( st ) ;
2017-11-29 21:01:26 +01:00
}
2018-08-18 00:59:26 +02:00
xi . state . clear ( ) ;
2017-11-29 21:01:26 +01:00
# endif
2017-06-27 11:26:43 +02:00
if ( xic ) {
XUnsetICFocus ( xic ) ;
}
2017-03-05 16:44:50 +01:00
break ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
case ConfigureNotify :
2017-10-24 21:35:28 +02:00
_window_changed ( & event ) ;
2017-03-05 16:44:50 +01:00
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 ;
}
2016-03-09 00:00:52 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseButton > mb ;
mb . instance ( ) ;
get_key_modifier_state ( event . xbutton . state , mb ) ;
2018-12-15 14:10:06 +01:00
mb - > set_button_index ( event . xbutton . button ) ;
2017-05-20 17:38:03 +02:00
if ( mb - > get_button_index ( ) = = 2 )
mb - > set_button_index ( 3 ) ;
else if ( mb - > get_button_index ( ) = = 3 )
mb - > set_button_index ( 2 ) ;
2018-12-15 14:10:06 +01:00
mb - > set_button_mask ( get_mouse_button_state ( mb - > get_button_index ( ) , event . xbutton . type ) ) ;
2018-07-08 15:12:13 +02:00
mb - > set_position ( Vector2 ( event . xbutton . x , event . xbutton . y ) ) ;
mb - > set_global_position ( mb - > get_position ( ) ) ;
2017-05-20 17:38:03 +02:00
mb - > set_pressed ( ( event . type = = ButtonPress ) ) ;
2016-03-09 00:00:52 +01:00
2018-07-20 18:16:09 +02:00
if ( event . type = = ButtonPress ) {
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
uint64_t diff = get_ticks_usec ( ) / 1000 - last_click_ms ;
2014-02-10 02:10:30 +01:00
2018-07-20 18:16:09 +02:00
if ( mb - > get_button_index ( ) = = last_click_button_index ) {
2014-02-10 02:10:30 +01:00
2018-07-20 18:16:09 +02:00
if ( diff < 400 & & Point2 ( last_click_pos ) . distance_to ( Point2 ( event . xbutton . x , event . xbutton . y ) ) < 5 ) {
2016-03-09 00:00:52 +01:00
2018-07-20 18:16:09 +02:00
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 ( ) ) {
2017-03-05 16:44:50 +01:00
last_click_ms + = diff ;
last_click_pos = Point2 ( event . xbutton . x , event . xbutton . y ) ;
}
2015-08-29 06:43:21 +02:00
}
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( mb ) ;
2017-03-05 16:44:50 +01:00
} break ;
case MotionNotify : {
2018-10-02 00:30:11 +02:00
// 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.
2017-03-05 16:44:50 +01:00
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 ( XPending ( x11_display ) > 0 ) {
XEvent tevent ;
XPeekEvent ( x11_display , & tevent ) ;
if ( tevent . type = = MotionNotify ) {
XNextEvent ( x11_display , & event ) ;
} else {
break ;
}
2015-08-29 06:43:21 +02:00
} else {
break ;
}
}
2017-03-05 16:44:50 +01:00
last_timestamp = event . xmotion . time ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
// Motion is also simple.
// A little hack is in order
// to be able to send relative motion events.
2018-08-18 00:59:26 +02:00
Point2 pos ( event . xmotion . x , event . xmotion . y ) ;
2014-02-10 02:10:30 +01:00
2018-02-24 03:04:30 +01:00
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false ;
// Adding some tolerance to match better Point2i to Vector2
2018-08-18 00:59:26 +02:00
if ( xi . state . size ( ) & & Vector2 ( pos ) . distance_squared_to ( xi . mouse_pos_to_filter ) < 2 ) {
2018-02-24 03:04:30 +01:00
filter = true ;
}
// Invalidate to avoid filtering a possible legitimate similar event coming later
2018-08-18 00:59:26 +02:00
xi . mouse_pos_to_filter = Vector2 ( 1e10 , 1e10 ) ;
2018-02-24 03:04:30 +01:00
if ( filter ) {
break ;
}
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
2018-08-18 00:59:26 +02:00
if ( xi . relative_motion . x = = 0 & & xi . relative_motion . y = = 0 ) {
2017-03-05 16:44:50 +01:00
break ;
}
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Point2i new_center = pos ;
2018-08-18 00:59:26 +02:00
pos = last_mouse_pos + xi . relative_motion ;
2017-03-05 16:44:50 +01:00
center = new_center ;
do_mouse_warp = window_has_focus ; // warp the cursor if we're focused in
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( ! last_mouse_pos_valid ) {
2015-02-12 15:58:00 +01:00
2017-03-05 16:44:50 +01:00
last_mouse_pos = pos ;
last_mouse_pos_valid = true ;
}
2016-03-09 00:00:52 +01:00
2018-08-18 00:59:26 +02:00
// 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 ;
2017-03-05 16:44:50 +01:00
2018-09-04 15:11:37 +02:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
pos = Point2i ( current_videomode . width / 2 , current_videomode . height / 2 ) ;
}
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
2017-03-05 16:44:50 +01:00
2018-12-13 21:32:11 +01:00
mm - > set_pressure ( xi . pressure ) ;
mm - > set_tilt ( xi . tilt ) ;
2018-08-18 00:59:26 +02:00
// Make the absolute position integral so it doesn't look _too_ weird :)
Point2i posi ( pos ) ;
2017-05-20 17:38:03 +02:00
get_key_modifier_state ( event . xmotion . state , mm ) ;
2018-07-08 15:12:13 +02:00
mm - > set_button_mask ( get_mouse_button_state ( ) ) ;
2018-08-18 00:59:26 +02:00
mm - > set_position ( posi ) ;
mm - > set_global_position ( posi ) ;
input - > set_mouse_position ( posi ) ;
2017-05-20 17:38:03 +02:00
mm - > set_speed ( input - > get_last_mouse_speed ( ) ) ;
2018-08-18 00:59:26 +02:00
2017-05-20 17:38:03 +02:00
mm - > set_relative ( rel ) ;
2017-03-05 16:44:50 +01:00
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 )
2019-03-03 23:52:18 +01:00
input - > accumulate_input_event ( mm ) ;
2017-03-05 16:44:50 +01:00
} break ;
case KeyPress :
case KeyRelease : {
last_timestamp = event . xkey . time ;
// key event is a little complex, so
2018-10-25 02:19:21 +02:00
// it will be handled in its own function.
2017-03-05 16:44:50 +01:00
handle_key_event ( ( XKeyEvent * ) & event ) ;
} break ;
case SelectionRequest : {
XSelectionRequestEvent * req ;
XEvent e , respond ;
e = event ;
req = & ( e . xselectionrequest ) ;
2018-01-29 00:33:57 +01:00
if ( req - > target = = XInternAtom ( x11_display , " UTF8_STRING " , 0 ) | |
req - > target = = XInternAtom ( x11_display , " COMPOUND_TEXT " , 0 ) | |
req - > target = = XInternAtom ( x11_display , " TEXT " , 0 ) | |
req - > target = = XA_STRING | |
req - > target = = XInternAtom ( x11_display , " text/plain;charset=utf-8 " , 0 ) | |
req - > target = = XInternAtom ( x11_display , " text/plain " , 0 ) ) {
2017-03-05 16:44:50 +01:00
CharString clip = OS : : get_clipboard ( ) . utf8 ( ) ;
XChangeProperty ( x11_display ,
req - > requestor ,
req - > property ,
req - > target ,
8 ,
PropModeReplace ,
( unsigned char * ) clip . get_data ( ) ,
clip . length ( ) ) ;
respond . xselection . property = req - > property ;
} else if ( req - > target = = XInternAtom ( x11_display , " TARGETS " , 0 ) ) {
2018-01-29 00:33:57 +01:00
Atom data [ 7 ] ;
data [ 0 ] = XInternAtom ( x11_display , " TARGETS " , 0 ) ;
data [ 1 ] = XInternAtom ( x11_display , " UTF8_STRING " , 0 ) ;
data [ 2 ] = XInternAtom ( x11_display , " COMPOUND_TEXT " , 0 ) ;
data [ 3 ] = XInternAtom ( x11_display , " TEXT " , 0 ) ;
data [ 4 ] = XA_STRING ;
data [ 5 ] = XInternAtom ( x11_display , " text/plain;charset=utf-8 " , 0 ) ;
data [ 6 ] = XInternAtom ( x11_display , " text/plain " , 0 ) ;
XChangeProperty ( x11_display ,
req - > requestor ,
req - > property ,
XA_ATOM ,
32 ,
PropModeReplace ,
( unsigned char * ) & data ,
sizeof ( data ) / sizeof ( data [ 0 ] ) ) ;
2017-03-05 16:44:50 +01:00
respond . xselection . property = req - > property ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
} else {
2018-01-29 00:33:57 +01:00
char * targetname = XGetAtomName ( x11_display , req - > target ) ;
printf ( " No Target '%s' \n " , targetname ) ;
if ( targetname )
XFree ( targetname ) ;
2017-03-05 16:44:50 +01:00
respond . xselection . property = None ;
}
2018-01-29 00:33:57 +01:00
2017-03-05 16:44:50 +01:00
respond . xselection . type = SelectionNotify ;
respond . xselection . display = req - > display ;
respond . xselection . requestor = req - > requestor ;
respond . xselection . selection = req - > selection ;
respond . xselection . target = req - > target ;
respond . xselection . time = req - > time ;
2018-01-29 00:33:57 +01:00
XSendEvent ( x11_display , req - > requestor , True , NoEventMask , & respond ) ;
2017-03-05 16:44:50 +01:00
XFlush ( x11_display ) ;
} break ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
case SelectionNotify :
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( event . xselection . target = = requested ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Property p = read_property ( x11_display , x11_window , XInternAtom ( x11_display , " PRIMARY " , 0 ) ) ;
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
Vector < String > files = String ( ( char * ) p . data ) . split ( " \n " , false ) ;
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
2019-05-31 15:27:53 +02:00
files . write [ i ] = files [ i ] . replace ( " file:// " , " " ) . http_unescape ( ) . strip_edges ( ) ;
2017-03-05 16:44:50 +01:00
}
main_loop - > drop_files ( files ) ;
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
//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.
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
XSendEvent ( x11_display , xdnd_source_window , False , NoEventMask , ( XEvent * ) & m ) ;
2016-05-28 21:35:42 +02:00
}
2017-03-05 16:44:50 +01:00
break ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
case ClientMessage :
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
if ( ( unsigned int ) event . xclient . data . l [ 0 ] = = ( unsigned int ) wm_delete )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_QUIT_REQUEST ) ;
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
else if ( ( unsigned int ) event . xclient . message_type = = ( unsigned int ) xdnd_enter ) {
2016-05-28 21:35:42 +02:00
2017-03-05 16:44:50 +01:00
//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 ) ;
} 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
2016-05-28 21:35:42 +02:00
XClientMessageEvent m ;
2016-06-19 02:06:25 +02:00
memset ( & m , 0 , sizeof ( m ) ) ;
2016-05-28 21:35:42 +02:00
m . type = ClientMessage ;
m . display = event . xclient . display ;
m . window = event . xclient . data . l [ 0 ] ;
2017-03-05 16:44:50 +01:00
m . message_type = xdnd_status ;
m . format = 32 ;
2016-05-28 21:35:42 +02:00
m . data . l [ 0 ] = x11_window ;
2017-03-05 16:44:50 +01:00
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 ) ;
}
2016-05-28 21:35:42 +02:00
}
2017-03-05 16:44:50 +01:00
break ;
default :
break ;
2014-02-10 02:10:30 +01:00
}
}
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
XFlush ( x11_display ) ;
if ( do_mouse_warp ) {
XWarpPointer ( x11_display , None , x11_window ,
2017-03-05 16:44:50 +01:00
0 , 0 , 0 , 0 , ( int ) current_videomode . width / 2 , ( int ) current_videomode . height / 2 ) ;
2015-02-12 15:58:00 +01:00
2016-03-09 00:00:52 +01:00
/*
2015-02-12 15:58:00 +01:00
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 ) ;
*/
2014-02-10 02:10:30 +01:00
}
2019-03-03 23:52:18 +01:00
input - > flush_accumulated_events ( ) ;
2014-02-10 02:10:30 +01:00
}
MainLoop * OS_X11 : : get_main_loop ( ) const {
return main_loop ;
}
void OS_X11 : : delete_main_loop ( ) {
if ( main_loop )
memdelete ( main_loop ) ;
2017-03-05 16:44:50 +01:00
main_loop = NULL ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_X11 : : set_main_loop ( MainLoop * p_main_loop ) {
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
main_loop = p_main_loop ;
2014-02-10 02:10:30 +01:00
input - > set_main_loop ( p_main_loop ) ;
}
bool OS_X11 : : can_draw ( ) const {
return ! minimized ;
} ;
2017-03-05 16:44:50 +01:00
void OS_X11 : : set_clipboard ( const String & p_text ) {
2014-02-10 02:10:30 +01:00
OS : : set_clipboard ( p_text ) ;
XSetSelectionOwner ( x11_display , XA_PRIMARY , x11_window , CurrentTime ) ;
XSetSelectionOwner ( x11_display , XInternAtom ( x11_display , " CLIPBOARD " , 0 ) , x11_window , CurrentTime ) ;
} ;
2017-07-25 00:36:19 +02:00
static String _get_clipboard_impl ( Atom p_source , Window x11_window , : : Display * x11_display , String p_internal_clipboard , Atom target ) {
2014-02-10 02:10:30 +01:00
String ret ;
Atom type ;
Atom selection = XA_PRIMARY ;
int format , result ;
unsigned long len , bytes_left , dummy ;
unsigned char * data ;
2017-03-05 16:44:50 +01:00
Window Sown = XGetSelectionOwner ( x11_display , p_source ) ;
2014-02-10 02:10:30 +01:00
if ( Sown = = x11_window ) {
return p_internal_clipboard ;
} ;
if ( Sown ! = None ) {
2017-07-25 00:36:19 +02:00
XConvertSelection ( x11_display , p_source , target , selection ,
2017-03-05 16:44:50 +01:00
x11_window , CurrentTime ) ;
XFlush ( x11_display ) ;
2014-02-10 02:10:30 +01:00
while ( true ) {
XEvent event ;
XNextEvent ( x11_display , & event ) ;
if ( event . type = = SelectionNotify & & event . xselection . requestor = = x11_window ) {
break ;
} ;
} ;
//
// Do not get any data, see how much data is there
//
2017-03-05 16:44:50 +01:00
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 , //that
& data ) ;
2014-02-10 02:10:30 +01:00
// DATA is There
2017-03-05 16:44:50 +01:00
if ( bytes_left > 0 ) {
result = XGetWindowProperty ( x11_display , x11_window ,
selection , 0 , bytes_left , 0 ,
AnyPropertyType , & type , & format ,
& len , & dummy , & data ) ;
2014-02-10 02:10:30 +01:00
if ( result = = Success ) {
2017-03-05 16:44:50 +01:00
ret . parse_utf8 ( ( const char * ) data ) ;
} else
printf ( " FAIL \n " ) ;
XFree ( data ) ;
2014-02-10 02:10:30 +01:00
}
}
return ret ;
2016-07-21 21:11:34 +02:00
}
2014-02-10 02:10:30 +01:00
2017-07-25 00:36:19 +02:00
static String _get_clipboard ( Atom p_source , Window x11_window , : : Display * x11_display , String p_internal_clipboard ) {
String ret ;
Atom utf8_atom = XInternAtom ( x11_display , " UTF8_STRING " , True ) ;
if ( utf8_atom ! = None ) {
ret = _get_clipboard_impl ( p_source , x11_window , x11_display , p_internal_clipboard , utf8_atom ) ;
}
if ( ret = = " " ) {
ret = _get_clipboard_impl ( p_source , x11_window , x11_display , p_internal_clipboard , XA_STRING ) ;
}
return ret ;
}
2014-02-10 02:10:30 +01:00
String OS_X11 : : get_clipboard ( ) const {
String ret ;
ret = _get_clipboard ( XInternAtom ( x11_display , " CLIPBOARD " , 0 ) , x11_window , x11_display , OS : : get_clipboard ( ) ) ;
if ( ret = = " " ) {
ret = _get_clipboard ( XA_PRIMARY , x11_window , x11_display , OS : : get_clipboard ( ) ) ;
} ;
return ret ;
2016-07-21 21:11:34 +02:00
}
2014-02-10 02:10:30 +01:00
2019-05-20 19:36:24 +02:00
String OS_X11 : : get_name ( ) const {
2014-02-10 02:10:30 +01:00
return " X11 " ;
}
2014-04-18 16:43:54 +02:00
Error OS_X11 : : shell_open ( String p_uri ) {
2014-12-02 18:02:41 +01:00
Error ok ;
List < String > args ;
args . push_back ( p_uri ) ;
2017-10-13 16:45:24 +02:00
ok = execute ( " xdg-open " , args , false ) ;
2017-03-05 16:44:50 +01:00
if ( ok = = OK )
2014-12-02 18:02:41 +01:00
return OK ;
2017-03-05 16:44:50 +01:00
ok = execute ( " gnome-open " , args , false ) ;
if ( ok = = OK )
2014-12-02 18:02:41 +01:00
return OK ;
2017-03-05 16:44:50 +01:00
ok = execute ( " kde-open " , args , false ) ;
2014-12-02 18:02:41 +01:00
return ok ;
}
2017-07-19 22:00:46 +02:00
bool OS_X11 : : _check_internal_feature_support ( const String & p_feature ) {
2019-02-26 15:58:47 +01:00
return p_feature = = " pc " ;
2017-07-19 22:00:46 +02:00
}
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-17 17:11:41 +01:00
String OS_X11 : : get_config_path ( ) const {
if ( has_environment ( " XDG_CONFIG_HOME " ) ) {
return get_environment ( " XDG_CONFIG_HOME " ) ;
} else if ( has_environment ( " HOME " ) ) {
return get_environment ( " HOME " ) . plus_file ( " .config " ) ;
} else {
return " . " ;
}
}
String OS_X11 : : get_data_path ( ) const {
if ( has_environment ( " XDG_DATA_HOME " ) ) {
return get_environment ( " XDG_DATA_HOME " ) ;
} else if ( has_environment ( " HOME " ) ) {
return get_environment ( " HOME " ) . plus_file ( " .local/share " ) ;
} else {
return get_config_path ( ) ;
}
}
String OS_X11 : : get_cache_path ( ) const {
if ( has_environment ( " XDG_CACHE_HOME " ) ) {
return get_environment ( " XDG_CACHE_HOME " ) ;
} else if ( has_environment ( " HOME " ) ) {
return get_environment ( " HOME " ) . plus_file ( " .cache " ) ;
} else {
return get_config_path ( ) ;
}
}
2014-12-02 18:02:41 +01:00
String OS_X11 : : get_system_dir ( SystemDir p_dir ) const {
String xdgparam ;
2017-03-05 16:44:50 +01:00
switch ( p_dir ) {
2014-12-02 18:02:41 +01:00
case SYSTEM_DIR_DESKTOP : {
2017-03-05 16:44:50 +01:00
xdgparam = " DESKTOP " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DCIM : {
2017-03-05 16:44:50 +01:00
xdgparam = " PICTURES " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DOCUMENTS : {
2017-03-05 16:44:50 +01:00
xdgparam = " DOCUMENTS " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DOWNLOADS : {
2017-03-05 16:44:50 +01:00
xdgparam = " DOWNLOAD " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_MOVIES : {
2017-03-05 16:44:50 +01:00
xdgparam = " VIDEOS " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_MUSIC : {
2017-03-05 16:44:50 +01:00
xdgparam = " MUSIC " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_PICTURES : {
2017-03-05 16:44:50 +01:00
xdgparam = " PICTURES " ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_RINGTONES : {
2017-03-05 16:44:50 +01:00
xdgparam = " MUSIC " ;
2014-12-02 18:02:41 +01:00
} break ;
}
String pipe ;
List < String > arg ;
arg . push_back ( xdgparam ) ;
2017-10-13 16:45:24 +02:00
Error err = const_cast < OS_X11 * > ( this ) - > execute ( " xdg-user-dir " , arg , true , NULL , & pipe ) ;
2017-03-05 16:44:50 +01:00
if ( err ! = OK )
2014-12-02 18:02:41 +01:00
return " . " ;
return pipe . strip_edges ( ) ;
2014-04-18 16:43:54 +02:00
}
2014-02-10 02:10:30 +01:00
void OS_X11 : : move_window_to_foreground ( ) {
2018-08-16 21:36:37 +02:00
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 ) ;
2014-02-10 02:10:30 +01:00
}
void OS_X11 : : set_cursor_shape ( CursorShape p_shape ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2014-02-10 02:10:30 +01:00
2018-09-15 20:54:22 +02:00
if ( p_shape = = current_cursor ) {
2014-02-10 02:10:30 +01:00
return ;
2018-09-15 20:54:22 +02:00
}
if ( mouse_mode = = MOUSE_MODE_VISIBLE | | mouse_mode = = MOUSE_MODE_CONFINED ) {
if ( cursors [ p_shape ] ! = None ) {
2017-03-05 16:44:50 +01:00
XDefineCursor ( x11_display , x11_window , cursors [ p_shape ] ) ;
2018-09-15 20:54:22 +02:00
} else if ( cursors [ CURSOR_ARROW ] ! = None ) {
2017-03-05 16:44:50 +01:00
XDefineCursor ( x11_display , x11_window , cursors [ CURSOR_ARROW ] ) ;
2018-09-15 20:54:22 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
current_cursor = p_shape ;
2014-02-10 02:10:30 +01:00
}
2019-04-15 17:30:20 +02:00
OS : : CursorShape OS_X11 : : get_cursor_shape ( ) const {
return current_cursor ;
}
2017-11-10 11:50:11 +01:00
void OS_X11 : : set_custom_mouse_cursor ( const RES & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2019-07-08 22:37:18 +02:00
2017-11-10 11:50:11 +01:00
if ( p_cursor . is_valid ( ) ) {
2019-07-08 22:37:18 +02:00
Map < CursorShape , Vector < Variant > > : : 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 ) ;
}
2017-11-10 11:50:11 +01:00
Ref < Texture > texture = p_cursor ;
2018-05-08 16:07:43 +02:00
Ref < AtlasTexture > atlas_texture = p_cursor ;
Ref < Image > image ;
Size2 texture_size ;
Rect2 atlas_rect ;
2017-11-10 11:50:11 +01:00
2018-05-08 16:07:43 +02:00
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 ( ) ) ;
2018-09-04 04:42:23 +02:00
ERR_FAIL_COND ( p_hotspot . x < 0 | | p_hotspot . y < 0 ) ;
2018-05-08 16:07:43 +02:00
ERR_FAIL_COND ( texture_size . width > 256 | | texture_size . height > 256 ) ;
2018-09-03 14:21:13 +02:00
ERR_FAIL_COND ( p_hotspot . x > texture_size . width | | p_hotspot . y > texture_size . height ) ;
2018-05-08 16:07:43 +02:00
image = texture - > get_data ( ) ;
2017-11-10 11:50:11 +01:00
2018-05-28 14:36:30 +02:00
ERR_FAIL_COND ( ! image . is_valid ( ) ) ;
2017-11-10 11:50:11 +01:00
// Create the cursor structure
2018-05-08 16:07:43 +02:00
XcursorImage * cursor_image = XcursorImageCreate ( texture_size . width , texture_size . height ) ;
XcursorUInt image_size = texture_size . width * texture_size . height ;
2017-11-10 11:50:11 +01:00
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
2018-05-26 20:58:12 +02:00
cursor_image - > pixels = ( XcursorPixel * ) memalloc ( size ) ;
2017-11-10 11:50:11 +01:00
image - > lock ( ) ;
for ( XcursorPixel index = 0 ; index < image_size ; index + + ) {
2018-05-08 16:07:43 +02:00
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 ) ;
}
2017-11-10 11:50:11 +01:00
2018-04-05 18:07:44 +02:00
* ( cursor_image - > pixels + index ) = image - > get_pixel ( column_index , row_index ) . to_argb32 ( ) ;
2017-11-10 11:50:11 +01:00
}
image - > unlock ( ) ;
ERR_FAIL_COND ( cursor_image - > pixels = = NULL ) ;
// Save it for a further usage
cursors [ p_shape ] = XcursorImageLoadCursor ( x11_display , cursor_image ) ;
2019-07-08 22:37:18 +02:00
Vector < Variant > params ;
params . push_back ( p_cursor ) ;
params . push_back ( p_hotspot ) ;
cursors_cache . insert ( p_shape , params ) ;
2018-09-13 19:53:20 +02:00
if ( p_shape = = current_cursor ) {
2018-09-17 16:37:54 +02:00
if ( mouse_mode = = MOUSE_MODE_VISIBLE | | mouse_mode = = MOUSE_MODE_CONFINED ) {
XDefineCursor ( x11_display , x11_window , cursors [ p_shape ] ) ;
}
2017-11-10 11:50:11 +01:00
}
2018-05-26 20:58:12 +02:00
memfree ( cursor_image - > pixels ) ;
XcursorImageDestroy ( cursor_image ) ;
2018-05-11 00:17:00 +02:00
} else {
// Reset to default system cursor
if ( img [ p_shape ] ) {
cursors [ p_shape ] = XcursorImageLoadCursor ( x11_display , img [ p_shape ] ) ;
}
2018-09-13 19:53:20 +02:00
CursorShape c = current_cursor ;
2018-05-11 00:17:00 +02:00
current_cursor = CURSOR_MAX ;
2018-09-13 19:53:20 +02:00
set_cursor_shape ( c ) ;
2019-10-03 13:02:11 +02:00
cursors_cache . erase ( p_shape ) ;
2017-11-10 11:50:11 +01:00
}
}
2014-02-10 02:10:30 +01:00
void OS_X11 : : release_rendering_thread ( ) {
2018-06-22 16:42:28 +02:00
# if defined(OPENGL_ENABLED)
2014-02-10 02:10:30 +01:00
context_gl - > release_current ( ) ;
2018-06-22 16:42:28 +02:00
# endif
2014-02-10 02:10:30 +01:00
}
void OS_X11 : : make_rendering_thread ( ) {
2018-06-22 16:42:28 +02:00
# if defined(OPENGL_ENABLED)
2014-02-10 02:10:30 +01:00
context_gl - > make_current ( ) ;
2018-06-22 16:42:28 +02:00
# endif
2014-02-10 02:10:30 +01:00
}
void OS_X11 : : swap_buffers ( ) {
2018-06-22 16:42:28 +02:00
# if defined(OPENGL_ENABLED)
2014-02-10 02:10:30 +01:00
context_gl - > swap_buffers ( ) ;
2018-06-22 16:42:28 +02:00
# endif
2019-06-07 18:07:57 +02:00
Vector < Color > clear ;
2019-06-08 22:10:52 +02:00
float color [ 4 ] = { 1 , 0 , 1 , 1 } ;
2019-06-07 18:07:57 +02:00
clear . push_back ( Color ( 0.5 , 0.8 , 0.2 ) ) ;
RenderingDevice : : ID cmd_list = rendering_device - > draw_list_begin ( test_framebuffer , RenderingDevice : : INITIAL_ACTION_CLEAR , RenderingDevice : : FINAL_ACTION_READ_COLOR_DISCARD_DEPTH , clear ) ;
rendering_device - > draw_list_bind_render_pipeline ( cmd_list , test_pipeline ) ;
rendering_device - > draw_list_bind_index_array ( cmd_list , test_index_array ) ;
rendering_device - > draw_list_bind_vertex_array ( cmd_list , test_vertex_array ) ;
rendering_device - > draw_list_bind_uniform_set ( cmd_list , test_uniform_set , 0 ) ;
2019-06-08 22:10:52 +02:00
rendering_device - > draw_list_set_push_constant ( cmd_list , color , 4 * 4 ) ;
2019-06-07 18:07:57 +02:00
rendering_device - > draw_list_draw ( cmd_list , true ) ;
rendering_device - > draw_list_end ( ) ;
cmd_list = rendering_device - > draw_list_begin_for_screen ( ) ;
rendering_device - > draw_list_bind_render_pipeline ( cmd_list , test_framebuffer_pipeline ) ;
rendering_device - > draw_list_bind_index_array ( cmd_list , test_index_array ) ;
rendering_device - > draw_list_bind_vertex_array ( cmd_list , test_vertex_array ) ;
rendering_device - > draw_list_bind_uniform_set ( cmd_list , test_framebuffer_uniform_set , 0 ) ;
2019-06-08 22:10:52 +02:00
rendering_device - > draw_list_set_push_constant ( cmd_list , color , 4 * 4 ) ;
2019-06-07 18:07:57 +02:00
rendering_device - > draw_list_draw ( cmd_list , true ) ;
rendering_device - > draw_list_end ( ) ;
rendering_device - > finalize_frame ( ) ;
context_vulkan - > swap_buffers ( ) ;
rendering_device - > advance_frame ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_X11 : : alert ( const String & p_alert , const String & p_title ) {
2018-08-24 22:41:29 +02:00
const char * message_programs [ ] = { " zenity " , " kdialog " , " Xdialog " , " xmessage " } ;
String path = get_environment ( " PATH " ) ;
Vector < String > path_elems = path . split ( " : " , false ) ;
String program ;
for ( int i = 0 ; i < path_elems . size ( ) ; i + + ) {
2019-09-22 18:45:08 +02:00
for ( uint64_t k = 0 ; k < sizeof ( message_programs ) / sizeof ( char * ) ; k + + ) {
2019-06-16 14:31:57 +02:00
String tested_path = path_elems [ i ] . plus_file ( message_programs [ k ] ) ;
2018-08-24 22:41:29 +02:00
if ( FileAccess : : exists ( tested_path ) ) {
program = tested_path ;
break ;
}
}
if ( program . length ( ) )
break ;
}
2015-09-21 14:39:46 +02:00
List < String > args ;
2018-08-24 22:41:29 +02:00
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 ) ;
}
2015-09-21 14:39:46 +02:00
}
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
bool g_set_icon_error = false ;
int set_icon_errorhandler ( Display * dpy , XErrorEvent * ev ) {
g_set_icon_error = true ;
return 0 ;
}
2017-05-17 12:36:47 +02:00
void OS_X11 : : set_icon ( const Ref < Image > & p_icon ) {
2018-08-26 23:07:15 +02:00
int ( * oldHandler ) ( Display * , XErrorEvent * ) = XSetErrorHandler ( & set_icon_errorhandler ) ;
2015-10-25 18:36:27 +01:00
Atom net_wm_icon = XInternAtom ( x11_display , " _NET_WM_ICON " , False ) ;
2015-10-25 15:15:56 +01:00
2017-05-17 12:36:47 +02:00
if ( p_icon . is_valid ( ) ) {
Ref < Image > img = p_icon - > duplicate ( ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
while ( true ) {
int w = img - > get_width ( ) ;
int h = img - > get_height ( ) ;
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
if ( g_set_icon_error ) {
g_set_icon_error = false ;
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
WARN_PRINT ( " Icon too large, attempting to resize icon. " ) ;
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
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 ;
}
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
w = new_width ;
h = new_height ;
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
if ( ! w | | ! h ) {
WARN_PRINT ( " Unable to set icon. " ) ;
break ;
}
img - > resize ( w , h , Image : : INTERPOLATE_CUBIC ) ;
}
2014-02-10 02:10:30 +01:00
2018-08-26 23:07:15 +02:00
// We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
Vector < long > pd ;
pd . resize ( 2 + w * h ) ;
pd . write [ 0 ] = w ;
pd . write [ 1 ] = h ;
PoolVector < uint8_t > : : 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 ;
}
XChangeProperty ( x11_display , x11_window , net_wm_icon , XA_CARDINAL , 32 , PropModeReplace , ( unsigned char * ) pd . ptr ( ) , pd . size ( ) ) ;
if ( ! g_set_icon_error )
break ;
2014-02-10 02:10:30 +01:00
}
} else {
2017-03-05 16:44:50 +01:00
XDeleteProperty ( x11_display , x11_window , net_wm_icon ) ;
2014-02-10 02:10:30 +01:00
}
2018-08-26 23:07:15 +02:00
2014-02-10 02:10:30 +01:00
XFlush ( x11_display ) ;
2018-08-26 23:07:15 +02:00
XSetErrorHandler ( oldHandler ) ;
2014-02-10 02:10:30 +01:00
}
2017-12-14 12:59:46 +01:00
void OS_X11 : : force_process_input ( ) {
process_xevents ( ) ; // get rid of pending events
# ifdef JOYDEV_ENABLED
joypad - > process_joypads ( ) ;
# endif
}
2014-02-10 02:10:30 +01:00
void OS_X11 : : run ( ) {
force_quit = false ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
if ( ! main_loop )
return ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
main_loop - > init ( ) ;
2016-03-09 00:00:52 +01:00
2017-01-14 12:26:56 +01:00
//uint64_t last_ticks=get_ticks_usec();
2016-03-09 00:00:52 +01:00
2017-01-14 12:26:56 +01:00
//int frames=0;
//uint64_t frame=0;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( ! force_quit ) {
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
process_xevents ( ) ; // get rid of pending events
2015-12-21 22:39:03 +01:00
# ifdef JOYDEV_ENABLED
2017-03-26 15:59:13 +02:00
joypad - > process_joypads ( ) ;
2015-12-18 06:12:53 +01:00
# endif
2018-10-03 19:40:37 +02:00
if ( Main : : iteration ( ) )
2014-02-10 02:10:30 +01:00
break ;
} ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
main_loop - > finish ( ) ;
}
2016-01-08 00:40:41 +01:00
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 ) ;
}
2017-12-16 21:09:25 +01:00
void OS_X11 : : _set_use_vsync ( bool p_enable ) {
2018-06-22 16:42:28 +02:00
# if defined(OPENGL_ENABLED)
2016-06-06 00:14:33 +02:00
if ( context_gl )
2018-06-22 16:42:28 +02:00
context_gl - > set_use_vsync ( p_enable ) ;
# endif
2016-06-06 00:14:33 +02:00
}
2017-12-16 21:09:25 +01:00
/*
2016-11-08 15:06:57 +01:00
bool OS_X11 : : is_vsync_enabled ( ) const {
2016-06-06 00:14:33 +02:00
if ( context_gl )
return context_gl - > is_using_vsync ( ) ;
return true ;
}
2017-12-16 21:09:25 +01:00
*/
2016-01-27 21:53:37 +01:00
void OS_X11 : : set_context ( int p_context ) {
2017-08-21 21:15:36 +02:00
XClassHint * classHint = XAllocClassHint ( ) ;
2019-01-30 02:12:41 +01:00
2016-01-27 21:53:37 +01:00
if ( classHint ) {
2019-03-12 13:57:22 +01:00
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 ;
}
2018-10-05 22:18:10 +02:00
2019-03-12 13:57:22 +01:00
CharString class_str ;
2018-10-05 22:18:10 +02:00
if ( p_context = = CONTEXT_ENGINE ) {
2019-03-12 13:57:22 +01:00
String config_name = GLOBAL_GET ( " application/config/name " ) ;
if ( config_name . length ( ) = = 0 ) {
class_str = " Godot_Engine " ;
2019-03-12 14:41:02 +01:00
} else {
2019-03-12 13:57:22 +01:00
class_str = config_name . utf8 ( ) ;
2019-03-12 14:41:02 +01:00
}
2019-03-12 13:57:22 +01:00
} else {
class_str = " Godot " ;
2018-10-05 22:18:10 +02:00
}
2019-03-12 13:57:22 +01:00
classHint - > res_class = class_str . ptrw ( ) ;
classHint - > res_name = name_str . ptrw ( ) ;
2018-10-05 22:18:10 +02:00
2016-01-27 21:53:37 +01:00
XSetClassHint ( x11_display , x11_window , classHint ) ;
XFree ( classHint ) ;
}
}
2017-09-12 21:09:06 +02:00
OS : : PowerState OS_X11 : : get_power_state ( ) {
2016-07-23 13:15:55 +02:00
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 ( ) ;
}
2017-09-08 03:01:49 +02:00
void OS_X11 : : disable_crash_handler ( ) {
crash_handler . disable ( ) ;
}
bool OS_X11 : : is_disable_crash_handler ( ) const {
return crash_handler . is_disabled ( ) ;
}
2017-09-25 15:15:11 +02:00
static String get_mountpoint ( const String & p_path ) {
struct stat s ;
if ( stat ( p_path . utf8 ( ) . get_data ( ) , & s ) ) {
return " " ;
}
2017-10-17 18:50:41 +02:00
# ifdef HAVE_MNTENT
2017-09-25 15:15:11 +02:00
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 ) ;
2017-10-17 18:50:41 +02:00
# endif
2017-09-25 15:15:11 +02:00
return " " ;
}
Error OS_X11 : : move_to_trash ( const String & p_path ) {
2018-02-14 00:04:11 +01:00
String trash_can = " " ;
2017-09-25 15:15:11 +02:00
String mnt = get_mountpoint ( p_path ) ;
2018-02-14 00:04:11 +01:00
// If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
2017-09-25 15:15:11 +02:00
if ( mnt ! = " " ) {
String path ( mnt + " /.Trash- " + itos ( getuid ( ) ) + " /files " ) ;
struct stat s ;
if ( ! stat ( path . utf8 ( ) . get_data ( ) , & s ) ) {
2018-02-14 00:04:11 +01:00
trash_can = path ;
2017-09-25 15:15:11 +02:00
}
}
2018-02-14 00:04:11 +01:00
// Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
if ( trash_can = = " " ) {
2017-09-25 15:15:11 +02:00
char * dhome = getenv ( " XDG_DATA_HOME " ) ;
if ( dhome ) {
2018-02-14 00:04:11 +01:00
trash_can = String ( dhome ) + " /Trash/files " ;
2017-09-25 15:15:11 +02:00
}
}
2018-02-14 00:04:11 +01:00
// Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
if ( trash_can = = " " ) {
2017-09-25 15:15:11 +02:00
char * home = getenv ( " HOME " ) ;
if ( home ) {
2018-02-14 00:04:11 +01:00
trash_can = String ( home ) + " /.local/share/Trash/files " ;
2017-09-25 15:15:11 +02:00
}
}
2018-02-14 00:04:11 +01:00
// Issue an error if none of the previous locations is appropriate for the trash can.
if ( trash_can = = " " ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " move_to_trash: Could not determine the trash can location " ) ;
2017-09-25 15:15:11 +02:00
return FAILED ;
}
2018-02-14 00:04:11 +01:00
// Create needed directories for decided trash can location.
DirAccess * dir_access = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
Error err = dir_access - > make_dir_recursive ( trash_can ) ;
memdelete ( dir_access ) ;
// Issue an error if trash can is not created proprely.
if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " move_to_trash: Could not create the trash can \" " + trash_can + " \" " ) ;
2018-02-14 00:04:11 +01:00
return err ;
2017-09-25 15:15:11 +02:00
}
2018-02-14 00:04:11 +01:00
// The trash can is successfully created, now move the given resource to it.
// Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
List < String > mv_args ;
mv_args . push_back ( p_path ) ;
mv_args . push_back ( trash_can ) ;
int retval ;
err = execute ( " mv " , mv_args , true , NULL , NULL , & retval ) ;
// Issue an error if "mv" failed to move the given resource to the trash can.
if ( err ! = OK | | retval ! = 0 ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " move_to_trash: Could not move the resource \" " + p_path + " \" to the trash can \" " + trash_can + " \" " ) ;
2018-02-14 00:04:11 +01:00
return FAILED ;
}
return OK ;
2017-09-25 15:15:11 +02:00
}
2017-10-30 15:40:42 +01:00
OS : : LatinKeyboardVariant OS_X11 : : get_latin_keyboard_variant ( ) const {
XkbDescRec * xkbdesc = XkbAllocKeyboard ( ) ;
ERR_FAIL_COND_V ( ! xkbdesc , LATIN_KEYBOARD_QWERTY ) ;
XkbGetNames ( x11_display , XkbSymbolsNameMask , xkbdesc ) ;
ERR_FAIL_COND_V ( ! xkbdesc - > names , LATIN_KEYBOARD_QWERTY ) ;
ERR_FAIL_COND_V ( ! xkbdesc - > names - > symbols , LATIN_KEYBOARD_QWERTY ) ;
char * layout = XGetAtomName ( x11_display , xkbdesc - > names - > symbols ) ;
ERR_FAIL_COND_V ( ! layout , LATIN_KEYBOARD_QWERTY ) ;
Vector < String > info = String ( layout ) . split ( " + " ) ;
ERR_FAIL_INDEX_V ( 1 , info . size ( ) , LATIN_KEYBOARD_QWERTY ) ;
2017-10-24 19:32:40 +02:00
if ( info [ 1 ] . find ( " colemak " ) ! = - 1 ) {
2017-10-30 15:40:42 +01:00
return LATIN_KEYBOARD_COLEMAK ;
2017-10-24 19:32:40 +02:00
} else if ( info [ 1 ] . find ( " qwertz " ) ! = - 1 ) {
2017-10-30 15:40:42 +01:00
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 ;
}
2018-09-09 22:13:50 +02:00
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 ) ;
}
}
}
2014-02-10 02:10:30 +01:00
OS_X11 : : OS_X11 ( ) {
2014-12-19 13:44:34 +01:00
# ifdef PULSEAUDIO_ENABLED
2017-01-15 20:06:14 +01:00
AudioDriverManager : : add_driver ( & driver_pulseaudio ) ;
2014-12-19 13:44:34 +01:00
# endif
2014-12-21 15:42:44 +01:00
# ifdef ALSA_ENABLED
2017-01-15 20:06:14 +01:00
AudioDriverManager : : add_driver ( & driver_alsa ) ;
2014-12-21 15:42:44 +01:00
# endif
2019-04-08 16:06:57 +02:00
xi . opcode = 0 ;
xi . last_relative_time = 0 ;
2017-12-10 19:38:26 +01:00
layered_window = false ;
2014-02-10 02:10:30 +01:00
minimized = false ;
2019-11-28 13:41:07 +01:00
window_focused = true ;
2017-03-05 16:44:50 +01:00
xim_style = 0L ;
mouse_mode = MOUSE_MODE_VISIBLE ;
2020-02-03 16:14:41 +01:00
last_position_before_fs = Vector2 ( ) ;
2016-07-21 21:11:34 +02:00
}