|
|
@@ -66,6 +66,20 @@ x11GraphicsPipe(const std::string &display) :
|
|
|
// point to mean a decimal point.
|
|
|
setlocale(LC_NUMERIC, "C");
|
|
|
|
|
|
+ // Also save the startup ID. We are required to unset it by the FreeDesktop
|
|
|
+ // specification so that it is not propagated to child processes.
|
|
|
+ {
|
|
|
+ char *startup_id = getenv("DESKTOP_STARTUP_ID");
|
|
|
+ if (startup_id != nullptr) {
|
|
|
+ _startup_id.assign(startup_id);
|
|
|
+ if (x11display_cat.is_debug()) {
|
|
|
+ x11display_cat.debug()
|
|
|
+ << "Got desktop startup ID " << _startup_id << "\n";
|
|
|
+ }
|
|
|
+ unsetenv("DESKTOP_STARTUP_ID");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
_is_valid = false;
|
|
|
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
|
|
|
_display = nullptr;
|
|
|
@@ -375,21 +389,25 @@ x11GraphicsPipe(const std::string &display) :
|
|
|
}
|
|
|
|
|
|
// Get some X atom numbers.
|
|
|
+ _utf8_string = XInternAtom(_display, "UTF8_STRING", false);
|
|
|
_wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
|
|
|
+ _net_startup_id = XInternAtom(_display, "_NET_STARTUP_ID", false);
|
|
|
+ _net_startup_info = XInternAtom(_display, "_NET_STARTUP_INFO", false);
|
|
|
+ _net_startup_info_begin = XInternAtom(_display, "_NET_STARTUP_INFO_BEGIN", false);
|
|
|
+ _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
|
|
|
_net_wm_pid = XInternAtom(_display, "_NET_WM_PID", false);
|
|
|
_net_wm_ping = XInternAtom(_display, "_NET_WM_PING", false);
|
|
|
- _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
|
|
|
- _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
|
|
|
- _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
|
|
|
_net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
|
|
|
- _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
|
|
|
_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
|
|
|
- _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
|
|
|
_net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
|
|
|
- _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
|
|
|
- _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
|
|
|
- _net_wm_state_maximized_vert = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
|
|
|
+ _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
|
|
|
+ _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
|
|
|
_net_wm_state_maximized_horz = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_HORZ", false);
|
|
|
+ _net_wm_state_maximized_vert = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
|
|
|
+ _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
|
|
|
+ _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
|
|
|
+ _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
|
|
|
+ _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -406,6 +424,82 @@ x11GraphicsPipe::
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Tells the window manager that the launch sequence with the indicated startup
|
|
|
+ * ID has finished. Will only send it once, and only if DESKTOP_STARTUP_ID was
|
|
|
+ * passed in as an environment variable.
|
|
|
+ */
|
|
|
+void x11GraphicsPipe::
|
|
|
+send_startup_notification() {
|
|
|
+ if (_sent_startup_notification || _startup_id.empty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Allocate enough room for the message, with room for escape characters.
|
|
|
+ char *message = (char *)alloca(_startup_id.size() * 2 + 14);
|
|
|
+ strcpy(message, "remove: ID=\"");
|
|
|
+
|
|
|
+ char *p = message + 12;
|
|
|
+ for (char c : _startup_id) {
|
|
|
+ if (c == '"' || c == '\\') {
|
|
|
+ *p++ = '\\';
|
|
|
+ }
|
|
|
+ *p++ = c;
|
|
|
+ }
|
|
|
+ *p++ = '\"';
|
|
|
+ *p++ = '\0';
|
|
|
+
|
|
|
+ if (x11display_cat.is_debug()) {
|
|
|
+ x11display_cat.debug()
|
|
|
+ << "Sending startup info message: " << message << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // It doesn't *strictly* seem to be necessary to create a window for this
|
|
|
+ // (just passing in the root window works too) but the spec says we should
|
|
|
+ // and GTK does it too, so why not?
|
|
|
+ XSetWindowAttributes attrs;
|
|
|
+ attrs.override_redirect = True;
|
|
|
+ attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
|
|
|
+ X11_Window xwin = XCreateWindow(_display, _root, -100, -100, 1, 1, 0,
|
|
|
+ CopyFromParent, CopyFromParent, CopyFromParent,
|
|
|
+ CWOverrideRedirect | CWEventMask, &attrs);
|
|
|
+
|
|
|
+ XEvent xevent;
|
|
|
+ xevent.xclient.type = ClientMessage;
|
|
|
+ xevent.xclient.message_type = _net_startup_info_begin;
|
|
|
+ xevent.xclient.display = _display;
|
|
|
+ xevent.xclient.window = xwin;
|
|
|
+ xevent.xclient.format = 8;
|
|
|
+
|
|
|
+ const char *src = message;
|
|
|
+ const char *src_end = message + strlen(message) + 1;
|
|
|
+
|
|
|
+ char *dest, *dest_end;
|
|
|
+ while (src != src_end) {
|
|
|
+ dest = &xevent.xclient.data.b[0];
|
|
|
+ dest_end = dest + 20;
|
|
|
+
|
|
|
+ while (dest != dest_end && src != src_end) {
|
|
|
+ *dest = *src;
|
|
|
+ ++dest;
|
|
|
+ ++src;
|
|
|
+ }
|
|
|
+
|
|
|
+ while (dest != dest_end) {
|
|
|
+ *dest = '\0';
|
|
|
+ ++dest;
|
|
|
+ }
|
|
|
+
|
|
|
+ XSendEvent(_display, _root, False, PropertyChangeMask, &xevent);
|
|
|
+ xevent.xclient.message_type = _net_startup_info;
|
|
|
+ }
|
|
|
+
|
|
|
+ XDestroyWindow(_display, xwin);
|
|
|
+ XFlush(_display);
|
|
|
+
|
|
|
+ _sent_startup_notification = true;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Enables raw mouse mode for this display. Returns false if unsupported.
|
|
|
*/
|