mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 02:15:43 -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 "
|
PRC_DESC("Specify the value to use for the res_class field of the window's "
|
||||||
"WM_CLASS property."));
|
"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
|
* 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
|
* 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 ConfigVariableInt x_cursor_size;
|
||||||
extern ConfigVariableString x_wm_class_name;
|
extern ConfigVariableString x_wm_class_name;
|
||||||
extern ConfigVariableString x_wm_class;
|
extern ConfigVariableString x_wm_class;
|
||||||
|
extern ConfigVariableBool x_send_startup_notification;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,6 +57,15 @@ get_hidden_cursor() {
|
|||||||
return _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.
|
* 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.
|
// point to mean a decimal point.
|
||||||
setlocale(LC_NUMERIC, "C");
|
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;
|
_is_valid = false;
|
||||||
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
|
_supported_types = OT_window | OT_buffer | OT_texture_buffer;
|
||||||
_display = nullptr;
|
_display = nullptr;
|
||||||
@ -375,21 +389,25 @@ x11GraphicsPipe(const std::string &display) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get some X atom numbers.
|
// Get some X atom numbers.
|
||||||
|
_utf8_string = XInternAtom(_display, "UTF8_STRING", false);
|
||||||
_wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", 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_pid = XInternAtom(_display, "_NET_WM_PID", false);
|
||||||
_net_wm_ping = XInternAtom(_display, "_NET_WM_PING", 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 = 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_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_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
|
||||||
_net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
|
_net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
|
||||||
_net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
|
_net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
|
||||||
_net_wm_state_maximized_vert = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_VERT", false);
|
|
||||||
_net_wm_state_maximized_horz = XInternAtom(_display, "_NET_WM_STATE_MAXIMIZED_HORZ", 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.
|
* Enables raw mouse mode for this display. Returns false if unsupported.
|
||||||
*/
|
*/
|
||||||
|
@ -144,6 +144,9 @@ public:
|
|||||||
|
|
||||||
INLINE X11_Cursor get_hidden_cursor();
|
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 supports_relative_mouse() const;
|
||||||
INLINE bool enable_dga_mouse();
|
INLINE bool enable_dga_mouse();
|
||||||
INLINE void disable_dga_mouse();
|
INLINE void disable_dga_mouse();
|
||||||
@ -165,21 +168,25 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Atom specifications.
|
// Atom specifications.
|
||||||
|
Atom _utf8_string;
|
||||||
Atom _wm_delete_window;
|
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_pid;
|
||||||
Atom _net_wm_ping;
|
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;
|
||||||
Atom _net_wm_state_fullscreen;
|
|
||||||
Atom _net_wm_state_above;
|
Atom _net_wm_state_above;
|
||||||
Atom _net_wm_state_below;
|
|
||||||
Atom _net_wm_state_add;
|
Atom _net_wm_state_add;
|
||||||
Atom _net_wm_state_remove;
|
Atom _net_wm_state_below;
|
||||||
Atom _net_wm_bypass_compositor;
|
Atom _net_wm_state_fullscreen;
|
||||||
Atom _net_wm_state_maximized_vert;
|
|
||||||
Atom _net_wm_state_maximized_horz;
|
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.
|
// Extension functions.
|
||||||
typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *);
|
typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *);
|
||||||
@ -224,6 +231,9 @@ protected:
|
|||||||
|
|
||||||
X11_Cursor _hidden_cursor;
|
X11_Cursor _hidden_cursor;
|
||||||
|
|
||||||
|
std::string _startup_id;
|
||||||
|
bool _sent_startup_notification = false;
|
||||||
|
|
||||||
typedef Bool (*pfn_XF86DGAQueryVersion)(X11_Display *, int*, int*);
|
typedef Bool (*pfn_XF86DGAQueryVersion)(X11_Display *, int*, int*);
|
||||||
typedef Status (*pfn_XF86DGADirectVideo)(X11_Display *, int, int);
|
typedef Status (*pfn_XF86DGADirectVideo)(X11_Display *, int, int);
|
||||||
pfn_XF86DGADirectVideo _XF86DGADirectVideo;
|
pfn_XF86DGADirectVideo _XF86DGADirectVideo;
|
||||||
|
@ -1260,6 +1260,15 @@ open_window() {
|
|||||||
XDefineCursor(_display, _xwindow, cursor);
|
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);
|
XMapWindow(_display, _xwindow);
|
||||||
|
|
||||||
if (_properties.get_raw_mice()) {
|
if (_properties.get_raw_mice()) {
|
||||||
@ -1279,6 +1288,12 @@ open_window() {
|
|||||||
_parent_window_handle->attach_child(_window_handle);
|
_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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user