Panda3D
x11GraphicsPipe.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file x11GraphicsPipe.cxx
10  * @author rdb
11  * @date 2009-07-07
12  */
13 
14 #include "x11GraphicsPipe.h"
15 #include "x11GraphicsWindow.h"
16 #include "config_x11display.h"
17 #include "frameBufferProperties.h"
18 #include "displayInformation.h"
19 
20 #include <dlfcn.h>
21 
22 TypeHandle x11GraphicsPipe::_type_handle;
23 
24 bool x11GraphicsPipe::_error_handlers_installed = false;
25 x11GraphicsPipe::ErrorHandlerFunc *x11GraphicsPipe::_prev_error_handler;
26 x11GraphicsPipe::IOErrorHandlerFunc *x11GraphicsPipe::_prev_io_error_handler;
27 bool x11GraphicsPipe::_x_error_messages_enabled = true;
28 int x11GraphicsPipe::_x_error_count = 0;
29 
30 LightReMutex x11GraphicsPipe::_x_mutex;
31 
32 /**
33  *
34  */
35 x11GraphicsPipe::
36 x11GraphicsPipe(const std::string &display) :
37  _have_xrandr(false),
38  _xcursor_size(-1),
39  _XF86DGADirectVideo(nullptr) {
40 
41  std::string display_spec = display;
42  if (display_spec.empty()) {
43  display_spec = display_cfg;
44  }
45  if (display_spec.empty()) {
46  display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
47  }
48  if (display_spec.empty()) {
49  display_spec = ":0.0";
50  }
51 
52  // The X docs say we should do this to get international character support
53  // from the keyboard.
54  setlocale(LC_ALL, "");
55 
56  // But it's important that we use the "C" locale for numeric formatting,
57  // since all of the internal Panda code assumes this--we need a decimal
58  // point to mean a decimal point.
59  setlocale(LC_NUMERIC, "C");
60 
61  _is_valid = false;
62  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
63  _display = nullptr;
64  _screen = 0;
65  _root = (X11_Window)nullptr;
66  _im = (XIM)nullptr;
67  _hidden_cursor = None;
68 
69  // According to the documentation, we should call this before making any
70  // other Xlib calls if we wish to use the Xlib locking system.
71  if (x_init_threads) {
72  XInitThreads();
73  }
74 
75  install_error_handlers();
76 
77  _display = XOpenDisplay(display_spec.c_str());
78  if (!_display) {
79  x11display_cat.error()
80  << "Could not open display \"" << display_spec << "\".\n";
81  _is_valid = false;
82  _screen = None;
83  _root = None;
84  _display_width = 0;
85  _display_height = 0;
86  return;
87  }
88 
89  if (!XSupportsLocale()) {
90  x11display_cat.warning()
91  << "X does not support locale " << setlocale(LC_ALL, nullptr) << "\n";
92  }
93  XSetLocaleModifiers("");
94 
95  _screen = DefaultScreen(_display);
96  _root = RootWindow(_display, _screen);
97  _display_width = DisplayWidth(_display, _screen);
98  _display_height = DisplayHeight(_display, _screen);
99  _is_valid = true;
100 
101  // Dynamically load the xf86dga extension.
102  void *xf86dga = dlopen("libXxf86dga.so.1", RTLD_NOW | RTLD_LOCAL);
103  if (xf86dga != nullptr) {
104  pfn_XF86DGAQueryVersion _XF86DGAQueryVersion = (pfn_XF86DGAQueryVersion)dlsym(xf86dga, "XF86DGAQueryVersion");
105  _XF86DGADirectVideo = (pfn_XF86DGADirectVideo)dlsym(xf86dga, "XF86DGADirectVideo");
106 
107  int major_ver, minor_ver;
108  if (_XF86DGAQueryVersion == nullptr || _XF86DGADirectVideo == nullptr) {
109  x11display_cat.warning()
110  << "libXxf86dga.so.1 does not provide required functions; relative mouse mode will not work.\n";
111 
112  } else if (!_XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) {
113  _XF86DGADirectVideo = nullptr;
114  }
115  } else {
116  _XF86DGADirectVideo = nullptr;
117  if (x11display_cat.is_debug()) {
118  x11display_cat.debug()
119  << "cannot dlopen libXxf86dga.so.1; cursor changing will not work.\n";
120  }
121  }
122 
123  // Dynamically load the XCursor extension.
124  void *xcursor = dlopen("libXcursor.so.1", RTLD_NOW | RTLD_LOCAL);
125  if (xcursor != nullptr) {
126  pfn_XcursorGetDefaultSize _XcursorGetDefaultSize = (pfn_XcursorGetDefaultSize)dlsym(xcursor, "XcursorGetDefaultSize");
127  _XcursorXcFileLoadImages = (pfn_XcursorXcFileLoadImages)dlsym(xcursor, "XcursorXcFileLoadImages");
128  _XcursorImagesLoadCursor = (pfn_XcursorImagesLoadCursor)dlsym(xcursor, "XcursorImagesLoadCursor");
129  _XcursorImagesDestroy = (pfn_XcursorImagesDestroy)dlsym(xcursor, "XcursorImagesDestroy");
130  _XcursorImageCreate = (pfn_XcursorImageCreate)dlsym(xcursor, "XcursorImageCreate");
131  _XcursorImageLoadCursor = (pfn_XcursorImageLoadCursor)dlsym(xcursor, "XcursorImageLoadCursor");
132  _XcursorImageDestroy = (pfn_XcursorImageDestroy)dlsym(xcursor, "XcursorImageDestroy");
133 
134  if (_XcursorGetDefaultSize == nullptr || _XcursorXcFileLoadImages == nullptr ||
135  _XcursorImagesLoadCursor == nullptr || _XcursorImagesDestroy == nullptr ||
136  _XcursorImageCreate == nullptr || _XcursorImageLoadCursor == nullptr ||
137  _XcursorImageDestroy == nullptr) {
138  _xcursor_size = -1;
139  x11display_cat.warning()
140  << "libXcursor.so.1 does not provide required functions; cursor changing will not work.\n";
141 
142  } else if (x_cursor_size.get_value() >= 0) {
143  _xcursor_size = x_cursor_size;
144  } else {
145  _xcursor_size = _XcursorGetDefaultSize(_display);
146  }
147  } else {
148  _xcursor_size = -1;
149  if (x11display_cat.is_debug()) {
150  x11display_cat.debug()
151  << "cannot dlopen libXcursor.so.1; cursor changing will not work.\n";
152  }
153  }
154 
155  // Dynamically load the XRandr extension.
156  void *xrandr = dlopen("libXrandr.so.2", RTLD_NOW | RTLD_LOCAL);
157  if (xrandr != nullptr) {
158  pfn_XRRQueryExtension _XRRQueryExtension = (pfn_XRRQueryExtension)dlsym(xrandr, "XRRQueryExtension");
159  _XRRSizes = (pfn_XRRSizes)dlsym(xrandr, "XRRSizes");
160  _XRRRates = (pfn_XRRRates)dlsym(xrandr, "XRRRates");
161  _XRRGetScreenInfo = (pfn_XRRGetScreenInfo)dlsym(xrandr, "XRRGetScreenInfo");
162  _XRRConfigCurrentConfiguration = (pfn_XRRConfigCurrentConfiguration)dlsym(xrandr, "XRRConfigCurrentConfiguration");
163  _XRRSetScreenConfig = (pfn_XRRSetScreenConfig)dlsym(xrandr, "XRRSetScreenConfig");
164 
165  if (_XRRQueryExtension == nullptr || _XRRSizes == nullptr || _XRRRates == nullptr ||
166  _XRRGetScreenInfo == nullptr || _XRRConfigCurrentConfiguration == nullptr ||
167  _XRRSetScreenConfig == nullptr) {
168  _have_xrandr = false;
169  x11display_cat.warning()
170  << "libXrandr.so.2 does not provide required functions; resolution setting will not work.\n";
171  } else {
172  int event, error;
173  _have_xrandr = _XRRQueryExtension(_display, &event, &error);
174  }
175  } else {
176  _have_xrandr = false;
177  if (x11display_cat.is_debug()) {
178  x11display_cat.debug()
179  << "cannot dlopen libXrandr.so.2; resolution setting will not work.\n";
180  }
181  }
182 
183  // Use Xrandr to fill in the supported resolution list.
184  if (_have_xrandr) {
185  int num_sizes, num_rates;
186  XRRScreenSize *xrrs;
187  xrrs = _XRRSizes(_display, 0, &num_sizes);
188  _display_information->_total_display_modes = 0;
189  for (int i = 0; i < num_sizes; ++i) {
190  _XRRRates(_display, 0, i, &num_rates);
191  _display_information->_total_display_modes += num_rates;
192  }
193 
194  short *rates;
195  short counter = 0;
196  _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes];
197  for (int i = 0; i < num_sizes; ++i) {
198  int num_rates;
199  rates = _XRRRates(_display, 0, i, &num_rates);
200  for (int j = 0; j < num_rates; ++j) {
201  DisplayMode* dm = _display_information->_display_mode_array + counter;
202  dm->width = xrrs[i].width;
203  dm->height = xrrs[i].height;
204  dm->refresh_rate = rates[j];
205  dm->bits_per_pixel = -1;
206  dm->fullscreen_only = false;
207  ++counter;
208  }
209  }
210  }
211 
212  // Connect to an input method for supporting international text entry.
213  _im = XOpenIM(_display, nullptr, nullptr, nullptr);
214  if (_im == (XIM)nullptr) {
215  x11display_cat.warning()
216  << "Couldn't open input method.\n";
217  }
218 
219  // What styles does the current input method support?
220  /*
221  XIMStyles *im_supported_styles;
222  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
223 
224  for (int i = 0; i < im_supported_styles->count_styles; i++) {
225  XIMStyle style = im_supported_styles->supported_styles[i];
226  cerr << "style " << i << ". " << hex << style << dec << "\n";
227  }
228 
229  XFree(im_supported_styles);
230  */
231 
232  // Get some X atom numbers.
233  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
234  _net_wm_pid = XInternAtom(_display, "_NET_WM_PID", false);
235  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
236  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
237  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
238  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
239  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
240  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
241  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
242  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
243  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
244  _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
245 }
246 
247 /**
248  *
249  */
250 x11GraphicsPipe::
251 ~x11GraphicsPipe() {
252  release_hidden_cursor();
253  if (_im) {
254  XCloseIM(_im);
255  }
256  if (_display) {
257  XCloseDisplay(_display);
258  }
259 }
260 
261 /**
262  * Returns an indication of the thread in which this GraphicsPipe requires its
263  * window processing to be performed: typically either the app thread (e.g.
264  * X) or the draw thread (Windows).
265  */
266 GraphicsPipe::PreferredWindowThread
268  // Actually, since we're creating the graphics context in open_window() now,
269  // it appears we need to ensure the open_window() call is performed in the
270  // draw thread for now, even though X wants all of its calls to be single-
271  // threaded.
272 
273  // This means that all X windows may have to be handled by the same draw
274  // thread, which we didn't intend (though the global _x_mutex may allow them
275  // to be technically served by different threads, even though the actual X
276  // calls will be serialized). There might be a better way.
277 
278  return PWT_draw;
279 }
280 
281 /**
282  * Called once to make an invisible Cursor for return from
283  * get_hidden_cursor().
284  */
285 void x11GraphicsPipe::
286 make_hidden_cursor() {
287  nassertv(_hidden_cursor == None);
288 
289  unsigned int x_size, y_size;
290  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
291 
292  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
293 
294  XColor black;
295  memset(&black, 0, sizeof(black));
296 
297  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
298  &black, &black, x_size, y_size);
299  XFreePixmap(_display, empty);
300 }
301 
302 /**
303  * Called once to release the invisible cursor created by
304  * make_hidden_cursor().
305  */
306 void x11GraphicsPipe::
307 release_hidden_cursor() {
308  if (_hidden_cursor != None) {
309  XFreeCursor(_display, _hidden_cursor);
310  _hidden_cursor = None;
311  }
312 }
313 
314 /**
315  * Installs new Xlib error handler functions if this is the first time this
316  * function has been called. These error handler functions will attempt to
317  * reduce Xlib's annoying tendency to shut down the client at the first error.
318  * Unfortunately, it is difficult to play nice with the client if it has
319  * already installed its own error handlers.
320  */
321 void x11GraphicsPipe::
322 install_error_handlers() {
323  if (_error_handlers_installed) {
324  return;
325  }
326 
327  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
328  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
329  _error_handlers_installed = true;
330 }
331 
332 /**
333  * This function is installed as the error handler for a non-fatal Xlib error.
334  */
335 int x11GraphicsPipe::
336 error_handler(X11_Display *display, XErrorEvent *error) {
337  ++_x_error_count;
338 
339  static const int msg_len = 80;
340  char msg[msg_len];
341  XGetErrorText(display, error->error_code, msg, msg_len);
342 
343  if (!_x_error_messages_enabled) {
344  if (x11display_cat.is_debug()) {
345  x11display_cat.debug()
346  << msg << "\n";
347  }
348  return 0;
349  }
350 
351  x11display_cat.error()
352  << msg << "\n";
353 
354  if (x_error_abort) {
355  abort();
356  }
357 
358  // We return to allow the application to continue running, unlike the
359  // default X error handler which exits.
360  return 0;
361 }
362 
363 /**
364  * This function is installed as the error handler for a fatal Xlib error.
365  */
366 int x11GraphicsPipe::
367 io_error_handler(X11_Display *display) {
368  x11display_cat.fatal()
369  << "X fatal error on display " << (void *)display << "\n";
370 
371  // Unfortunately, we can't continue from this function, even if we promise
372  // never to use X again. We're supposed to terminate without returning, and
373  // if we do return, the caller will exit anyway. Sigh. Very poor design on
374  // X's part.
375  return 0;
376 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A lightweight reentrant mutex.
Definition: lightReMutex.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_value
Returns the variable's value.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...