panda3d/panda/src/x11display/x11GraphicsPipe.cxx
rdb 60acf82f04 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`
2022-12-01 15:52:15 +01:00

735 lines
24 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file x11GraphicsPipe.cxx
* @author rdb
* @date 2009-07-07
*/
#include "x11GraphicsPipe.h"
#include "x11GraphicsWindow.h"
#include "config_x11display.h"
#include "frameBufferProperties.h"
#include "displayInformation.h"
#include "pstrtod.h"
#include <dlfcn.h>
TypeHandle x11GraphicsPipe::_type_handle;
bool x11GraphicsPipe::_error_handlers_installed = false;
x11GraphicsPipe::ErrorHandlerFunc *x11GraphicsPipe::_prev_error_handler;
x11GraphicsPipe::IOErrorHandlerFunc *x11GraphicsPipe::_prev_io_error_handler;
bool x11GraphicsPipe::_x_error_messages_enabled = true;
int x11GraphicsPipe::_x_error_count = 0;
LightReMutex x11GraphicsPipe::_x_mutex;
/**
*
*/
x11GraphicsPipe::
x11GraphicsPipe(const std::string &display) :
_xcursor_size(-1),
_have_xrandr(false),
_XF86DGADirectVideo(nullptr) {
std::string display_spec = display;
if (display_spec.empty()) {
display_spec = display_cfg;
}
if (display_spec.empty()) {
display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
}
if (display_spec.empty()) {
display_spec = ":0.0";
}
// Store the current locale, so that we can restore it later.
std::string saved_locale;
char *saved_locale_p = setlocale(LC_ALL, nullptr);
if (saved_locale_p != nullptr) {
saved_locale = saved_locale_p;
}
// The X docs say we should do this to get international character support
// from the keyboard.
setlocale(LC_ALL, "");
// But it's important that we use the "C" locale for numeric formatting,
// since all of the internal Panda code assumes this--we need a decimal
// 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;
_screen = 0;
_root = (X11_Window)nullptr;
_im = (XIM)nullptr;
_hidden_cursor = None;
// According to the documentation, we should call this before making any
// other Xlib calls if we wish to use the Xlib locking system.
if (x_init_threads) {
XInitThreads();
}
install_error_handlers();
_display = XOpenDisplay(display_spec.c_str());
if (!_display) {
x11display_cat.error()
<< "Could not open display \"" << display_spec << "\".\n";
_is_valid = false;
_screen = None;
_root = None;
_display_width = 0;
_display_height = 0;
return;
}
if (!XSupportsLocale()) {
x11display_cat.warning()
<< "X does not support locale " << setlocale(LC_ALL, nullptr) << "\n";
}
XSetLocaleModifiers("");
_screen = DefaultScreen(_display);
_root = RootWindow(_display, _screen);
_display_width = DisplayWidth(_display, _screen);
_display_height = DisplayHeight(_display, _screen);
_is_valid = true;
// Dynamically load the xf86dga extension.
void *xf86dga = dlopen("libXxf86dga.so.1", RTLD_NOW | RTLD_LOCAL);
if (xf86dga != nullptr) {
pfn_XF86DGAQueryVersion _XF86DGAQueryVersion = (pfn_XF86DGAQueryVersion)dlsym(xf86dga, "XF86DGAQueryVersion");
_XF86DGADirectVideo = (pfn_XF86DGADirectVideo)dlsym(xf86dga, "XF86DGADirectVideo");
int major_ver, minor_ver;
if (_XF86DGAQueryVersion == nullptr || _XF86DGADirectVideo == nullptr) {
x11display_cat.warning()
<< "libXxf86dga.so.1 does not provide required functions; relative mouse mode may not work.\n";
} else if (!_XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) {
_XF86DGADirectVideo = nullptr;
}
} else {
_XF86DGADirectVideo = nullptr;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "cannot dlopen libXxf86dga.so.1; relative mouse mode may not work.\n";
}
}
// Dynamically load the XCursor extension.
void *xcursor = dlopen("libXcursor.so.1", RTLD_NOW | RTLD_LOCAL);
if (xcursor != nullptr) {
pfn_XcursorGetDefaultSize _XcursorGetDefaultSize = (pfn_XcursorGetDefaultSize)dlsym(xcursor, "XcursorGetDefaultSize");
_XcursorXcFileLoadImages = (pfn_XcursorXcFileLoadImages)dlsym(xcursor, "XcursorXcFileLoadImages");
_XcursorImagesLoadCursor = (pfn_XcursorImagesLoadCursor)dlsym(xcursor, "XcursorImagesLoadCursor");
_XcursorImagesDestroy = (pfn_XcursorImagesDestroy)dlsym(xcursor, "XcursorImagesDestroy");
_XcursorImageCreate = (pfn_XcursorImageCreate)dlsym(xcursor, "XcursorImageCreate");
_XcursorImageLoadCursor = (pfn_XcursorImageLoadCursor)dlsym(xcursor, "XcursorImageLoadCursor");
_XcursorImageDestroy = (pfn_XcursorImageDestroy)dlsym(xcursor, "XcursorImageDestroy");
if (_XcursorGetDefaultSize == nullptr || _XcursorXcFileLoadImages == nullptr ||
_XcursorImagesLoadCursor == nullptr || _XcursorImagesDestroy == nullptr ||
_XcursorImageCreate == nullptr || _XcursorImageLoadCursor == nullptr ||
_XcursorImageDestroy == nullptr) {
_xcursor_size = -1;
x11display_cat.warning()
<< "libXcursor.so.1 does not provide required functions; cursor changing will not work.\n";
} else if (x_cursor_size.get_value() >= 0) {
_xcursor_size = x_cursor_size;
} else {
_xcursor_size = _XcursorGetDefaultSize(_display);
}
} else {
_xcursor_size = -1;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "cannot dlopen libXcursor.so.1; cursor changing will not work.\n";
}
}
// Dynamically load the XRandr extension.
void *xrandr = dlopen("libXrandr.so.2", RTLD_NOW | RTLD_LOCAL);
if (xrandr != nullptr) {
pfn_XRRQueryExtension _XRRQueryExtension = (pfn_XRRQueryExtension)dlsym(xrandr, "XRRQueryExtension");
pfn_XRRQueryVersion _XRRQueryVersion = (pfn_XRRQueryVersion)dlsym(xrandr, "XRRQueryVersion");
_XRRSizes = (pfn_XRRSizes)dlsym(xrandr, "XRRSizes");
_XRRRates = (pfn_XRRRates)dlsym(xrandr, "XRRRates");
_XRRGetScreenInfo = (pfn_XRRGetScreenInfo)dlsym(xrandr, "XRRGetScreenInfo");
_XRRConfigCurrentConfiguration = (pfn_XRRConfigCurrentConfiguration)dlsym(xrandr, "XRRConfigCurrentConfiguration");
_XRRSetScreenConfig = (pfn_XRRSetScreenConfig)dlsym(xrandr, "XRRSetScreenConfig");
int event, error, major, minor;
if (_XRRQueryExtension == nullptr || _XRRSizes == nullptr || _XRRRates == nullptr ||
_XRRGetScreenInfo == nullptr || _XRRConfigCurrentConfiguration == nullptr ||
_XRRSetScreenConfig == nullptr || _XRRQueryVersion == nullptr) {
_have_xrandr = false;
x11display_cat.warning()
<< "libXrandr.so.2 does not provide required functions; resolution setting will not work.\n";
}
else if (_XRRQueryExtension(_display, &event, &error) &&
_XRRQueryVersion(_display, &major, &minor)) {
_have_xrandr = true;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Found RandR extension " << major << "." << minor << "\n";
}
if (major > 1 || (major == 1 && minor >= 2)) {
if (major > 1 || (major == 1 && minor >= 3)) {
_XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources)
dlsym(xrandr, "XRRGetScreenResourcesCurrent");
} else {
// Fall back to this slower version.
_XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources)
dlsym(xrandr, "XRRGetScreenResources");
}
_XRRFreeScreenResources = (pfn_XRRFreeScreenResources)dlsym(xrandr, "XRRFreeScreenResources");
_XRRGetCrtcInfo = (pfn_XRRGetCrtcInfo)dlsym(xrandr, "XRRGetCrtcInfo");
_XRRFreeCrtcInfo = (pfn_XRRFreeCrtcInfo)dlsym(xrandr, "XRRFreeCrtcInfo");
} else {
_XRRGetScreenResourcesCurrent = nullptr;
_XRRFreeScreenResources = nullptr;
_XRRGetCrtcInfo = nullptr;
_XRRFreeCrtcInfo = nullptr;
}
} else {
_have_xrandr = false;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "RandR extension not supported; resolution setting will not work.\n";
}
}
} else {
_have_xrandr = false;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "cannot dlopen libXrandr.so.2; resolution setting will not work.\n";
}
}
// Dynamically load the XInput2 extension.
int ev, err;
if (XQueryExtension(_display, "XInputExtension", &_xi_opcode, &ev, &err)) {
void *xi = dlopen("libXi.so.6", RTLD_NOW | RTLD_LOCAL);
if (xi != nullptr) {
pfn_XIQueryVersion _XIQueryVersion = (pfn_XIQueryVersion)dlsym(xi, "XIQueryVersion");
_XISelectEvents = (pfn_XISelectEvents)dlsym(xi, "XISelectEvents");
int major_ver = 2, minor_ver = 0;
if (_XIQueryVersion == nullptr || _XISelectEvents == nullptr) {
x11display_cat.warning()
<< "libXi.so.6 does not provide required functions; relative mouse mode will not work.\n";
_XISelectEvents = nullptr;
dlclose(xi);
} else if (_XIQueryVersion(_display, &major_ver, &minor_ver) == Success) {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Found XInput extension " << major_ver << "." << minor_ver << "\n";
}
} else {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "XInput2 extension not supported; relative mouse mode will not work.\n";
}
_XISelectEvents = nullptr;
dlclose(xi);
}
} else {
_XISelectEvents = nullptr;
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "cannot dlopen libXi.so.1; relative mouse mode will not work.\n";
}
}
}
// Use Xrandr to fill in the supported resolution list.
if (_have_xrandr) {
// If we have XRRGetScreenResources, we prefer that. It seems to be more
// reliable than XRRSizes in multi-monitor set-ups.
if (auto res = get_screen_resources()) {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Using XRRScreenResources to obtain display modes\n";
}
_display_information->_total_display_modes = res->nmode;
_display_information->_display_mode_array = new DisplayMode[res->nmode];
for (int i = 0; i < res->nmode; ++i) {
XRRModeInfo &mode = res->modes[i];
DisplayMode *dm = _display_information->_display_mode_array + i;
dm->width = mode.width;
dm->height = mode.height;
dm->bits_per_pixel = -1;
dm->fullscreen_only = false;
if (mode.hTotal && mode.vTotal) {
dm->refresh_rate = (double)mode.dotClock /
((double)mode.hTotal * (double)mode.vTotal);
} else {
dm->refresh_rate = 0;
}
}
} else {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Using XRRSizes and XRRRates to obtain display modes\n";
}
int num_sizes, num_rates;
XRRScreenSize *xrrs;
xrrs = _XRRSizes(_display, 0, &num_sizes);
_display_information->_total_display_modes = 0;
for (int i = 0; i < num_sizes; ++i) {
_XRRRates(_display, 0, i, &num_rates);
_display_information->_total_display_modes += num_rates;
}
short *rates;
short counter = 0;
_display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes];
for (int i = 0; i < num_sizes; ++i) {
int num_rates;
rates = _XRRRates(_display, 0, i, &num_rates);
for (int j = 0; j < num_rates; ++j) {
DisplayMode* dm = _display_information->_display_mode_array + counter;
dm->width = xrrs[i].width;
dm->height = xrrs[i].height;
dm->refresh_rate = rates[j];
dm->bits_per_pixel = -1;
dm->fullscreen_only = false;
++counter;
}
}
}
}
// Connect to an input method for supporting international text entry.
_im = XOpenIM(_display, nullptr, nullptr, nullptr);
if (_im == (XIM)nullptr) {
// Fall back to internal input method.
XSetLocaleModifiers("@im=none");
_im = XOpenIM(_display, nullptr, nullptr, nullptr);
if (_im == (XIM)nullptr) {
x11display_cat.warning()
<< "Couldn't open input method.\n";
}
}
// What styles does the current input method support?
/*
XIMStyles *im_supported_styles;
XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
for (int i = 0; i < im_supported_styles->count_styles; i++) {
XIMStyle style = im_supported_styles->supported_styles[i];
cerr << "style " << i << ". " << hex << style << dec << "\n";
}
XFree(im_supported_styles);
*/
// Restore the previous locale.
if (!saved_locale.empty()) {
setlocale(LC_ALL, saved_locale.c_str());
}
const char *dpi = XGetDefault(_display, "Xft", "dpi");
if (dpi != nullptr) {
char *endptr = nullptr;
double result = pstrtod(dpi, &endptr);
if (result != 0 && !cnan(result) && endptr[0] == '\0') {
result /= 96;
set_detected_display_zoom(result);
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Determined display zoom to be " << result
<< " based on specified Xft.dpi " << dpi << "\n";
}
} else {
x11display_cat.warning()
<< "Unable to determine display zoom because Xft.dpi is invalid: "
<< dpi << "\n";
}
} else if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Unable to determine display zoom because Xft.dpi was not set.\n";
}
// 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_state = XInternAtom(_display, "_NET_WM_STATE", false);
_net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
_net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", 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);
}
/**
*
*/
x11GraphicsPipe::
~x11GraphicsPipe() {
release_hidden_cursor();
if (_im) {
XCloseIM(_im);
}
if (_display) {
XCloseDisplay(_display);
}
}
/**
* 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.
*/
bool x11GraphicsPipe::
enable_raw_mouse() {
if (_num_raw_mouse_windows > 0) {
// Already enabled by another window.
++_num_raw_mouse_windows;
return true;
}
if (_XISelectEvents != nullptr) {
XIEventMask event_mask;
unsigned char mask[XIMaskLen(XI_RawMotion)] = {0};
event_mask.deviceid = XIAllMasterDevices;
event_mask.mask_len = sizeof(mask);
event_mask.mask = mask;
XISetMask(mask, XI_RawMotion);
if (_XISelectEvents(_display, _root, &event_mask, 1) == Success) {
if (x11display_cat.is_info()) {
x11display_cat.info()
<< "Enabled raw mouse events using XInput2 extension\n";
}
++_num_raw_mouse_windows;
return true;
}
}
return false;
}
/**
* Disables raw mouse mode for this display.
*/
void x11GraphicsPipe::
disable_raw_mouse() {
if (--_num_raw_mouse_windows == 0) {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< "Disabling raw mouse events using XInput2 extension\n";
}
XIEventMask event_mask;
unsigned char mask[] = {0};
event_mask.deviceid = XIAllMasterDevices;
event_mask.mask_len = sizeof(mask);
event_mask.mask = mask;
_XISelectEvents(_display, _root, &event_mask, 1);
}
}
/**
* Returns an XRRScreenResources object, or null if RandR 1.2 is not supported.
*/
std::unique_ptr<XRRScreenResources, pfn_XRRFreeScreenResources> x11GraphicsPipe::
get_screen_resources() const {
XRRScreenResources *res = nullptr;
if (_have_xrandr && _XRRGetScreenResourcesCurrent != nullptr) {
res = _XRRGetScreenResourcesCurrent(_display, _root);
}
return std::unique_ptr<XRRScreenResources, pfn_XRRFreeScreenResources>(res, _XRRFreeScreenResources);
}
/**
* Returns an XRRCrtcInfo object, or null if RandR 1.2 is not supported.
*/
std::unique_ptr<XRRCrtcInfo, pfn_XRRFreeCrtcInfo> x11GraphicsPipe::
get_crtc_info(XRRScreenResources *res, RRCrtc crtc) const {
XRRCrtcInfo *info = nullptr;
if (_have_xrandr && _XRRGetCrtcInfo != nullptr) {
info = _XRRGetCrtcInfo(_display, res, crtc);
}
return std::unique_ptr<XRRCrtcInfo, pfn_XRRFreeCrtcInfo>(info, _XRRFreeCrtcInfo);
}
/**
* Finds a CRTC for going fullscreen to, at the given origin. The new CRTC
* is returned, along with its x, y, width and height.
*
* If the required RandR extension is not supported, a value of None will be
* returned, but x, y, width and height will still be populated.
*/
RRCrtc x11GraphicsPipe::
find_fullscreen_crtc(const LPoint2i &point,
int &x, int &y, int &width, int &height) {
x = 0;
y = 0;
width = DisplayWidth(_display, _screen);
height = DisplayHeight(_display, _screen);
if (auto res = get_screen_resources()) {
for (int i = 0; i < res->ncrtc; ++i) {
RRCrtc crtc = res->crtcs[i];
if (auto info = get_crtc_info(res.get(), crtc)) {
if (point[0] >= info->x && point[0] < info->x + (int)info->width &&
point[1] >= info->y && point[1] < info->y + (int)info->height) {
x = info->x;
y = info->y;
width = (int)info->width;
height = (int)info->height;
return crtc;
}
}
}
}
return None;
}
/**
* Returns an indication of the thread in which this GraphicsPipe requires its
* window processing to be performed: typically either the app thread (e.g.
* X) or the draw thread (Windows).
*/
GraphicsPipe::PreferredWindowThread
x11GraphicsPipe::get_preferred_window_thread() const {
// Actually, since we're creating the graphics context in open_window() now,
// it appears we need to ensure the open_window() call is performed in the
// draw thread for now, even though X wants all of its calls to be single-
// threaded.
// This means that all X windows may have to be handled by the same draw
// thread, which we didn't intend (though the global _x_mutex may allow them
// to be technically served by different threads, even though the actual X
// calls will be serialized). There might be a better way.
return PWT_draw;
}
/**
* Called once to make an invisible Cursor for return from
* get_hidden_cursor().
*/
void x11GraphicsPipe::
make_hidden_cursor() {
nassertv(_hidden_cursor == None);
unsigned int x_size, y_size;
XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
XColor black;
memset(&black, 0, sizeof(black));
_hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
&black, &black, x_size, y_size);
XFreePixmap(_display, empty);
}
/**
* Called once to release the invisible cursor created by
* make_hidden_cursor().
*/
void x11GraphicsPipe::
release_hidden_cursor() {
if (_hidden_cursor != None) {
XFreeCursor(_display, _hidden_cursor);
_hidden_cursor = None;
}
}
/**
* Installs new Xlib error handler functions if this is the first time this
* function has been called. These error handler functions will attempt to
* reduce Xlib's annoying tendency to shut down the client at the first error.
* Unfortunately, it is difficult to play nice with the client if it has
* already installed its own error handlers.
*/
void x11GraphicsPipe::
install_error_handlers() {
if (_error_handlers_installed) {
return;
}
_prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
_prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
_error_handlers_installed = true;
}
/**
* This function is installed as the error handler for a non-fatal Xlib error.
*/
int x11GraphicsPipe::
error_handler(X11_Display *display, XErrorEvent *error) {
++_x_error_count;
static const int msg_len = 80;
char msg[msg_len];
XGetErrorText(display, error->error_code, msg, msg_len);
if (!_x_error_messages_enabled) {
if (x11display_cat.is_debug()) {
x11display_cat.debug()
<< msg << "\n";
}
return 0;
}
x11display_cat.error()
<< msg << "\n";
if (x_error_abort) {
abort();
}
// We return to allow the application to continue running, unlike the
// default X error handler which exits.
return 0;
}
/**
* This function is installed as the error handler for a fatal Xlib error.
*/
int x11GraphicsPipe::
io_error_handler(X11_Display *display) {
x11display_cat.fatal()
<< "X fatal error on display " << (void *)display << "\n";
// Unfortunately, we can't continue from this function, even if we promise
// never to use X again. We're supposed to terminate without returning, and
// if we do return, the caller will exit anyway. Sigh. Very poor design on
// X's part.
return 0;
}