Panda3D
 All Classes Functions Variables Enumerations
x11GraphicsPipe.cxx
1 // Filename: x11GraphicsPipe.cxx
2 // Created by: rdb (07Jul09)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "x11GraphicsPipe.h"
16 #include "x11GraphicsWindow.h"
17 #include "config_x11display.h"
18 #include "frameBufferProperties.h"
19 
20 TypeHandle x11GraphicsPipe::_type_handle;
21 
22 bool x11GraphicsPipe::_error_handlers_installed = false;
23 x11GraphicsPipe::ErrorHandlerFunc *x11GraphicsPipe::_prev_error_handler;
24 x11GraphicsPipe::IOErrorHandlerFunc *x11GraphicsPipe::_prev_io_error_handler;
25 bool x11GraphicsPipe::_x_error_messages_enabled = true;
26 int x11GraphicsPipe::_x_error_count = 0;
27 
28 LightReMutex x11GraphicsPipe::_x_mutex;
29 
30 ////////////////////////////////////////////////////////////////////
31 // Function: x11GraphicsPipe::Constructor
32 // Access: Public
33 // Description:
34 ////////////////////////////////////////////////////////////////////
35 x11GraphicsPipe::
36 x11GraphicsPipe(const string &display) {
37  string display_spec = display;
38  if (display_spec.empty()) {
39  display_spec = display_cfg;
40  }
41  if (display_spec.empty()) {
42  display_spec = ExecutionEnvironment::get_environment_variable("DISPLAY");
43  }
44  if (display_spec.empty()) {
45  display_spec = ":0.0";
46  }
47 
48  // The X docs say we should do this to get international character
49  // support from the keyboard.
50  setlocale(LC_ALL, "");
51 
52  // But it's important that we use the "C" locale for numeric
53  // formatting, since all of the internal Panda code assumes this--we
54  // need a decimal point to mean a decimal point.
55  setlocale(LC_NUMERIC, "C");
56 
57  _is_valid = false;
58  _supported_types = OT_window | OT_buffer | OT_texture_buffer;
59  _display = NULL;
60  _screen = 0;
61  _root = (X11_Window)NULL;
62  _im = (XIM)NULL;
63  _hidden_cursor = None;
64 
65  install_error_handlers();
66 
67  _display = XOpenDisplay(display_spec.c_str());
68  if (!_display) {
69  x11display_cat.error()
70  << "Could not open display \"" << display_spec << "\".\n";
71  _is_valid = false;
72  _screen = None;
73  _root = None;
74  _display_width = 0;
75  _display_height = 0;
76  return;
77  }
78 
79  if (!XSupportsLocale()) {
80  x11display_cat.warning()
81  << "X does not support locale " << setlocale(LC_ALL, NULL) << "\n";
82  }
83  XSetLocaleModifiers("");
84 
85  _screen = DefaultScreen(_display);
86  _root = RootWindow(_display, _screen);
87  _display_width = DisplayWidth(_display, _screen);
88  _display_height = DisplayHeight(_display, _screen);
89  _is_valid = true;
90 
91 #ifdef HAVE_XRANDR
92  // Use Xrandr to fill in the supported resolution list.
93  int num_sizes, num_rates;
94  XRRScreenSize *xrrs;
95  xrrs = XRRSizes(_display, 0, &num_sizes);
96  _display_information->_total_display_modes = 0;
97  for (int i = 0; i < num_sizes; ++i) {
98  XRRRates(_display, 0, i, &num_rates);
99  _display_information->_total_display_modes += num_rates;
100  }
101 
102  short *rates;
103  short counter = 0;
104  _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes];
105  for (int i = 0; i < num_sizes; ++i) {
106  int num_rates;
107  rates = XRRRates(_display, 0, i, &num_rates);
108  for (int j = 0; j < num_rates; ++j) {
109  DisplayMode* dm = _display_information->_display_mode_array + counter;
110  dm->width = xrrs[i].width;
111  dm->height = xrrs[i].height;
112  dm->refresh_rate = rates[j];
113  dm->bits_per_pixel = -1;
114  dm->fullscreen_only = false;
115  ++counter;
116  }
117  }
118 #endif
119 
120  // Connect to an input method for supporting international text
121  // entry.
122  _im = XOpenIM(_display, NULL, NULL, NULL);
123  if (_im == (XIM)NULL) {
124  x11display_cat.warning()
125  << "Couldn't open input method.\n";
126  }
127 
128  // What styles does the current input method support?
129  /*
130  XIMStyles *im_supported_styles;
131  XGetIMValues(_im, XNQueryInputStyle, &im_supported_styles, NULL);
132 
133  for (int i = 0; i < im_supported_styles->count_styles; i++) {
134  XIMStyle style = im_supported_styles->supported_styles[i];
135  cerr << "style " << i << ". " << hex << style << dec << "\n";
136  }
137 
138  XFree(im_supported_styles);
139  */
140 
141  // Get some X atom numbers.
142  _wm_delete_window = XInternAtom(_display, "WM_DELETE_WINDOW", false);
143  _net_wm_pid = XInternAtom(_display, "_NET_WM_PID", false);
144  _net_wm_window_type = XInternAtom(_display, "_NET_WM_WINDOW_TYPE", false);
145  _net_wm_window_type_splash = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_SPLASH", false);
146  _net_wm_window_type_fullscreen = XInternAtom(_display, "_NET_WM_WINDOW_TYPE_FULLSCREEN", false);
147  _net_wm_state = XInternAtom(_display, "_NET_WM_STATE", false);
148  _net_wm_state_fullscreen = XInternAtom(_display, "_NET_WM_STATE_FULLSCREEN", false);
149  _net_wm_state_above = XInternAtom(_display, "_NET_WM_STATE_ABOVE", false);
150  _net_wm_state_below = XInternAtom(_display, "_NET_WM_STATE_BELOW", false);
151  _net_wm_state_add = XInternAtom(_display, "_NET_WM_STATE_ADD", false);
152  _net_wm_state_remove = XInternAtom(_display, "_NET_WM_STATE_REMOVE", false);
153 }
154 
155 ////////////////////////////////////////////////////////////////////
156 // Function: x11GraphicsPipe::Destructor
157 // Access: Public, Virtual
158 // Description:
159 ////////////////////////////////////////////////////////////////////
160 x11GraphicsPipe::
161 ~x11GraphicsPipe() {
162  release_hidden_cursor();
163  if (_im) {
164  XCloseIM(_im);
165  }
166  if (_display) {
167  XCloseDisplay(_display);
168  }
169 }
170 
171 ////////////////////////////////////////////////////////////////////
172 // Function: x11GraphicsPipe::get_preferred_window_thread
173 // Access: Public, Virtual
174 // Description: Returns an indication of the thread in which this
175 // GraphicsPipe requires its window processing to be
176 // performed: typically either the app thread (e.g. X)
177 // or the draw thread (Windows).
178 ////////////////////////////////////////////////////////////////////
179 GraphicsPipe::PreferredWindowThread
181  // Actually, since we're creating the graphics context in
182  // open_window() now, it appears we need to ensure the open_window()
183  // call is performed in the draw thread for now, even though X wants
184  // all of its calls to be single-threaded.
185 
186  // This means that all X windows may have to be handled by the same
187  // draw thread, which we didn't intend (though the global _x_mutex
188  // may allow them to be technically served by different threads,
189  // even though the actual X calls will be serialized). There might
190  // be a better way.
191 
192  return PWT_draw;
193 }
194 
195 ////////////////////////////////////////////////////////////////////
196 // Function: x11GraphicsPipe::make_hidden_cursor
197 // Access: Private
198 // Description: Called once to make an invisible Cursor for return
199 // from get_hidden_cursor().
200 ////////////////////////////////////////////////////////////////////
201 void x11GraphicsPipe::
202 make_hidden_cursor() {
203  nassertv(_hidden_cursor == None);
204 
205  unsigned int x_size, y_size;
206  XQueryBestCursor(_display, _root, 1, 1, &x_size, &y_size);
207 
208  Pixmap empty = XCreatePixmap(_display, _root, x_size, y_size, 1);
209 
210  XColor black;
211  memset(&black, 0, sizeof(black));
212 
213  _hidden_cursor = XCreatePixmapCursor(_display, empty, empty,
214  &black, &black, x_size, y_size);
215  XFreePixmap(_display, empty);
216 }
217 
218 ////////////////////////////////////////////////////////////////////
219 // Function: x11GraphicsPipe::release_hidden_cursor
220 // Access: Private
221 // Description: Called once to release the invisible cursor created
222 // by make_hidden_cursor().
223 ////////////////////////////////////////////////////////////////////
224 void x11GraphicsPipe::
225 release_hidden_cursor() {
226  if (_hidden_cursor != None) {
227  XFreeCursor(_display, _hidden_cursor);
228  _hidden_cursor = None;
229  }
230 }
231 
232 ////////////////////////////////////////////////////////////////////
233 // Function: x11GraphicsPipe::install_error_handlers
234 // Access: Private, Static
235 // Description: Installs new Xlib error handler functions if this is
236 // the first time this function has been called. These
237 // error handler functions will attempt to reduce Xlib's
238 // annoying tendency to shut down the client at the
239 // first error. Unfortunately, it is difficult to play
240 // nice with the client if it has already installed its
241 // own error handlers.
242 ////////////////////////////////////////////////////////////////////
243 void x11GraphicsPipe::
244 install_error_handlers() {
245  if (_error_handlers_installed) {
246  return;
247  }
248 
249  _prev_error_handler = (ErrorHandlerFunc *)XSetErrorHandler(error_handler);
250  _prev_io_error_handler = (IOErrorHandlerFunc *)XSetIOErrorHandler(io_error_handler);
251  _error_handlers_installed = true;
252 }
253 
254 ////////////////////////////////////////////////////////////////////
255 // Function: x11GraphicsPipe::error_handler
256 // Access: Private, Static
257 // Description: This function is installed as the error handler for a
258 // non-fatal Xlib error.
259 ////////////////////////////////////////////////////////////////////
260 int x11GraphicsPipe::
261 error_handler(X11_Display *display, XErrorEvent *error) {
262  ++_x_error_count;
263 
264  static const int msg_len = 80;
265  char msg[msg_len];
266  XGetErrorText(display, error->error_code, msg, msg_len);
267 
268  if (!_x_error_messages_enabled) {
269  if (x11display_cat.is_debug()) {
270  x11display_cat.debug()
271  << msg << "\n";
272  }
273  return 0;
274  }
275 
276  x11display_cat.error()
277  << msg << "\n";
278 
279  if (x_error_abort) {
280  abort();
281  }
282 
283  // We return to allow the application to continue running, unlike
284  // the default X error handler which exits.
285  return 0;
286 }
287 
288 ////////////////////////////////////////////////////////////////////
289 // Function: x11GraphicsPipe::io_error_handler
290 // Access: Private, Static
291 // Description: This function is installed as the error handler for a
292 // fatal Xlib error.
293 ////////////////////////////////////////////////////////////////////
294 int x11GraphicsPipe::
295 io_error_handler(X11_Display *display) {
296  x11display_cat.fatal()
297  << "X fatal error on display " << (void *)display << "\n";
298 
299  // Unfortunately, we can't continue from this function, even if we
300  // promise never to use X again. We're supposed to terminate
301  // without returning, and if we do return, the caller will exit
302  // anyway. Sigh. Very poor design on X's part.
303  return 0;
304 }
A lightweight reentrant mutex.
Definition: lightReMutex.h:34
virtual PreferredWindowThread get_preferred_window_thread() const
Returns an indication of the thread in which this GraphicsPipe requires its window processing to be p...
static string get_environment_variable(const string &var)
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85