diff options
Diffstat (limited to 'platform/x11/os_x11.cpp')
| -rw-r--r-- | platform/x11/os_x11.cpp | 325 |
1 files changed, 258 insertions, 67 deletions
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 66c02561b5..ca9fd68412 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "os_x11.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "errno.h" #include "key_mapping_x11.h" @@ -76,25 +77,6 @@ #include <X11/XKBlib.h> -int OS_X11::get_video_driver_count() const { - return 1; -} - -const char *OS_X11::get_video_driver_name(int p_driver) const { - return "GLES3"; -} - -int OS_X11::get_audio_driver_count() const { - return AudioDriverManager::get_driver_count(); -} - -const char *OS_X11::get_audio_driver_name(int p_driver) const { - - AudioDriver *driver = AudioDriverManager::get_driver(p_driver); - ERR_FAIL_COND_V(!driver, ""); - return AudioDriverManager::get_driver(p_driver)->get_name(); -} - void OS_X11::initialize_core() { crash_handler.initialize(); @@ -102,6 +84,10 @@ void OS_X11::initialize_core() { OS_Unix::initialize_core(); } +int OS_X11::get_current_video_driver() const { + return video_driver_index; +} + Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { long im_event_mask = 0; @@ -184,13 +170,13 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a #ifdef TOUCH_ENABLED if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) { - fprintf(stderr, "XInput extension not available"); + print_verbose("XInput extension not available, touch support disabled."); } else { // 2.2 is the first release with multitouch int xi_major = 2; int xi_minor = 2; if (XIQueryVersion(x11_display, &xi_major, &xi_minor) != Success) { - fprintf(stderr, "XInput 2.2 not available (server supports %d.%d)\n", xi_major, xi_minor); + print_verbose(vformat("XInput 2.2 not available (server supports %d.%d), touch support disabled.", xi_major, xi_minor)); touch.opcode = 0; } else { int dev_count; @@ -212,14 +198,14 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a } if (direct_touch) { touch.devices.push_back(dev->deviceid); - fprintf(stderr, "Using touch device: %s\n", dev->name); + print_verbose("XInput: Using touch device: " + String(dev->name)); } } XIFreeDeviceInfo(info); - if (is_stdout_verbose() && !touch.devices.size()) { - fprintf(stderr, "No touch devices found\n"); + if (!touch.devices.size()) { + print_verbose("XInput: No touch devices found."); } } } @@ -280,15 +266,29 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a */ // maybe contextgl wants to be in charge of creating the window -//print_line("def videomode "+itos(current_videomode.width)+","+itos(current_videomode.height)); #if defined(OPENGL_ENABLED) - context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, true)); + ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_3_0_COMPATIBLE; + + if (p_video_driver == VIDEO_DRIVER_GLES2) { + opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; + } + + context_gl = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); context_gl->initialize(); - RasterizerGLES3::register_config(); + switch (opengl_api_type) { + case ContextGL_X11::GLES_2_0_COMPATIBLE: { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } break; + case ContextGL_X11::GLES_3_0_COMPATIBLE: { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } break; + } - RasterizerGLES3::make_current(); + video_driver_index = p_video_driver; // FIXME TODO - FIX IF DRIVER DETECTION HAPPENS AND GLES2 MUST BE USED context_gl->set_use_vsync(current_videomode.use_vsync); @@ -396,6 +396,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); + im_active = false; + im_position = Vector2(); + if (xim && xim_style) { xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL); @@ -405,7 +408,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a xic = NULL; } if (xic) { - XSetICFocus(xic); + XUnsetICFocus(xic); } else { WARN_PRINT("XCreateIC couldn't create xic"); } @@ -419,9 +422,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a cursor_theme = XcursorGetTheme(x11_display); if (!cursor_theme) { - if (is_stdout_verbose()) { - print_line("XcursorGetTheme could not get cursor theme"); - } + print_verbose("XcursorGetTheme could not get cursor theme"); cursor_theme = "default"; } @@ -434,7 +435,6 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a current_cursor = CURSOR_ARROW; if (cursor_theme) { - //print_line("cursor theme: "+String(cursor_theme)); for (int i = 0; i < CURSOR_MAX; i++) { static const char *cursor_file[] = { @@ -460,10 +460,8 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size); if (img[i]) { cursors[i] = XcursorImageLoadCursor(x11_display, img[i]); - //print_line("found cursor: "+String(cursor_file[i])+" id "+itos(cursors[i])); } else { - if (OS::is_stdout_verbose()) - print_line("failed cursor: " + String(cursor_file[i])); + print_verbose("Failed loading custom cursor: " + String(cursor_file[i])); } } } @@ -522,6 +520,10 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a power_manager = memnew(PowerX11); + if (p_desired.layered_splash) { + set_window_per_pixel_transparency_enabled(true); + } + XEvent xevent; while (XPending(x11_display) > 0) { XNextEvent(x11_display, &xevent); @@ -542,8 +544,25 @@ void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, os->xic = NULL; } +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); + } +} + void OS_X11::set_ime_position(const Point2 &p_pos) { + im_position = p_pos; + if (!xic) return; @@ -581,6 +600,9 @@ void OS_X11::finalize() { memdelete(debugger_connection_console); } */ +#ifdef ALSAMIDI_ENABLED + driver_alsamidi.close(); +#endif #ifdef JOYDEV_ENABLED memdelete(joypad); @@ -639,7 +661,7 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) { bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); if (showCursor) { - XUndefineCursor(x11_display, x11_window); // show cursor + XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor } else { XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor } @@ -712,6 +734,25 @@ Point2 OS_X11::get_mouse_position() const { return last_mouse_pos; } +bool OS_X11::get_window_per_pixel_transparency_enabled() const { + + if (!is_layered_allowed()) return false; + return layered_window; +} + +void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) { + + if (!is_layered_allowed()) return; + if (layered_window != p_enabled) { + if (p_enabled) { + set_borderless_window(true); + layered_window = true; + } else { + layered_window = false; + } + } +} + void OS_X11::set_window_title(const String &p_title) { XStoreName(x11_display, x11_window, p_title.utf8().get_data()); @@ -1011,9 +1052,13 @@ void OS_X11::set_window_size(const Size2 p_size) { } void OS_X11::set_window_fullscreen(bool p_enabled) { + if (current_videomode.fullscreen == p_enabled) return; + if (layered_window) + set_window_per_pixel_transparency_enabled(false); + if (p_enabled && current_videomode.always_on_top) { // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity) set_window_maximized(true); @@ -1193,6 +1238,7 @@ bool OS_X11::is_window_maximized() const { unsigned long len; unsigned long remaining; unsigned char *data = NULL; + bool retval = false; int result = XGetWindowProperty( x11_display, @@ -1221,13 +1267,15 @@ bool OS_X11::is_window_maximized() const { if (atoms[i] == wm_max_vert) found_wm_max_vert = true; - if (found_wm_max_horz && found_wm_max_vert) - return true; + if (found_wm_max_horz && found_wm_max_vert) { + retval = true; + break; + } } - XFree(atoms); } - return false; + XFree(data); + return retval; } void OS_X11::set_window_always_on_top(bool p_enabled) { @@ -1256,6 +1304,9 @@ void OS_X11::set_borderless_window(bool p_borderless) { if (current_videomode.borderless_window == p_borderless) return; + if (!p_borderless && layered_window) + set_window_per_pixel_transparency_enabled(false); + current_videomode.borderless_window = p_borderless; Hints hints; @@ -1264,6 +1315,9 @@ void OS_X11::set_borderless_window(bool p_borderless) { 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); + + // Preserve window size + set_window_size(Size2(current_videomode.width, current_videomode.height)); } bool OS_X11::get_borderless_window() { @@ -1275,7 +1329,7 @@ void OS_X11::request_attention() { // // Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE // Will be unset by the window manager after user react on the request for attention - // + XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); Atom wm_attention = XInternAtom(x11_display, "_NET_WM_STATE_DEMANDS_ATTENTION", False); @@ -1289,6 +1343,7 @@ void OS_X11::request_attention() { xev.xclient.data.l[1] = wm_attention; XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(x11_display); } void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) { @@ -1451,7 +1506,6 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { // KeyMappingX11 also translates keysym to unicode. // It does a binary search on a table to translate // most properly. - //print_line("keysym_unicode: "+rtos(keysym_unicode)); unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0; /* Phase 4, determine if event must be filtered */ @@ -1474,7 +1528,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) { // know Mod1 was ALT and Mod4 was META (applekey/winkey) // just tried Mods until i found them. - //print_line("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); + //print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); Ref<InputEventKey> k; k.instance(); @@ -1692,6 +1746,11 @@ void OS_X11::process_xevents() { if (touch.state.has(index)) // Defensive break; touch.state[index] = pos; + if (touch.state.size() == 1) { + // X11 may send a motion event when a touch gesture begins, that would result + // in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out + touch.mouse_pos_to_filter = pos; + } input->parse_input_event(st); } else { if (!touch.state.has(index)) // Defensive @@ -1769,8 +1828,8 @@ void OS_X11::process_xevents() { GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } #ifdef TOUCH_ENABLED - // Grab touch devices to avoid OS gesture interference - /*for (int i = 0; i < touch.devices.size(); ++i) { + // Grab touch devices to avoid OS gesture interference + /*for (int i = 0; i < touch.devices.size(); ++i) { XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); }*/ #endif @@ -1898,6 +1957,20 @@ void OS_X11::process_xevents() { // to be able to send relative motion events. Point2i pos(event.xmotion.x, event.xmotion.y); +#ifdef TOUCH_ENABLED + // Avoidance of spurious mouse motion (see handling of touch) + bool filter = false; + // Adding some tolerance to match better Point2i to Vector2 + if (touch.state.size() && Vector2(pos).distance_squared_to(touch.mouse_pos_to_filter) < 2) { + filter = true; + } + // Invalidate to avoid filtering a possible legitimate similar event coming later + touch.mouse_pos_to_filter = Vector2(1e10, 1e10); + if (filter) { + break; + } +#endif + if (mouse_mode == MOUSE_MODE_CAPTURED) { if (pos == Point2i(current_videomode.width / 2, current_videomode.height / 2)) { @@ -2022,7 +2095,7 @@ void OS_X11::process_xevents() { Vector<String> files = String((char *)p.data).split("\n", false); for (int i = 0; i < files.size(); i++) { - files[i] = files[i].replace("file://", "").replace("%20", " ").strip_escapes(); + files.write[i] = files[i].replace("file://", "").replace("%20", " ").strip_escapes(); } main_loop->drop_files(files); @@ -2260,7 +2333,7 @@ Error OS_X11::shell_open(String p_uri) { bool OS_X11::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc" || p_feature == "s3tc"; + return p_feature == "pc" || p_feature == "s3tc" || p_feature == "bptc"; } String OS_X11::get_config_path() const { @@ -2353,7 +2426,19 @@ String OS_X11::get_system_dir(SystemDir p_dir) const { void OS_X11::move_window_to_foreground() { - XRaiseWindow(x11_display, x11_window); + XEvent xev; + Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = x11_window; + xev.xclient.message_type = net_active_window; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 1; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(x11_display); } void OS_X11::set_cursor_shape(CursorShape p_shape) { @@ -2362,7 +2447,7 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { if (p_shape == current_cursor) return; - if (mouse_mode == MOUSE_MODE_VISIBLE) { + if (mouse_mode == MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { if (cursors[p_shape] != None) XDefineCursor(x11_display, x11_window, cursors[p_shape]); else if (cursors[CURSOR_ARROW] != None) @@ -2375,13 +2460,40 @@ void OS_X11::set_cursor_shape(CursorShape p_shape) { void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (p_cursor.is_valid()) { Ref<Texture> texture = p_cursor; - Ref<Image> image = texture->get_data(); + Ref<AtlasTexture> atlas_texture = p_cursor; + Ref<Image> image; + Size2 texture_size; + Rect2 atlas_rect; - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + if (texture.is_valid()) { + image = texture->get_data(); + } + + if (!image.is_valid() && atlas_texture.is_valid()) { + texture = atlas_texture->get_atlas(); + + atlas_rect.size.width = texture->get_width(); + atlas_rect.size.height = texture->get_height(); + atlas_rect.position.x = atlas_texture->get_region().position.x; + atlas_rect.position.y = atlas_texture->get_region().position.y; + + texture_size.width = atlas_texture->get_region().size.x; + texture_size.height = atlas_texture->get_region().size.y; + } else if (image.is_valid()) { + texture_size.width = texture->get_width(); + texture_size.height = texture->get_height(); + } + + ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); + + image = texture->get_data(); + + ERR_FAIL_COND(!image.is_valid()); // Create the cursor structure - XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height()); - XcursorUInt image_size = 32 * 32; + XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height); + XcursorUInt image_size = texture_size.width * texture_size.height; XcursorDim size = sizeof(XcursorPixel) * image_size; cursor_image->version = 1; @@ -2390,15 +2502,20 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c cursor_image->yhot = p_hotspot.y; // allocate memory to contain the whole file - cursor_image->pixels = (XcursorPixel *)malloc(size); + cursor_image->pixels = (XcursorPixel *)memalloc(size); image->lock(); for (XcursorPixel index = 0; index < image_size; index++) { - int column_index = floor(index / 32); - int row_index = index % 32; + int row_index = floor(index / texture_size.width) + atlas_rect.position.y; + int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; + + if (atlas_texture.is_valid()) { + column_index = MIN(column_index, atlas_rect.size.width - 1); + row_index = MIN(row_index, atlas_rect.size.height - 1); + } - *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32(); + *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); } image->unlock(); @@ -2411,33 +2528,104 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c if (p_shape == CURSOR_ARROW) { XDefineCursor(x11_display, x11_window, cursors[p_shape]); } + + memfree(cursor_image->pixels); + XcursorImageDestroy(cursor_image); + } else { + // Reset to default system cursor + if (img[p_shape]) { + cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]); + } + + current_cursor = CURSOR_MAX; + set_cursor_shape(p_shape); } } void OS_X11::release_rendering_thread() { +#if defined(OPENGL_ENABLED) context_gl->release_current(); +#endif } void OS_X11::make_rendering_thread() { +#if defined(OPENGL_ENABLED) context_gl->make_current(); +#endif } void OS_X11::swap_buffers() { +#if defined(OPENGL_ENABLED) context_gl->swap_buffers(); +#endif } void OS_X11::alert(const String &p_alert, const String &p_title) { + 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++) { + for (unsigned int k = 0; k < sizeof(message_programs) / sizeof(char *); k++) { + String tested_path = path_elems[i] + "/" + message_programs[k]; + + if (FileAccess::exists(tested_path)) { + program = tested_path; + break; + } + } + + if (program.length()) + break; + } List<String> args; - args.push_back("-center"); - args.push_back("-title"); - args.push_back(p_title); - args.push_back(p_alert); - execute("xmessage", args, true); + 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); + } + + return; } void OS_X11::set_icon(const Ref<Image> &p_icon) { @@ -2455,12 +2643,12 @@ void OS_X11::set_icon(const Ref<Image> &p_icon) { pd.resize(2 + w * h); - pd[0] = w; - pd[1] = h; + pd.write[0] = w; + pd.write[1] = h; PoolVector<uint8_t>::Read r = img->get_data().read(); - long *wr = &pd[2]; + long *wr = &pd.write[2]; uint8_t const *pr = r.ptr(); for (int i = 0; i < w * h; i++) { @@ -2520,8 +2708,10 @@ String OS_X11::get_joy_guid(int p_device) const { } void OS_X11::_set_use_vsync(bool p_enable) { +#if defined(OPENGL_ENABLED) if (context_gl) - return context_gl->set_use_vsync(p_enable); + context_gl->set_use_vsync(p_enable); +#endif } /* bool OS_X11::is_vsync_enabled() const { @@ -2700,6 +2890,7 @@ OS_X11::OS_X11() { AudioDriverManager::add_driver(&driver_alsa); #endif + layered_window = false; minimized = false; xim_style = 0L; mouse_mode = MOUSE_MODE_VISIBLE; |
