mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-30 16:58:40 -04:00
x11: Support desktop startup notification
This lets the window manager know when a program has finished launching, to stop showing a spinning cursor Can be disabled with `x-send-startup-notification false`
This commit is contained in:
parent
f593026061
commit
60acf82f04
@ -84,6 +84,13 @@ ConfigVariableString x_wm_class
|
||||
PRC_DESC("Specify the value to use for the res_class field of the window's "
|
||||
"WM_CLASS property."));
|
||||
|
||||
ConfigVariableBool x_send_startup_notification
|
||||
("x-send-startup-notification", true,
|
||||
PRC_DESC("Set this to true to send a startup notification to the window "
|
||||
"manager automatically after the first window is opened. This "
|
||||
"lets the window manager know that an application has launched, so "
|
||||
"that it no longer needs to display a spinning mouse cursor."));
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
* the functions or classes in this library can be used. Normally it will be
|
||||
|
@ -36,5 +36,6 @@ extern ConfigVariableInt x_wheel_right_button;
|
||||
extern ConfigVariableInt x_cursor_size;
|
||||
extern ConfigVariableString x_wm_class_name;
|
||||
extern ConfigVariableString x_wm_class;
|
||||
extern ConfigVariableBool x_send_startup_notification;
|
||||
|
||||
#endif
|
||||
|
@ -57,6 +57,15 @@ get_hidden_cursor() {
|
||||
return _hidden_cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the startup id that may have been passed by the environment variable
|
||||
* DESKTOP_STARTUP_ID.
|
||||
*/
|
||||
INLINE const std::string &x11GraphicsPipe::
|
||||
get_startup_id() const {
|
||||
return _startup_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a form of relative mouse mode is supported on this display.
|
||||
*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -144,6 +144,9 @@ public:
|
||||
|
||||
INLINE X11_Cursor get_hidden_cursor();
|
||||
|
||||
INLINE const std::string &get_startup_id() const;
|
||||
void send_startup_notification();
|
||||
|
||||
INLINE bool supports_relative_mouse() const;
|
||||
INLINE bool enable_dga_mouse();
|
||||
INLINE void disable_dga_mouse();
|
||||
@ -165,21 +168,25 @@ public:
|
||||
|
||||
public:
|
||||
// Atom specifications.
|
||||
Atom _utf8_string;
|
||||
Atom _wm_delete_window;
|
||||
Atom _net_startup_id;
|
||||
Atom _net_startup_info;
|
||||
Atom _net_startup_info_begin;
|
||||
Atom _net_wm_bypass_compositor;
|
||||
Atom _net_wm_pid;
|
||||
Atom _net_wm_ping;
|
||||
Atom _net_wm_window_type;
|
||||
Atom _net_wm_window_type_splash;
|
||||
Atom _net_wm_window_type_fullscreen;
|
||||
Atom _net_wm_state;
|
||||
Atom _net_wm_state_fullscreen;
|
||||
Atom _net_wm_state_above;
|
||||
Atom _net_wm_state_below;
|
||||
Atom _net_wm_state_add;
|
||||
Atom _net_wm_state_remove;
|
||||
Atom _net_wm_bypass_compositor;
|
||||
Atom _net_wm_state_maximized_vert;
|
||||
Atom _net_wm_state_below;
|
||||
Atom _net_wm_state_fullscreen;
|
||||
Atom _net_wm_state_maximized_horz;
|
||||
Atom _net_wm_state_maximized_vert;
|
||||
Atom _net_wm_state_remove;
|
||||
Atom _net_wm_window_type;
|
||||
Atom _net_wm_window_type_fullscreen;
|
||||
Atom _net_wm_window_type_splash;
|
||||
|
||||
// Extension functions.
|
||||
typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *);
|
||||
@ -224,6 +231,9 @@ protected:
|
||||
|
||||
X11_Cursor _hidden_cursor;
|
||||
|
||||
std::string _startup_id;
|
||||
bool _sent_startup_notification = false;
|
||||
|
||||
typedef Bool (*pfn_XF86DGAQueryVersion)(X11_Display *, int*, int*);
|
||||
typedef Status (*pfn_XF86DGADirectVideo)(X11_Display *, int, int);
|
||||
pfn_XF86DGADirectVideo _XF86DGADirectVideo;
|
||||
|
@ -1260,6 +1260,15 @@ open_window() {
|
||||
XDefineCursor(_display, _xwindow, cursor);
|
||||
}
|
||||
|
||||
// Set _NET_STARTUP_ID if we've got it, so that the window manager knows
|
||||
// this window belongs to a particular launch request.
|
||||
const std::string &startup_id = x11_pipe->get_startup_id();
|
||||
if (!startup_id.empty() && _parent_window_handle == nullptr) {
|
||||
XChangeProperty(_display, _xwindow, x11_pipe->_net_startup_id,
|
||||
x11_pipe->_utf8_string, 8, PropModeReplace,
|
||||
(unsigned char *)startup_id.c_str(), startup_id.size());
|
||||
}
|
||||
|
||||
XMapWindow(_display, _xwindow);
|
||||
|
||||
if (_properties.get_raw_mice()) {
|
||||
@ -1279,6 +1288,12 @@ open_window() {
|
||||
_parent_window_handle->attach_child(_window_handle);
|
||||
}
|
||||
|
||||
// Now that we've opened a window, tell the window manager that the
|
||||
// application has finished starting up.
|
||||
if (!startup_id.empty() && x_send_startup_notification) {
|
||||
x11_pipe->send_startup_notification();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user