diff options
Diffstat (limited to 'platform/x11/os_x11.cpp')
-rw-r--r-- | platform/x11/os_x11.cpp | 286 |
1 files changed, 234 insertions, 52 deletions
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index f018145d82..c210a03688 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -132,10 +132,9 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au // Try to support IME if detectable auto-repeat is supported if (xkb_dar == True) { -// Xutf8LookupString will be used later instead of XmbLookupString before -// the multibyte sequences can be converted to unicode string. - #ifdef X_HAVE_UTF8_STRING + // Xutf8LookupString will be used later instead of XmbLookupString before + // the multibyte sequences can be converted to unicode string. modifiers = XSetLocaleModifiers(""); #endif } @@ -178,6 +177,50 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au } } +#ifdef TOUCH_ENABLED + if (!XQueryExtension(x11_display, "XInputExtension", &touch.opcode, &event_base, &error_base)) { + fprintf(stderr, "XInput extension not available"); + } 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); + touch.opcode = 0; + } else { + 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) + continue;*/ + + bool direct_touch = false; + for (int j = 0; j < dev->num_classes; j++) { + if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { + direct_touch = true; + printf("%d) %d %s\n", i, dev->attachment, dev->name); + break; + } + } + if (direct_touch) { + touch.devices.push_back(dev->deviceid); + fprintf(stderr, "Using touch device: %s\n", dev->name); + } + } + + XIFreeDeviceInfo(info); + + if (!touch.devices.size()) { + fprintf(stderr, "No suitable touch device found\n"); + } + } + } +#endif + xim = XOpenIM(x11_display, NULL, NULL, NULL); if (xim == NULL) { @@ -250,41 +293,13 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); } - - // borderless fullscreen window mode - if (current_videomode.fullscreen) { - // set bypass compositor hint - Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); - unsigned long compositing_disable_on = 1; - XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); - - // needed for lxde/openbox, possibly others - 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); - XMapRaised(x11_display, x11_window); - XWindowAttributes xwa; - XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa); - XMoveResizeWindow(x11_display, x11_window, 0, 0, xwa.width, xwa.height); - - // code for netwm-compliants - XEvent xev; - Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); - Atom fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); - - memset(&xev, 0, sizeof(xev)); - xev.type = ClientMessage; - xev.xclient.window = x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; - xev.xclient.data.l[1] = fullscreen; - xev.xclient.data.l[2] = 0; - - XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureNotifyMask, &xev); + if (current_videomode.maximized) { + current_videomode.maximized = false; + set_window_maximized(true); + // borderless fullscreen window mode + } else if (current_videomode.fullscreen) { + current_videomode.fullscreen = false; + set_window_fullscreen(true); } else if (current_videomode.borderless_window) { Hints hints; Atom property; @@ -336,6 +351,32 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr); +#ifdef TOUCH_ENABLED + if (touch.devices.size()) { + + // Must be alive after this block + static unsigned char mask_data[XIMaskLen(XI_LASTEVENT)] = {}; + + touch.event_mask.deviceid = XIAllMasterDevices; + touch.event_mask.mask_len = sizeof(mask_data); + touch.event_mask.mask = mask_data; + + XISetMask(touch.event_mask.mask, XI_TouchBegin); + XISetMask(touch.event_mask.mask, XI_TouchUpdate); + XISetMask(touch.event_mask.mask, XI_TouchEnd); + XISetMask(touch.event_mask.mask, XI_TouchOwnership); + + XISelectEvents(x11_display, x11_window, &touch.event_mask, 1); + + XIClearMask(touch.event_mask.mask, XI_TouchOwnership); + + // 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 + /* set the titlebar name */ XStoreName(x11_display, x11_window, "Godot"); @@ -464,11 +505,20 @@ void OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_au #ifdef JOYDEV_ENABLED joypad = memnew(JoypadLinux(input)); #endif - _ensure_data_dir(); + _ensure_user_data_dir(); power_manager = memnew(PowerX11); + + XEvent xevent; + while (XCheckIfEvent(x11_display, &xevent, _check_window_events, NULL)) { + _window_changed(&xevent); + } } +int OS_X11::_check_window_events(Display *display, XEvent *event, char *arg) { + if (event->type == ConfigureNotify) return 1; + return 0; +} void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, ::XPointer call_data) { @@ -497,7 +547,7 @@ void OS_X11::finalize() { memdelete(main_loop); main_loop = NULL; -/* + /* if (debugger_connection_console) { memdelete(debugger_connection_console); } @@ -506,6 +556,10 @@ void OS_X11::finalize() { #ifdef JOYDEV_ENABLED memdelete(joypad); #endif +#ifdef TOUCH_ENABLED + touch.devices.clear(); + touch.state.clear(); +#endif memdelete(input); visual_server->finish(); @@ -648,6 +702,9 @@ void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) con } void OS_X11::set_wm_fullscreen(bool p_enabled) { + if (current_videomode.fullscreen == p_enabled) + return; + if (p_enabled && !is_window_resizable()) { // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. XSizeHints *xsh; @@ -971,6 +1028,9 @@ bool OS_X11::is_window_minimized() const { } void OS_X11::set_window_maximized(bool p_enabled) { + if (is_window_maximized() == p_enabled) + return; + // Using EWMH -- Extended Window Manager Hints XEvent xev; Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); @@ -1417,6 +1477,20 @@ static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p return None; } +void OS_X11::_window_changed(XEvent *event) { + + if (xic) { + // Not portable. + set_ime_position(Point2(0, 1)); + } + if ((event->xconfigure.width == current_videomode.width) && + (event->xconfigure.height == current_videomode.height)) + return; + + current_videomode.width = event->xconfigure.width; + current_videomode.height = event->xconfigure.height; +} + void OS_X11::process_xevents() { //printf("checking events %i\n", XPending(x11_display)); @@ -1434,6 +1508,69 @@ void OS_X11::process_xevents() { continue; } +#ifdef TOUCH_ENABLED + if (XGetEventData(x11_display, &event.xcookie)) { + + if (event.xcookie.extension == touch.opcode) { + + XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; + int index = event_data->detail; + Vector2 pos = Vector2(event_data->event_x, event_data->event_y); + + switch (event_data->evtype) { + + case XI_TouchBegin: // Fall-through + XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch); + + 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) { + if (touch.state.has(index)) // Defensive + break; + touch.state[index] = pos; + input->parse_input_event(st); + } else { + if (!touch.state.has(index)) // Defensive + break; + touch.state.erase(index); + input->parse_input_event(st); + } + } break; + + case XI_TouchUpdate: { + + Map<int, Vector2>::Element *curr_pos_elem = touch.state.find(index); + 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()); + input->parse_input_event(sd); + + curr_pos_elem->value() = pos; + } + } break; + } + } + + XFreeEventData(x11_display, &event.xcookie); + } +#endif + switch (event.type) { case Expose: Main::force_redraw(); @@ -1476,6 +1613,12 @@ void OS_X11::process_xevents() { ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime); } +#ifdef TOUCH_ENABLED + // Grab touch devices to avoid OS gesture interference + for (int i = 0; i < touch.devices.size(); ++i) { + XIGrabDevice(x11_display, touch.devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &touch.event_mask); + } +#endif if (xic) { XSetICFocus(xic); } @@ -1492,24 +1635,30 @@ void OS_X11::process_xevents() { } XUngrabPointer(x11_display, CurrentTime); } +#ifdef TOUCH_ENABLED + // Ungrab touch devices so input works as usual while we are unfocused + for (int i = 0; i < touch.devices.size(); ++i) { + XIUngrabDevice(x11_display, touch.devices[i], CurrentTime); + } + + // Release every pointer to avoid sticky points + for (Map<int, Vector2>::Element *E = touch.state.front(); E; E = E->next()) { + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(E->key()); + st->set_position(E->get()); + input->parse_input_event(st); + } + touch.state.clear(); +#endif if (xic) { XUnsetICFocus(xic); } break; case ConfigureNotify: - if (xic) { - // Not portable. - set_ime_position(Point2(0, 1)); - } - /* call resizeGLScene only if our window-size changed */ - - if ((event.xconfigure.width == current_videomode.width) && - (event.xconfigure.height == current_videomode.height)) - break; - - current_videomode.width = event.xconfigure.width; - current_videomode.height = event.xconfigure.height; + _window_changed(&event); break; case ButtonPress: case ButtonRelease: { @@ -1941,6 +2090,39 @@ bool OS_X11::_check_internal_feature_support(const String &p_feature) { return p_feature == "pc" || p_feature == "s3tc"; } +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(); + } +} + String OS_X11::get_system_dir(SystemDir p_dir) const { String xdgparam; |