diff options
Diffstat (limited to 'platform/linuxbsd/display_server_x11.cpp')
-rw-r--r-- | platform/linuxbsd/display_server_x11.cpp | 269 |
1 files changed, 208 insertions, 61 deletions
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index e04dde7969..74c69c0823 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -552,6 +552,60 @@ String DisplayServerX11::clipboard_get() const { return ret; } +Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) { + if (event->xany.window == *(Window *)arg) { + return (event->type == SelectionRequest) || + (event->type == SelectionNotify); + } else { + return False; + } +} + +void DisplayServerX11::_clipboard_transfer_ownership(Atom p_source, Window x11_window) const { + _THREAD_SAFE_METHOD_ + + Window selection_owner = XGetSelectionOwner(x11_display, p_source); + + if (selection_owner != x11_window) { + return; + } + + // Block events polling while processing selection events. + MutexLock mutex_lock(events_mutex); + + Atom clipboard_manager = XInternAtom(x11_display, "CLIPBOARD_MANAGER", False); + Atom save_targets = XInternAtom(x11_display, "SAVE_TARGETS", False); + XConvertSelection(x11_display, clipboard_manager, save_targets, None, + x11_window, CurrentTime); + + // Process events from the queue. + while (true) { + if (!_wait_for_events()) { + // Error or timeout, abort. + break; + } + + // Non-blocking wait for next event and remove it from the queue. + XEvent ev; + while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_save_targets, (XPointer)&x11_window)) { + switch (ev.type) { + case SelectionRequest: + _handle_selection_request_event(&(ev.xselectionrequest)); + break; + + case SelectionNotify: { + if (ev.xselection.target == save_targets) { + // Once SelectionNotify is received, we're done whether it succeeded or not. + return; + } + + break; + } + } + } + } +} + int DisplayServerX11::get_screen_count() const { _THREAD_SAFE_METHOD_ @@ -794,7 +848,9 @@ void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false); Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false); - XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length()); + if (_net_wm_name != None && utf8_string != None) { + XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length()); + } } void DisplayServerX11::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { @@ -1276,7 +1332,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { hints.flags = 2; hints.decorations = 0; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); - XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + if (property != None) { + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } } if (p_enabled) { @@ -1303,7 +1361,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { // 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, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); + if (bypass_compositor != None) { + XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); + } XFlush(x11_display); @@ -1317,7 +1377,9 @@ void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { hints.flags = 2; hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); - XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + if (property != None) { + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } } } @@ -1511,7 +1573,9 @@ void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, Windo hints.flags = 2; hints.decorations = p_enabled ? 0 : 1; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); - XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + if (property != None) { + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } // Preserve window size window_set_size(window_get_size(p_window), p_window); @@ -2281,53 +2345,105 @@ void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, Input::get_singleton()->accumulate_input_event(k); } -void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) { - XEvent respond; - if (p_event->target == XInternAtom(x11_display, "UTF8_STRING", 0) || - p_event->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || - p_event->target == XInternAtom(x11_display, "TEXT", 0) || - p_event->target == XA_STRING || - p_event->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || - p_event->target == XInternAtom(x11_display, "text/plain", 0)) { - // Directly using internal clipboard because we know our window - // is the owner during a selection request. - CharString clip = internal_clipboard.utf8(); - XChangeProperty(x11_display, - p_event->requestor, - p_event->property, - p_event->target, - 8, - PropModeReplace, - (unsigned char *)clip.get_data(), - clip.length()); - respond.xselection.property = p_event->property; - } else if (p_event->target == XInternAtom(x11_display, "TARGETS", 0)) { - Atom data[7]; +Atom DisplayServerX11::_process_selection_request_target(Atom p_target, Window p_requestor, Atom p_property) const { + if (p_target == XInternAtom(x11_display, "TARGETS", 0)) { + // Request to list all supported targets. + Atom data[9]; data[0] = XInternAtom(x11_display, "TARGETS", 0); - data[1] = XInternAtom(x11_display, "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); + data[1] = XInternAtom(x11_display, "SAVE_TARGETS", 0); + data[2] = XInternAtom(x11_display, "MULTIPLE", 0); + data[3] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[4] = XInternAtom(x11_display, "COMPOUND_TEXT", 0); + data[5] = XInternAtom(x11_display, "TEXT", 0); + data[6] = XA_STRING; + data[7] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0); + data[8] = XInternAtom(x11_display, "text/plain", 0); XChangeProperty(x11_display, - p_event->requestor, - p_event->property, + p_requestor, + p_property, XA_ATOM, 32, PropModeReplace, (unsigned char *)&data, sizeof(data) / sizeof(data[0])); - respond.xselection.property = p_event->property; - + return p_property; + } else if (p_target == XInternAtom(x11_display, "SAVE_TARGETS", 0)) { + // Request to check if SAVE_TARGETS is supported, nothing special to do. + XChangeProperty(x11_display, + p_requestor, + p_property, + XInternAtom(x11_display, "NULL", False), + 32, + PropModeReplace, + nullptr, + 0); + return p_property; + } else if (p_target == XInternAtom(x11_display, "UTF8_STRING", 0) || + p_target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + p_target == XInternAtom(x11_display, "TEXT", 0) || + p_target == XA_STRING || + p_target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || + p_target == XInternAtom(x11_display, "text/plain", 0)) { + // Directly using internal clipboard because we know our window + // is the owner during a selection request. + CharString clip = internal_clipboard.utf8(); + XChangeProperty(x11_display, + p_requestor, + p_property, + p_target, + 8, + PropModeReplace, + (unsigned char *)clip.get_data(), + clip.length()); + return p_property; } else { - char *targetname = XGetAtomName(x11_display, p_event->target); - printf("No Target '%s'\n", targetname); - if (targetname) { - XFree(targetname); + char *target_name = XGetAtomName(x11_display, p_target); + printf("Target '%s' not supported.\n", target_name); + if (target_name) { + XFree(target_name); } + return None; + } +} + +void DisplayServerX11::_handle_selection_request_event(XSelectionRequestEvent *p_event) const { + XEvent respond; + if (p_event->target == XInternAtom(x11_display, "MULTIPLE", 0)) { + // Request for multiple target conversions at once. + Atom atom_pair = XInternAtom(x11_display, "ATOM_PAIR", False); respond.xselection.property = None; + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = nullptr; + if (XGetWindowProperty(x11_display, p_event->requestor, p_event->property, 0, LONG_MAX, False, atom_pair, &type, &format, &len, &remaining, &data) == Success) { + if ((len >= 2) && data) { + Atom *targets = (Atom *)data; + for (uint64_t i = 0; i < len; i += 2) { + Atom target = targets[i]; + Atom &property = targets[i + 1]; + property = _process_selection_request_target(target, p_event->requestor, property); + } + + XChangeProperty(x11_display, + p_event->requestor, + p_event->property, + atom_pair, + 32, + PropModeReplace, + (unsigned char *)targets, + len); + + respond.xselection.property = p_event->property; + } + XFree(data); + } + } else { + // Request for target conversion. + respond.xselection.property = _process_selection_request_target(p_event->target, p_event->requestor, p_event->property); } respond.xselection.type = SelectionNotify; @@ -2463,32 +2579,43 @@ Bool DisplayServerX11::_predicate_all_events(Display *display, XEvent *event, XP return True; } -void DisplayServerX11::_poll_events() { +bool DisplayServerX11::_wait_for_events() const { int x11_fd = ConnectionNumber(x11_display); fd_set in_fds; - while (!events_thread_done) { - XFlush(x11_display); + XFlush(x11_display); + + FD_ZERO(&in_fds); + FD_SET(x11_fd, &in_fds); - FD_ZERO(&in_fds); - FD_SET(x11_fd, &in_fds); + struct timeval tv; + tv.tv_usec = 0; + tv.tv_sec = 1; - struct timeval tv; - tv.tv_usec = 0; - tv.tv_sec = 1; + // Wait for next event or timeout. + int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv); - // Wait for next event or timeout. - int num_ready_fds = select(x11_fd + 1, &in_fds, NULL, NULL, &tv); + if (num_ready_fds > 0) { + // Event received. + return true; + } else { + // Error or timeout. if (num_ready_fds < 0) { - ERR_PRINT("_poll_events: select error: " + itos(errno)); + ERR_PRINT("_wait_for_events: select error: " + itos(errno)); } + return false; + } +} + +void DisplayServerX11::_poll_events() { + while (!events_thread_done) { + _wait_for_events(); // Process events from the queue. { MutexLock mutex_lock(events_mutex); - // Non-blocking wait for next event - // and remove it from the queue. + // Non-blocking wait for next event and remove it from the queue. XEvent ev; while (XCheckIfEvent(x11_display, &ev, _predicate_all_events, nullptr)) { // Check if the input manager wants to process the event. @@ -3385,7 +3512,9 @@ void DisplayServerX11::set_icon(const Ref<Image> &p_icon) { pr += 4; } - XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size()); + if (net_wm_icon != None) { + XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size()); + } if (!g_set_icon_error) { break; @@ -3478,7 +3607,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u { const long pid = OS::get_singleton()->get_process_id(); Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False); - XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); + if (net_wm_pid != None) { + XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); + } } long im_event_mask = 0; @@ -3526,7 +3657,9 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u /* set the titlebar name */ XStoreName(x11_display, wd.x11_window, "Godot"); XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1); - XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1); + if (xdnd_aware != None) { + XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1); + } if (xim && xim_style) { // Block events polling while changing input focus @@ -3557,20 +3690,25 @@ DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, u hints.flags = 2; hints.decorations = 0; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); - XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + if (property != None) { + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } } if (wd.menu_type) { // Set Utility type to disable fade animations. Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False); Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); - - XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); + if (wt_atom != None && type_atom != None) { + XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); + } } else { Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False); Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False); - XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); + if (wt_atom != None && type_atom != None) { + XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1); + } } _update_size_hints(id); @@ -3852,6 +3990,10 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode (screen_get_size(0).width - p_resolution.width) / 2, (screen_get_size(0).height - p_resolution.height) / 2); WindowID main_window = _create_window(p_mode, p_flags, Rect2i(window_position, p_resolution)); + if (main_window == INVALID_WINDOW_ID) { + r_error = ERR_CANT_CREATE; + return; + } for (int i = 0; i < WINDOW_FLAG_MAX; i++) { if (p_flags & (1 << i)) { window_set_flag(WindowFlags(i), true, main_window); @@ -4043,6 +4185,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode } DisplayServerX11::~DisplayServerX11() { + // Send owned clipboard data to clipboard manager before exit. + Window x11_main_window = windows[MAIN_WINDOW_ID].x11_window; + _clipboard_transfer_ownership(XA_PRIMARY, x11_main_window); + _clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window); + events_thread_done = true; Thread::wait_to_finish(events_thread); memdelete(events_thread); |