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 #include "pstrtod.h"
20 
21 #include <dlfcn.h>
22 
23 TypeHandle x11GraphicsPipe::_type_handle;
24 
25 bool x11GraphicsPipe::_error_handlers_installed = false;
26 x11GraphicsPipe::ErrorHandlerFunc *x11GraphicsPipe::_prev_error_handler;
27 x11GraphicsPipe::IOErrorHandlerFunc *x11GraphicsPipe::_prev_io_error_handler;
28 bool x11GraphicsPipe::_x_error_messages_enabled = true;
29 int x11GraphicsPipe::_x_error_count = 0;
30 
31 LightReMutex x11GraphicsPipe::_x_mutex;
32 
33 /**
34  *
35  */
36 x11GraphicsPipe::
37 x11GraphicsPipe(const std::string &display) :
38  _have_xrandr(false),
39  _xcursor_size(-1),
40  _XF86DGADirectVideo(nullptr) {
41 
42  std::string display_spec = display;
43  if (display_spec.empty()) {
44  display_spec = display_cfg;
45  }
46  if (display_spec.empty()) {
47  display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
48  }
49  if (display_spec.empty()) {
50  display_spec = ":0.0";
51  }
52 
53  // The X docs say we should do this to get international character support
54  // from the keyboard.
55  setlocale(LC_ALL, "");
56 
57  // But it's important that we use the "C" locale for numeric formatting,
58  // since all of the internal Panda code assumes this--we need a decimal
59  // point to mean a decimal point.
60  setlocale(LC_NUMERIC, "C");
61 
62  _is_valid = false;
63  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
64  _display = nullptr;
65  _screen = 0;
66  _root = (X11_Window)nullptr;
67  _im = (XIM)nullptr;
68  _hidden_cursor = None;
69 
70  // According to the documentation, we should call this before making any
71  // other Xlib calls if we wish to use the Xlib locking system.
72  if (x_init_threads) {
73  XInitThreads();
74  }
75 
76  install_error_handlers();
77 
78  _display = XOpenDisplay(display_spec.c_str());
79  if (!_display) {
80  x11display_cat.error()
81  << "Could not open display \"" << display_spec << "\".\n";
82  _is_valid = false;
83  _screen = None;
84  _root = None;
85  _display_width = 0;
86  _display_height = 0;
87  return;
88  }
89 
90  if (!XSupportsLocale()) {
91  x11display_cat.warning()
92  << "X does not support locale " << setlocale(LC_ALL, nullptr) << "\n";
93  }
94  XSetLocaleModifiers("");
95 
96  _screen = DefaultScreen(_display);
97  _root = RootWindow(_display, _screen);
98  _display_width = DisplayWidth(_display, _screen);
99  _display_height = DisplayHeight(_display, _screen);
100  _is_valid = true;
101 
102  // Dynamically load the xf86dga extension.
103  void *xf86dga = dlopen("libXxf86dga.so.1", RTLD_NOW | RTLD_LOCAL);
104  if (xf86dga != nullptr) {
105  pfn_XF86DGAQueryVersion _XF86DGAQueryVersion = (pfn_XF86DGAQueryVersion)dlsym(xf86dga, "XF86DGAQueryVersion");
106  _XF86DGADirectVideo = (pfn_XF86DGADirectVideo)dlsym(xf86dga, "XF86DGADirectVideo");
107 
108  int major_ver, minor_ver;
109  if (_XF86DGAQueryVersion == nullptr || _XF86DGADirectVideo == nullptr) {
110  x11display_cat.warning()
111  << "libXxf86dga.so.1 does not provide required functions; relative mouse mode will not work.\n";
112 
113  } else if (!_XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) {
114  _XF86DGADirectVideo = nullptr;
115  }
116  } else {
117  _XF86DGADirectVideo = nullptr;
118  if (x11display_cat.is_debug()) {
119  x11display_cat.debug()
120  << "cannot dlopen libXxf86dga.so.1; cursor changing will not work.\n";
121  }
122  }
123 
124  // Dynamically load the XCursor extension.
125  void *xcursor = dlopen("libXcursor.so.1", RTLD_NOW | RTLD_LOCAL);
126  if (xcursor != nullptr) {
127  pfn_XcursorGetDefaultSize _XcursorGetDefaultSize = (pfn_XcursorGetDefaultSize)dlsym(xcursor, "XcursorGetDefaultSize");
128  _XcursorXcFileLoadImages = (pfn_XcursorXcFileLoadImages)dlsym(xcursor, "XcursorXcFileLoadImages");
129  _XcursorImagesLoadCursor = (pfn_XcursorImagesLoadCursor)dlsym(xcursor, "XcursorImagesLoadCursor");
130  _XcursorImagesDestroy = (pfn_XcursorImagesDestroy)dlsym(xcursor, "XcursorImagesDestroy");
131  _XcursorImageCreate = (pfn_XcursorImageCreate)dlsym(xcursor, "XcursorImageCreate");
132  _XcursorImageLoadCursor = (pfn_XcursorImageLoadCursor)dlsym(xcursor, "XcursorImageLoadCursor");
133  _XcursorImageDestroy = (pfn_XcursorImageDestroy)dlsym(xcursor, "XcursorImageDestroy");
134 
135  if (_XcursorGetDefaultSize == nullptr || _XcursorXcFileLoadImages == nullptr ||
136  _XcursorImagesLoadCursor == nullptr || _XcursorImagesDestroy == nullptr ||
137  _XcursorImageCreate == nullptr || _XcursorImageLoadCursor == nullptr ||
138  _XcursorImageDestroy == nullptr) {
139  _xcursor_size = -1;
140  x11display_cat.warning()
141  << "libXcursor.so.1 does not provide required functions; cursor changing will not work.\n";
142 
143  } else if (x_cursor_size.get_value() >= 0) {
144  _xcursor_size = x_cursor_size;
145  } else {
146  _xcursor_size = _XcursorGetDefaultSize(_display);
147  }
148  } else {
149  _xcursor_size = -1;
150  if (x11display_cat.is_debug()) {
151  x11display_cat.debug()
152  << "cannot dlopen libXcursor.so.1; cursor changing will not work.\n";
153  }
154  }
155 
156  // Dynamically load the XRandr extension.
157  void *xrandr = dlopen("libXrandr.so.2", RTLD_NOW | RTLD_LOCAL);
158  if (xrandr != nullptr) {
159  pfn_XRRQueryExtension _XRRQueryExtension = (pfn_XRRQueryExtension)dlsym(xrandr, "XRRQueryExtension");
160  pfn_XRRQueryVersion _XRRQueryVersion = (pfn_XRRQueryVersion)dlsym(xrandr, "XRRQueryVersion");
161 
162  _XRRSizes = (pfn_XRRSizes)dlsym(xrandr, "XRRSizes");
163  _XRRRates = (pfn_XRRRates)dlsym(xrandr, "XRRRates");
164  _XRRGetScreenInfo = (pfn_XRRGetScreenInfo)dlsym(xrandr, "XRRGetScreenInfo");
165  _XRRConfigCurrentConfiguration = (pfn_XRRConfigCurrentConfiguration)dlsym(xrandr, "XRRConfigCurrentConfiguration");
166  _XRRSetScreenConfig = (pfn_XRRSetScreenConfig)dlsym(xrandr, "XRRSetScreenConfig");
167 
168  int event, error, major, minor;
169  if (_XRRQueryExtension == nullptr || _XRRSizes == nullptr || _XRRRates == nullptr ||
170  _XRRGetScreenInfo == nullptr || _XRRConfigCurrentConfiguration == nullptr ||
171  _XRRSetScreenConfig == nullptr || _XRRQueryVersion == nullptr) {
172  _have_xrandr = false;
173  x11display_cat.warning()
174  << "libXrandr.so.2 does not provide required functions; resolution setting will not work.\n";
175  }
176  else if (_XRRQueryExtension(_display, &event, &error) &&
177  _XRRQueryVersion(_display, &major, &minor)) {
178  _have_xrandr = true;
179  if (x11display_cat.is_debug()) {
180  x11display_cat.debug()
181  << "Found RandR extension " << major << "." << minor << "\n";
182  }
183 
184  if (major > 1 || (major == 1 && minor >= 2)) {
185  if (major > 1 || (major == 1 && minor >= 3)) {
186  _XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources)
187  dlsym(xrandr, "XRRGetScreenResourcesCurrent");
188  } else {
189  // Fall back to this slower version.
190  _XRRGetScreenResourcesCurrent = (pfn_XRRGetScreenResources)
191  dlsym(xrandr, "XRRGetScreenResources");
192  }
193 
194  _XRRFreeScreenResources = (pfn_XRRFreeScreenResources)dlsym(xrandr, "XRRFreeScreenResources");
195  _XRRGetCrtcInfo = (pfn_XRRGetCrtcInfo)dlsym(xrandr, "XRRGetCrtcInfo");
196  _XRRFreeCrtcInfo = (pfn_XRRFreeCrtcInfo)dlsym(xrandr, "XRRFreeCrtcInfo");
197  } else {
198  _XRRGetScreenResourcesCurrent = nullptr;
199  _XRRFreeScreenResources = nullptr;
200  _XRRGetCrtcInfo = nullptr;
201  _XRRFreeCrtcInfo = nullptr;
202  }
203  } else {
204  _have_xrandr = false;
205  if (x11display_cat.is_debug()) {
206  x11display_cat.debug()
207  << "RandR extension not supported; resolution setting will not work.\n";
208  }
209  }
210  } else {
211  _have_xrandr = false;
212  if (x11display_cat.is_debug()) {
213  x11display_cat.debug()
214  << "cannot dlopen libXrandr.so.2; resolution setting will not work.\n";
215  }
216  }
217 
218  // Use Xrandr to fill in the supported resolution list.
219  if (_have_xrandr) {
220  // If we have XRRGetScreenResources, we prefer that. It seems to be more
221  // reliable than XRRSizes in multi-monitor set-ups.
222  if (auto res = get_screen_resources()) {
223  if (x11display_cat.is_debug()) {
224  x11display_cat.debug()
225  << "Using XRRScreenResources to obtain display modes\n";
226  }
227  _display_information->_total_display_modes = res->nmode;
228  _display_information->_display_mode_array = new DisplayMode[res->nmode];
229  for (int i = 0; i < res->nmode; ++i) {
230  XRRModeInfo &mode = res->modes[i];
231 
232  DisplayMode *dm = _display_information->_display_mode_array + i;
233  dm->width = mode.width;
234  dm->height = mode.height;
235  dm->bits_per_pixel = -1;
236  dm->fullscreen_only = false;
237 
238  if (mode.hTotal && mode.vTotal) {
239  dm->refresh_rate = (double)mode.dotClock /
240  ((double)mode.hTotal * (double)mode.vTotal);
241  } else {
242  dm->refresh_rate = 0;
243  }
244  }
245  } else {
246  if (x11display_cat.is_debug()) {
247  x11display_cat.debug()
248  << "Using XRRSizes and XRRRates to obtain display modes\n";
249  }
250 
251  int num_sizes, num_rates;
252  XRRScreenSize *xrrs;
253  xrrs = _XRRSizes(_display, 0, &num_sizes);
254  _display_information->_total_display_modes = 0;
255  for (int i = 0; i < num_sizes; ++i) {
256  _XRRRates(_display, 0, i, &num_rates);
257  _display_information->_total_display_modes += num_rates;
258  }
259 
260  short *rates;
261  short counter = 0;
262  _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes];
263  for (int i = 0; i < num_sizes; ++i) {
264  int num_rates;
265  rates = _XRRRates(_display, 0, i, &num_rates);
266  for (int j = 0; j < num_rates; ++j) {
267  DisplayMode* dm = _display_information->_display_mode_array + counter;
268  dm->width = xrrs[i].width;
269  dm->height = xrrs[i].height;
270  dm->refresh_rate = rates[j];
271  dm->bits_per_pixel = -1;
272  dm->fullscreen_only = false;
273  ++counter;
274  }
275  }
276  }
277  }
278 
279  // Connect to an input method for supporting international text entry.
280  _im = XOpenIM(_display, nullptr, nullptr, nullptr);
281  if (_im == (XIM)nullptr) {
282  // Fall back to internal input method.
283  XSetLocaleModifiers("@im=none");
284  _im = XOpenIM(_display, nullptr, nullptr, nullptr);
285  if (_im == (XIM)nullptr) {
286  x11display_cat.warning()
287  << "Couldn't open input method.\n";
288  }
289  }
290 
291  // What styles does the current input method support?
292  /*
293  XIMStyles *im_supported_styles;
294  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
295 
296  for (int i = 0; i < im_supported_styles->count_styles; i++) {
297  XIMStyle style = im_supported_styles->supported_styles[i];
298  cerr << "style " << i << ". " << hex << style << dec << "\n";
299  }
300 
301  XFree(im_supported_styles);
302  */
303 
304  const char *dpi = XGetDefault(_display, "Xft", "dpi");
305  if (dpi != nullptr) {
306  char *endptr = nullptr;
307  double result = pstrtod(dpi, &endptr);
308  if (result != 0 && !cnan(result) && endptr[0] == '\0') {
309  result /= 96;
310  set_detected_display_zoom(result);
311 
312  if (x11display_cat.is_debug()) {
313  x11display_cat.debug()
314  << "Determined display zoom to be " << result
315  << " based on specified Xft.dpi " << dpi << "\n";
316  }
317  } else {
318  x11display_cat.warning()
319  << "Unable to determine display zoom because Xft.dpi is invalid: "
320  << dpi << "\n";
321  }
322  } else if (x11display_cat.is_debug()) {
323  x11display_cat.debug()
324  << "Unable to determine display zoom because Xft.dpi was not set.\n";
325  }
326 
327  // Get some X atom numbers.
328  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
329  _net_wm_pid = XInternAtom(_display, "_NET_WM_PID", false);
330  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
331  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
332  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
333  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
334  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
335  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
336  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
337  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
338  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
339  _net_wm_bypass_compositor = XInternAtom(_display, "_NET_WM_BYPASS_COMPOSITOR", false);
340 }
341 
342 /**
343  *
344  */
345 x11GraphicsPipe::
346 ~x11GraphicsPipe() {
347  release_hidden_cursor();
348  if (_im) {
349  XCloseIM(_im);
350  }
351  if (_display) {
352  XCloseDisplay(_display);
353  }
354 }
355 
356 /**
357  * Returns an XRRScreenResources object, or null if RandR 1.2 is not supported.
358  */
359 std::unique_ptr<XRRScreenResources, pfn_XRRFreeScreenResources> x11GraphicsPipe::
360 get_screen_resources() const {
361  XRRScreenResources *res = nullptr;
362 
363  if (_have_xrandr && _XRRGetScreenResourcesCurrent != nullptr) {
364  res = _XRRGetScreenResourcesCurrent(_display, _root);
365  }
366 
367  return std::unique_ptr<XRRScreenResources, pfn_XRRFreeScreenResources>(res, _XRRFreeScreenResources);
368 }
369 
370 /**
371  * Returns an XRRCrtcInfo object, or null if RandR 1.2 is not supported.
372  */
373 std::unique_ptr<XRRCrtcInfo, pfn_XRRFreeCrtcInfo> x11GraphicsPipe::
374 get_crtc_info(XRRScreenResources *res, RRCrtc crtc) const {
375  XRRCrtcInfo *info = nullptr;
376 
377  if (_have_xrandr && _XRRGetCrtcInfo != nullptr) {
378  info = _XRRGetCrtcInfo(_display, res, crtc);
379  }
380 
381  return std::unique_ptr<XRRCrtcInfo, pfn_XRRFreeCrtcInfo>(info, _XRRFreeCrtcInfo);
382 }
383 
384 /**
385  * Finds a CRTC for going fullscreen to, at the given origin. The new CRTC
386  * is returned, along with its x, y, width and height.
387  *
388  * If the required RandR extension is not supported, a value of None will be
389  * returned, but x, y, width and height will still be populated.
390  */
392 find_fullscreen_crtc(const LPoint2i &point,
393  int &x, int &y, int &width, int &height) {
394  x = 0;
395  y = 0;
396  width = DisplayWidth(_display, _screen);
397  height = DisplayHeight(_display, _screen);
398 
399  if (auto res = get_screen_resources()) {
400  for (int i = 0; i < res->ncrtc; ++i) {
401  RRCrtc crtc = res->crtcs[i];
402  if (auto info = get_crtc_info(res.get(), crtc)) {
403  if (point[0] >= info->x && point[0] < info->x + (int)info->width &&
404  point[1] >= info->y && point[1] < info->y + (int)info->height) {
405 
406  x = info->x;
407  y = info->y;
408  width = (int)info->width;
409  height = (int)info->height;
410  return crtc;
411  }
412  }
413  }
414  }
415 
416  return None;
417 }
418 
419 /**
420  * Returns an indication of the thread in which this GraphicsPipe requires its
421  * window processing to be performed: typically either the app thread (e.g.
422  * X) or the draw thread (Windows).
423  */
424 GraphicsPipe::PreferredWindowThread
426  // Actually, since we're creating the graphics context in open_window() now,
427  // it appears we need to ensure the open_window() call is performed in the
428  // draw thread for now, even though X wants all of its calls to be single-
429  // threaded.
430 
431  // This means that all X windows may have to be handled by the same draw
432  // thread, which we didn't intend (though the global _x_mutex may allow them
433  // to be technically served by different threads, even though the actual X
434  // calls will be serialized). There might be a better way.
435 
436  return PWT_draw;
437 }
438 
439 /**
440  * Called once to make an invisible Cursor for return from
441  * get_hidden_cursor().
442  */
443 void x11GraphicsPipe::
444 make_hidden_cursor() {
445  nassertv(_hidden_cursor == None);
446 
447  unsigned int x_size, y_size;
448  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
449 
450  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
451 
452  XColor black;
453  memset(&black, 0, sizeof(black));
454 
455  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
456  &black, &black, x_size, y_size);
457  XFreePixmap(_display, empty);
458 }
459 
460 /**
461  * Called once to release the invisible cursor created by
462  * make_hidden_cursor().
463  */
464 void x11GraphicsPipe::
465 release_hidden_cursor() {
466  if (_hidden_cursor != None) {
467  XFreeCursor(_display, _hidden_cursor);
468  _hidden_cursor = None;
469  }
470 }
471 
472 /**
473  * Installs new Xlib error handler functions if this is the first time this
474  * function has been called. These error handler functions will attempt to
475  * reduce Xlib's annoying tendency to shut down the client at the first error.
476  * Unfortunately, it is difficult to play nice with the client if it has
477  * already installed its own error handlers.
478  */
479 void x11GraphicsPipe::
480 install_error_handlers() {
481  if (_error_handlers_installed) {
482  return;
483  }
484 
485  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
486  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
487  _error_handlers_installed = true;
488 }
489 
490 /**
491  * This function is installed as the error handler for a non-fatal Xlib error.
492  */
493 int x11GraphicsPipe::
494 error_handler(X11_Display *display, XErrorEvent *error) {
495  ++_x_error_count;
496 
497  static const int msg_len = 80;
498  char msg[msg_len];
499  XGetErrorText(display, error->error_code, msg, msg_len);
500 
501  if (!_x_error_messages_enabled) {
502  if (x11display_cat.is_debug()) {
503  x11display_cat.debug()
504  << msg << "\n";
505  }
506  return 0;
507  }
508 
509  x11display_cat.error()
510  << msg << "\n";
511 
512  if (x_error_abort) {
513  abort();
514  }
515 
516  // We return to allow the application to continue running, unlike the
517  // default X error handler which exits.
518  return 0;
519 }
520 
521 /**
522  * This function is installed as the error handler for a fatal Xlib error.
523  */
524 int x11GraphicsPipe::
525 io_error_handler(X11_Display *display) {
526  x11display_cat.fatal()
527  << "X fatal error on display " << (void *)display << "\n";
528 
529  // Unfortunately, we can't continue from this function, even if we promise
530  // never to use X again. We're supposed to terminate without returning, and
531  // if we do return, the caller will exit anyway. Sigh. Very poor design on
532  // X's part.
533  return 0;
534 }
pstrtod.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pstrtod
double pstrtod(const char *nptr, char **endptr)
This function re-implements strtod, to avoid the problems that occur when the LC_NUMERIC locale gets ...
Definition: pstrtod.cxx:31
DisplayMode
Definition: displayInformation.h:20
frameBufferProperties.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
_XRRModeInfo
Definition: x11GraphicsPipe.h:46
x11GraphicsPipe::get_preferred_window_thread
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
Definition: x11GraphicsPipe.cxx:425
x11GraphicsPipe::get_screen_resources
std::unique_ptr< XRRScreenResources, pfn_XRRFreeScreenResources > get_screen_resources() const
Returns an XRRScreenResources object, or null if RandR 1.2 is not supported.
Definition: x11GraphicsPipe.cxx:360
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
_XRRScreenResources
Definition: x11GraphicsPipe.h:63
_XRRCrtcInfo
Definition: x11GraphicsPipe.h:74
config_x11display.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsPipe::get_crtc_info
std::unique_ptr< XRRCrtcInfo, pfn_XRRFreeCrtcInfo > get_crtc_info(XRRScreenResources *res, RRCrtc crtc) const
Returns an XRRCrtcInfo object, or null if RandR 1.2 is not supported.
Definition: x11GraphicsPipe.cxx:374
XRRScreenSize
Definition: x11GraphicsPipe.h:41
ConfigVariableInt::get_value
get_value
Returns the variable's value.
Definition: configVariableInt.h:43
displayInformation.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
x11GraphicsPipe::find_fullscreen_crtc
RRCrtc find_fullscreen_crtc(const LPoint2i &point, int &x, int &y, int &width, int &height)
Finds a CRTC for going fullscreen to, at the given origin.
Definition: x11GraphicsPipe.cxx:392
x11GraphicsWindow.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
LightReMutex
A lightweight reentrant mutex.
Definition: lightReMutex.h:32
x11GraphicsPipe.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ExecutionEnvironment::get_environment_variable
get_environment_variable
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
Definition: executionEnvironment.h:56