Panda3D
x11GraphicsWindow.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 x11GraphicsWindow.cxx
10 * @author rdb
11 * @date 2009-07-07
12 */
13
14#include "x11GraphicsWindow.h"
15#include "config_x11display.h"
16#include "x11GraphicsPipe.h"
17
18#include "graphicsPipe.h"
19#include "keyboardButton.h"
20#include "mouseButton.h"
21#include "buttonMap.h"
22#include "clockObject.h"
23#include "pStatTimer.h"
24#include "textEncoder.h"
25#include "throw_event.h"
26#include "lightReMutexHolder.h"
27#include "nativeWindowHandle.h"
28#include "virtualFileSystem.h"
29#include "get_x11.h"
30#include "pnmImage.h"
31#include "pnmFileTypeRegistry.h"
32#include "evdevInputDevice.h"
33
34#include <sys/time.h>
35#include <fcntl.h>
36
37using std::istream;
38using std::ostringstream;
39using std::string;
40
41struct _XcursorFile {
42 void *closure;
43 int (*read)(XcursorFile *, unsigned char *, int);
44 int (*write)(XcursorFile *, unsigned char *, int);
45 int (*seek)(XcursorFile *, long, int);
46};
47
48typedef struct _XcursorImage {
49 unsigned int version;
50 unsigned int size;
51 unsigned int width;
52 unsigned int height;
53 unsigned int xhot;
54 unsigned int yhot;
55 unsigned int delay;
56 unsigned int *pixels;
57} XcursorImage;
58
59static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) {
60 istream* str = (istream*) file->closure;
61 str->read((char*) buf, len);
62 return str->gcount();
63}
64
65static int xcursor_write(XcursorFile *file, unsigned char *buf, int len) {
66 // Not implemented, we don't need it.
67 nassertr_always(false, 0);
68 return 0;
69}
70
71static int xcursor_seek(XcursorFile *file, long offset, int whence) {
72 istream* str = (istream*) file->closure;
73 switch (whence) {
74 case SEEK_SET:
75 str->seekg(offset, istream::beg);
76 break;
77 case SEEK_CUR:
78 str->seekg(offset, istream::cur);
79 break;
80 case SEEK_END:
81 str->seekg(offset, istream::end);
82 }
83
84 return str->tellg();
85}
86
87TypeHandle x11GraphicsWindow::_type_handle;
88
89/**
90 *
91 */
92x11GraphicsWindow::
93x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe,
94 const string &name,
95 const FrameBufferProperties &fb_prop,
96 const WindowProperties &win_prop,
97 int flags,
99 GraphicsOutput *host) :
100 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
101{
102 x11GraphicsPipe *x11_pipe;
103 DCAST_INTO_V(x11_pipe, _pipe);
104 _display = x11_pipe->get_display();
105 _screen = x11_pipe->get_screen();
106 _xwindow = (X11_Window)nullptr;
107 _ic = (XIC)nullptr;
108 _visual_info = nullptr;
109 _orig_size_id = -1;
110
111 if (x11_pipe->_have_xrandr) {
112 // We may still need these functions after the pipe is already destroyed,
113 // so we copy them into the x11GraphicsWindow.
114 _XRRGetScreenInfo = x11_pipe->_XRRGetScreenInfo;
115 _XRRSetScreenConfig = x11_pipe->_XRRSetScreenConfig;
116 }
117
118 _awaiting_configure = false;
119 _dga_mouse_enabled = false;
120 _override_redirect = False;
121 _wm_delete_window = x11_pipe->_wm_delete_window;
122
123 PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse");
124 add_input_device(device);
125 _input = device;
126}
127
128/**
129 *
130 */
131x11GraphicsWindow::
132~x11GraphicsWindow() {
133 if (!_cursor_filenames.empty()) {
134 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
135 for (auto item : _cursor_filenames) {
136 XFreeCursor(_display, item.second);
137 }
138 }
139}
140
141/**
142 * Returns the MouseData associated with the nth input device's pointer. This
143 * is deprecated; use get_pointer_device().get_pointer() instead, or for raw
144 * mice, use the InputDeviceManager interface.
145 */
147get_pointer(int device) const {
148 MouseData result;
149 {
150 LightMutexHolder holder(_input_lock);
151 nassertr(device >= 0 && device < (int)_input_devices.size(), MouseData());
152
153 result = ((const GraphicsWindowInputDevice *)_input_devices[device].p())->get_pointer();
154
155 // We recheck this immediately to get the most up-to-date value, but we
156 // won't bother waiting for the lock if we can't.
157 if (device == 0 && !_dga_mouse_enabled && result._in_window &&
158 x11GraphicsPipe::_x_mutex.try_lock()) {
159 XEvent event;
160 if (_xwindow != None &&
161 XQueryPointer(_display, _xwindow, &event.xbutton.root,
162 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
163 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state)) {
165 result._xpos = event.xbutton.x;
166 result._ypos = event.xbutton.y;
167 ((GraphicsWindowInputDevice *)_input_devices[0].p())->set_pointer_in_window(result._xpos, result._ypos, time);
168 }
169 x11GraphicsPipe::_x_mutex.release();
170 }
171 }
172 return result;
173}
174
175/**
176 * Forces the pointer to the indicated position within the window, if
177 * possible.
178 *
179 * Returns true if successful, false on failure. This may fail if the mouse
180 * is not currently within the window, or if the API doesn't support this
181 * operation.
182 */
184move_pointer(int device, int x, int y) {
185 // Note: this is not thread-safe; it should be called only from App.
186 // Probably not an issue.
187 if (device == 0) {
188 // Move the system mouse pointer.
189 PointerData md = _input->get_pointer();
190 if (!_properties.get_foreground() || !md.get_in_window()) {
191 // If the window doesn't have input focus, or the mouse isn't currently
192 // within the window, forget it.
193 return false;
194 }
195
196 if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
197 if (!_dga_mouse_enabled) {
198 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
199 XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
200 }
201 _input->set_pointer_in_window(x, y);
202 }
203 return true;
204 } else {
205 // Can't move a raw mouse.
206 return false;
207 }
208}
209
210/**
211 * Clears the entire framebuffer before rendering, according to the settings
212 * of get_color_clear_active() and get_depth_clear_active() (inherited from
213 * DrawableRegion).
214 *
215 * This function is called only within the draw thread.
216 */
218clear(Thread *current_thread) {
219 if (is_any_clear_active()) {
220 // Evidently the NVIDIA driver may call glXCreateNewContext inside
221 // prepare_display_region, so we need to hold the X11 lock.
222 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
223 GraphicsOutput::clear(current_thread);
224 }
225}
226
227/**
228 * This function will be called within the draw thread before beginning
229 * rendering for a given frame. It should do whatever setup is required, and
230 * return true if the frame should be rendered, or false if it should be
231 * skipped.
232 */
234begin_frame(FrameMode mode, Thread *current_thread) {
235 PStatTimer timer(_make_current_pcollector, current_thread);
236
237 begin_frame_spam(mode);
238 if (_gsg == nullptr) {
239 return false;
240 }
241 if (_awaiting_configure) {
242 // Don't attempt to draw while we have just reconfigured the window and we
243 // haven't got the notification back yet.
244 return false;
245 }
246
247 // Reset the GSG state if this is the first time it has been used. (We
248 // can't just call reset() when we construct the GSG, because reset()
249 // requires having a current context.)
250 _gsg->reset_if_new();
251
252 if (mode == FM_render) {
253 // begin_render_texture();
254 clear_cube_map_selection();
255 }
256
257 _gsg->set_current_properties(&get_fb_properties());
258 return _gsg->begin_frame(current_thread);
259}
260
261/**
262 * This function will be called within the draw thread after rendering is
263 * completed for a given frame. It should do whatever finalization is
264 * required.
265 */
267end_frame(FrameMode mode, Thread *current_thread) {
268 end_frame_spam(mode);
269 nassertv(_gsg != nullptr);
270
271 if (mode == FM_render) {
272 // end_render_texture();
273 copy_to_textures();
274 }
275
276 _gsg->end_frame(current_thread);
277
278 if (mode == FM_render) {
279 trigger_flip();
280 clear_cube_map_selection();
281 }
282}
283
284/**
285 * Do whatever processing is necessary to ensure that the window responds to
286 * user events. Also, honor any requests recently made via
287 * request_properties()
288 *
289 * This function is called only within the window thread.
290 */
293 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
294
296
297 if (_xwindow == (X11_Window)0) {
298 return;
299 }
300
301 XEvent event;
302 XKeyEvent keyrelease_event;
303 bool got_keyrelease_event = false;
304
305 XConfigureEvent configure_event;
306 bool got_configure_event = false;
307
308 WindowProperties properties;
309 bool changed_properties = false;
310
311 while (XCheckIfEvent(_display, &event, check_event, (char *)this)) {
312 if (got_keyrelease_event) {
313 // If a keyrelease event is immediately followed by a matching keypress
314 // event, that's just key repeat and we should treat the two events
315 // accordingly. It would be nice if X provided a way to differentiate
316 // between keyrepeat and explicit keypresses more generally.
317 got_keyrelease_event = false;
318
319 if (event.type == KeyPress &&
320 event.xkey.keycode == keyrelease_event.keycode &&
321 (event.xkey.time - keyrelease_event.time <= 1)) {
322 if (!XFilterEvent(&event, None)) {
323 // In particular, we only generate down messages for the repeated
324 // keys, not down-and-up messages.
325 handle_keystroke(event.xkey);
326
327 // We thought about not generating the keypress event, but we need
328 // that repeat for backspace. Rethink later.
329 handle_keypress(event.xkey);
330 }
331 continue;
332
333 } else {
334 // This keyrelease event is not immediately followed by a matching
335 // keypress event, so it's a genuine release.
336 ButtonHandle raw_button = map_raw_button(keyrelease_event.keycode);
337 if (raw_button != ButtonHandle::none()) {
338 _input->raw_button_up(raw_button);
339 }
340
341 handle_keyrelease(keyrelease_event);
342 }
343 }
344
345 // Send out a raw key press event before we do XFilterEvent, which will
346 // filter out dead keys and such.
347 if (event.type == KeyPress) {
348 ButtonHandle raw_button = map_raw_button(event.xkey.keycode);
349 if (raw_button != ButtonHandle::none()) {
350 _input->raw_button_down(raw_button);
351 }
352 }
353
354 if (XFilterEvent(&event, None)) {
355 continue;
356 }
357
358 ButtonHandle button;
359
360 switch (event.type) {
361 case ReparentNotify:
362 break;
363
364 case ConfigureNotify:
365 // When resizing or moving the window, multiple ConfigureNotify events
366 // may be sent in rapid succession. We only respond to the last one.
367 configure_event = event.xconfigure;
368 got_configure_event = true;
369 break;
370
371 case ButtonPress:
372 // This refers to the mouse buttons.
373 button = get_mouse_button(event.xbutton);
374 if (!_dga_mouse_enabled) {
375 _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
376 }
377 _input->button_down(button);
378 break;
379
380 case ButtonRelease:
381 button = get_mouse_button(event.xbutton);
382 if (!_dga_mouse_enabled) {
383 _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
384 }
385 _input->button_up(button);
386 break;
387
388 case MotionNotify:
389 if (_dga_mouse_enabled) {
390 PointerData md = _input->get_pointer();
391 _input->set_pointer_in_window(md.get_x() + event.xmotion.x_root, md.get_y() + event.xmotion.y_root);
392 } else {
393 _input->set_pointer_in_window(event.xmotion.x, event.xmotion.y);
394 }
395 break;
396
397 case KeyPress:
398 handle_keystroke(event.xkey);
399 handle_keypress(event.xkey);
400 break;
401
402 case KeyRelease:
403 // The KeyRelease can't be processed immediately, because we have to
404 // check first if it's immediately followed by a matching KeyPress
405 // event.
406 keyrelease_event = event.xkey;
407 got_keyrelease_event = true;
408 break;
409
410 case EnterNotify:
411 if (_dga_mouse_enabled) {
412 PointerData md = _input->get_pointer();
413 _input->set_pointer_in_window(md.get_x(), md.get_y());
414 } else {
415 _input->set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
416 }
417 break;
418
419 case LeaveNotify:
421 break;
422
423 case FocusIn:
424 properties.set_foreground(true);
425 changed_properties = true;
426 break;
427
428 case FocusOut:
429 _input->focus_lost();
430 properties.set_foreground(false);
431 changed_properties = true;
432 break;
433
434 case UnmapNotify:
435 properties.set_minimized(true);
436 changed_properties = true;
437 break;
438
439 case MapNotify:
440 properties.set_minimized(false);
441 changed_properties = true;
442
443 // Auto-focus the window when it is mapped.
444 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
445 break;
446
447 case ClientMessage:
448 if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
449 // This is a message from the window manager indicating that the user
450 // has requested to close the window.
451 string close_request_event = get_close_request_event();
452 if (!close_request_event.empty()) {
453 // In this case, the app has indicated a desire to intercept the
454 // request and process it directly.
455 throw_event(close_request_event);
456
457 } else {
458 // In this case, the default case, the app does not intend to
459 // service the request, so we do by closing the window.
460
461 // TODO: don't release the gsg in the window thread.
462 close_window();
463 properties.set_open(false);
464 system_changed_properties(properties);
465 }
466 }
467 break;
468
469 case DestroyNotify:
470 // Apparently, we never get a DestroyNotify on a toplevel window.
471 // Instead, we rely on hints from the window manager (see above).
472 x11display_cat.info()
473 << "DestroyNotify\n";
474 break;
475
476 default:
477 x11display_cat.warning()
478 << "unhandled X event type " << event.type << "\n";
479 }
480 }
481
482 if (got_configure_event) {
483 // Now handle the last configure event we found.
484 _awaiting_configure = false;
485
486 // Is this the inner corner or the outer corner? The Xlib docs say it
487 // should be the outer corner, but it appears to be the inner corner on my
488 // own implementation, which is inconsistent with XConfigureWindow.
489 // (Panda really wants to work with the inner corner, anyway, but that
490 // means we need to fix XConfigureWindow too.)
491 properties.set_origin(configure_event.x, configure_event.y);
492 properties.set_size(configure_event.width, configure_event.height);
493
494 if (_properties.get_fixed_size()) {
495 // If the window properties indicate a fixed size only, undo any attempt
496 // by the user to change them. In X, there doesn't appear to be a way
497 // to universally disallow this directly (although we do set the
498 // min_size and max_size to the same value, which seems to work for most
499 // window managers.)
500 if (configure_event.width != _fixed_size.get_x() ||
501 configure_event.height != _fixed_size.get_y()) {
502 XWindowChanges changes;
503 changes.width = _fixed_size.get_x();
504 changes.height = _fixed_size.get_y();
505 int value_mask = (CWWidth | CWHeight);
506 XConfigureWindow(_display, _xwindow, value_mask, &changes);
507 }
508 }
509
510 // If the window was reconfigured, we may need to re-confine the mouse
511 // pointer. See GitHub bug #280.
512 if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
513 X11_Cursor cursor = None;
514 if (_properties.get_cursor_hidden()) {
515 x11GraphicsPipe *x11_pipe;
516 DCAST_INTO_V(x11_pipe, _pipe);
517 cursor = x11_pipe->get_hidden_cursor();
518 }
519
520 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
521 _xwindow, cursor, CurrentTime);
522 }
523
524 changed_properties = true;
525 }
526
527 if (properties.has_foreground() && (
528 _properties.get_mouse_mode() == WindowProperties::M_confined ||
529 _dga_mouse_enabled)) {
530 x11GraphicsPipe *x11_pipe;
531 DCAST_INTO_V(x11_pipe, _pipe);
532
533 // Focus has changed, let's let go of the pointer if we've grabbed or re-grab it if needed
534 if (properties.get_foreground()) {
535 // Window is going to the foreground, re-grab the pointer
536 X11_Cursor cursor = None;
537 if (_properties.get_cursor_hidden()) {
538 cursor = x11_pipe->get_hidden_cursor();
539 }
540
541 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
542 _xwindow, cursor, CurrentTime);
543 if (_dga_mouse_enabled) {
544 x11_pipe->enable_relative_mouse();
545 }
546 }
547 else {
548 // window is leaving the foreground, ungrab the pointer
549 if (_dga_mouse_enabled) {
550 x11_pipe->disable_relative_mouse();
551 } else if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
552 XUngrabPointer(_display, CurrentTime);
553 }
554 }
555 }
556
557 if (changed_properties) {
558 system_changed_properties(properties);
559 }
560
561 if (got_keyrelease_event) {
562 // This keyrelease event is not immediately followed by a matching
563 // keypress event, so it's a genuine release.
564 ButtonHandle raw_button = map_raw_button(keyrelease_event.keycode);
565 if (raw_button != ButtonHandle::none()) {
566 _input->raw_button_up(raw_button);
567 }
568
569 handle_keyrelease(keyrelease_event);
570 }
571}
572
573/**
574 * Applies the requested set of properties to the window, if possible, for
575 * instance to request a change in size or minimization status.
576 *
577 * The window properties are applied immediately, rather than waiting until
578 * the next frame. This implies that this method may *only* be called from
579 * within the window thread.
580 *
581 * The return value is true if the properties are set, false if they are
582 * ignored. This is mainly useful for derived classes to implement extensions
583 * to this function.
584 */
587 if (_pipe == nullptr) {
588 // If the pipe is null, we're probably closing down.
590 return;
591 }
592
593 x11GraphicsPipe *x11_pipe;
594 DCAST_INTO_V(x11_pipe, _pipe);
595
596 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
597
598 // We're either going into or out of fullscreen, or are in fullscreen and
599 // are changing the resolution.
600 bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen();
601 bool want_fullscreen = properties.has_fullscreen() ? properties.get_fullscreen() : is_fullscreen;
602
603 if (want_fullscreen && properties.has_origin()) {
604 // If we're fullscreen, reject changes to the origin.
605 properties.clear_origin();
606 }
607
608 if (is_fullscreen != want_fullscreen || (is_fullscreen && properties.has_size())) {
609 if (want_fullscreen) {
610 // OK, first figure out which CRTC the window is on. It may be on more
611 // than one, actually, so grab a point in the center in order to figure
612 // out which one it's more-or-less mostly on.
613 LPoint2i center(0, 0);
614 if (_properties.has_origin()) {
615 center = _properties.get_origin();
616 if (_properties.has_size()) {
617 center += _properties.get_size() / 2;
618 }
619 }
620 int x, y, width, height;
621 x11_pipe->find_fullscreen_crtc(center, x, y, width, height);
622
623 // Which size should we go fullscreen in?
624 int reqsizex, reqsizey;
625 if (properties.has_size()) {
626 reqsizex = properties.get_x_size();
627 reqsizey = properties.get_y_size();
628 } else if (_properties.has_size()) {
629 reqsizex = _properties.get_x_size();
630 reqsizey = _properties.get_y_size();
631 } else {
632 reqsizex = width;
633 reqsizey = height;
634 }
635
636 // Are we passing in pipe.display_width/height? This is actually the
637 // size of the virtual desktop, which may not be a real resolution, so
638 // if that is passed in, we have to assume that the user means to just
639 // fullscreen without changing the screen resolution.
640 if ((reqsizex == x11_pipe->get_display_width() &&
641 reqsizey == x11_pipe->get_display_height())
642 || (width == reqsizex && height == reqsizey)
643 || !x11_pipe->_have_xrandr) {
644
645 // Cover the current CRTC.
646 properties.set_origin(x, y);
647 properties.set_size(width, height);
648
649 if (x11display_cat.is_debug()) {
650 x11display_cat.debug()
651 << "Setting window to fullscreen on CRTC "
652 << width << "x" << height << "+" << x << "+" << y << "\n";
653 }
654 } else {
655 // We may need to change the screen resolution. The code below is
656 // suboptimal; in the future, we probably want to only touch the CRTC
657 // that the window is on.
658 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->get_root());
659 SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
660 SizeID new_size_id = (SizeID) -1;
661 int num_sizes = 0;
662
663 XRRScreenSize *xrrs;
664 xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
665 for (int i = 0; i < num_sizes; ++i) {
666 if (xrrs[i].width == reqsizex &&
667 xrrs[i].height == reqsizey) {
668 new_size_id = i;
669 }
670 }
671 if (new_size_id == (SizeID) -1) {
672 x11display_cat.error()
673 << "Videocard has no supported display resolutions at specified res ("
674 << reqsizex << " x " << reqsizey << ")\n";
675
676 // Just go fullscreen at native resolution, then.
677 properties.set_origin(x, y);
678 properties.set_size(width, height);
679 } else {
680 if (x11display_cat.is_debug()) {
681 x11display_cat.debug()
682 << "Switching to fullscreen with resolution "
683 << reqsizex << "x" << reqsizey << "\n";
684 }
685
686 if (new_size_id != old_size_id) {
687 _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime);
688 if (_orig_size_id == (SizeID) -1) {
689 // Remember the original resolution so we can switch back to it.
690 _orig_size_id = old_size_id;
691 }
692
693 // Since the above changes the entire screen configuration, we
694 // have to set the origin to 0, 0.
695 properties.set_origin(0, 0);
696 }
697 }
698 }
699 } else {
700 // Change the resolution back to what it was. Don't remove the SizeID
701 // typecast!
702 if (_orig_size_id != (SizeID) -1) {
703 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->get_root());
704 _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), _orig_size_id, _orig_rotation, CurrentTime);
705 _orig_size_id = (SizeID) -1;
706 }
707 // Set the origin back to what it was
708 if (!properties.has_origin() && _properties.has_origin()) {
709 properties.set_origin(_properties.get_x_origin(), _properties.get_y_origin());
710 }
711 }
712 }
713
714 if (properties.has_origin()) {
715 // A coordinate of -2 means to center the window on screen.
716 if (properties.get_x_origin() == -2 || properties.get_y_origin() == -2) {
717 int x_origin = properties.get_x_origin();
718 int y_origin = properties.get_y_origin();
719 if (properties.has_size()) {
720 if (x_origin == -2) {
721 x_origin = 0.5 * (x11_pipe->get_display_width() - properties.get_x_size());
722 }
723 if (y_origin == -2) {
724 y_origin = 0.5 * (x11_pipe->get_display_height() - properties.get_y_size());
725 }
726 } else {
727 if (x_origin == -2) {
728 x_origin = 0.5 * (x11_pipe->get_display_width() - _properties.get_x_size());
729 }
730 if (y_origin == -2) {
731 y_origin = 0.5 * (x11_pipe->get_display_height() - _properties.get_y_size());
732 }
733 }
734 properties.set_origin(x_origin, y_origin);
735 }
736 }
737
739 if (!properties.is_any_specified()) {
740 // The base class has already handled this case.
741 return;
742 }
743
744 // The window is already open; we are limited to what we can change on the
745 // fly.
746
747 // We'll pass some property requests on as a window manager hint.
748 set_wm_properties(properties, true);
749
750 // The window title may be changed by issuing another hint request. Assume
751 // this will be honored.
752 if (properties.has_title()) {
753 _properties.set_title(properties.get_title());
754 properties.clear_title();
755 }
756
757 // Same for fullscreen.
758 if (properties.has_fullscreen()) {
759 _properties.set_fullscreen(properties.get_fullscreen());
760 properties.clear_fullscreen();
761 }
762
763 // The size and position of an already-open window are changed via explicit
764 // X calls. These may still get intercepted by the window manager. Rather
765 // than changing _properties immediately, we'll wait for the ConfigureNotify
766 // message to come back.
767 XWindowChanges changes;
768 int value_mask = 0;
769
770 if (_properties.get_fullscreen()) {
771 if (_properties.get_x_origin() != 0 ||
772 _properties.get_y_origin() != 0) {
773 changes.x = 0;
774 changes.y = 0;
775 value_mask |= CWX | CWY;
776 properties.clear_origin();
777 }
778 }
779 else if (properties.has_origin()) {
780 changes.x = properties.get_x_origin();
781 changes.y = properties.get_y_origin();
782 if (changes.x != -1) value_mask |= CWX;
783 if (changes.y != -1) value_mask |= CWY;
784 properties.clear_origin();
785 }
786
787 // This, too. But we can't currently change out of fixed_size mode.
788 if (properties.has_fixed_size() && properties.get_fixed_size()) {
789 _properties.set_fixed_size(properties.get_fixed_size());
790 properties.clear_fixed_size();
791 _fixed_size = _properties.get_size();
792 }
793
794 if (properties.has_size()) {
795 changes.width = properties.get_x_size();
796 changes.height = properties.get_y_size();
797 value_mask |= (CWWidth | CWHeight);
798
799 if (_properties.get_fixed_size()) {
800 _fixed_size = properties.get_size();
801 }
802 properties.clear_size();
803 }
804
805 if (properties.has_z_order()) {
806 // We'll send the classic stacking request through the standard interface,
807 // for users of primitive window managers; but we'll also send it as a
808 // window manager hint, for users of modern window managers.
809 _properties.set_z_order(properties.get_z_order());
810 switch (properties.get_z_order()) {
811 case WindowProperties::Z_bottom:
812 changes.stack_mode = Below;
813 break;
814
815 case WindowProperties::Z_normal:
816 changes.stack_mode = TopIf;
817 break;
818
819 case WindowProperties::Z_top:
820 changes.stack_mode = Above;
821 break;
822 }
823
824 value_mask |= (CWStackMode);
825 properties.clear_z_order();
826 }
827
828 // We hide the cursor by setting it to an invisible pixmap. We can also
829 // load a custom cursor from a file.
830 if (properties.has_cursor_hidden() || properties.has_cursor_filename()) {
831 if (properties.has_cursor_hidden()) {
832 _properties.set_cursor_hidden(properties.get_cursor_hidden());
833 properties.clear_cursor_hidden();
834 }
835 Filename cursor_filename;
836 if (properties.has_cursor_filename()) {
837 cursor_filename = properties.get_cursor_filename();
838 _properties.set_cursor_filename(cursor_filename);
839 properties.clear_cursor_filename();
840 }
841 Filename filename = properties.get_cursor_filename();
842 _properties.set_cursor_filename(filename);
843
844 if (_properties.get_cursor_hidden()) {
845 XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
846
847 } else if (!cursor_filename.empty()) {
848 // Note that if the cursor fails to load, cursor will be None
849 X11_Cursor cursor = get_cursor(cursor_filename);
850 XDefineCursor(_display, _xwindow, cursor);
851
852 } else {
853 XDefineCursor(_display, _xwindow, None);
854 }
855
856 // Regrab the mouse if we changed the cursor, otherwise it won't update.
857 if (!properties.has_mouse_mode() &&
858 _properties.get_mouse_mode() != WindowProperties::M_absolute) {
859 properties.set_mouse_mode(_properties.get_mouse_mode());
860 }
861 }
862
863 if (properties.has_foreground()) {
864 if (properties.get_foreground()) {
865 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
866 } else {
867 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
868 }
869 properties.clear_foreground();
870 }
871
872 if (properties.has_mouse_mode()) {
873 switch (properties.get_mouse_mode()) {
874 case WindowProperties::M_absolute:
875 XUngrabPointer(_display, CurrentTime);
876 if (_dga_mouse_enabled) {
877 x11_pipe->disable_relative_mouse();
878 _dga_mouse_enabled = false;
879 }
880 _properties.set_mouse_mode(WindowProperties::M_absolute);
881 properties.clear_mouse_mode();
882 break;
883
884 case WindowProperties::M_relative:
885 if (!_dga_mouse_enabled) {
886 if (x11_pipe->supports_relative_mouse()) {
887 X11_Cursor cursor = None;
888 if (_properties.get_cursor_hidden()) {
889 x11GraphicsPipe *x11_pipe;
890 DCAST_INTO_V(x11_pipe, _pipe);
891 cursor = x11_pipe->get_hidden_cursor();
892 }
893
894 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
895 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
896 x11display_cat.error() << "Failed to grab pointer!\n";
897 } else {
898 x11_pipe->enable_relative_mouse();
899
900 _properties.set_mouse_mode(WindowProperties::M_relative);
901 properties.clear_mouse_mode();
902 _dga_mouse_enabled = true;
903
904 // Get the real mouse position, so we can addsubtract our relative
905 // coordinates later.
906 XEvent event;
907 XQueryPointer(_display, _xwindow, &event.xbutton.root,
908 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
909 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
910 _input->set_pointer_in_window(event.xbutton.x, event.xbutton.y);
911 }
912 } else {
913 x11display_cat.warning()
914 << "XF86DGA extension not available, cannot enable relative mouse mode\n";
915 _dga_mouse_enabled = false;
916 }
917 }
918 break;
919
920 case WindowProperties::M_confined:
921 {
922 x11GraphicsPipe *x11_pipe;
923 DCAST_INTO_V(x11_pipe, _pipe);
924
925 if (_dga_mouse_enabled) {
926 x11_pipe->disable_relative_mouse();
927 _dga_mouse_enabled = false;
928 }
929 X11_Cursor cursor = None;
930 if (_properties.get_cursor_hidden()) {
931 cursor = x11_pipe->get_hidden_cursor();
932 }
933
934 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
935 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
936 x11display_cat.error() << "Failed to grab pointer!\n";
937 } else {
938 _properties.set_mouse_mode(WindowProperties::M_confined);
939 properties.clear_mouse_mode();
940 }
941 }
942 break;
943 }
944 }
945
946 if (value_mask != 0) {
947 // We must call this after changing the WM properties, otherwise we may
948 // get misleading ConfigureNotify events in the wrong order.
949 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
950
951 // Don't draw anything until this is done reconfiguring.
952 _awaiting_configure = true;
953 }
954}
955
956/**
957 * Overridden from GraphicsWindow.
958 */
959void x11GraphicsWindow::
960mouse_mode_absolute() {
961 // unused: remove in 1.10!
962}
963
964/**
965 * Overridden from GraphicsWindow.
966 */
967void x11GraphicsWindow::
968mouse_mode_relative() {
969 // unused: remove in 1.10!
970}
971
972/**
973 * Closes the window right now. Called from the window thread.
974 */
975void x11GraphicsWindow::
976close_window() {
977 if (_gsg != nullptr) {
978 _gsg.clear();
979 }
980
981 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
982 if (_ic != (XIC)nullptr) {
983 XDestroyIC(_ic);
984 _ic = (XIC)nullptr;
985 }
986
987 if (_xwindow != (X11_Window)nullptr) {
988 XDestroyWindow(_display, _xwindow);
989 _xwindow = (X11_Window)nullptr;
990
991 // This may be necessary if we just closed the last X window in an
992 // application, so the server hears the close request.
993 XFlush(_display);
994 }
995
996 // Change the resolution back to what it was. Don't remove the SizeID
997 // typecast!
998 if (_orig_size_id != (SizeID) -1) {
999 X11_Window root;
1000 if (_pipe != nullptr) {
1001 x11GraphicsPipe *x11_pipe;
1002 DCAST_INTO_V(x11_pipe, _pipe);
1003 root = x11_pipe->get_root();
1004 } else {
1005 // Oops. Looks like the pipe was destroyed before the window gets
1006 // closed. Oh well, let's get the root window by ourselves.
1007 root = RootWindow(_display, _screen);
1008 }
1009 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
1010 _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
1011 _orig_size_id = -1;
1012 }
1013
1014 GraphicsWindow::close_window();
1015}
1016
1017/**
1018 * Opens the window right now. Called from the window thread. Returns true
1019 * if the window is successfully opened, or false if there was a problem.
1020 */
1021bool x11GraphicsWindow::
1022open_window() {
1023 if (_visual_info == nullptr) {
1024 // No X visual for this fbconfig; how can we open the window?
1025 x11display_cat.error()
1026 << "No X visual: cannot open window.\n";
1027 return false;
1028 }
1029
1030 x11GraphicsPipe *x11_pipe;
1031 DCAST_INTO_R(x11_pipe, _pipe, false);
1032
1033 if (!_properties.has_origin()) {
1034 _properties.set_origin(0, 0);
1035 }
1036 if (!_properties.has_size()) {
1037 _properties.set_size(100, 100);
1038 }
1039
1040 // Make sure we are not making X11 calls from other threads.
1041 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
1042
1043 X11_Window parent_window = x11_pipe->get_root();
1044 WindowHandle *window_handle = _properties.get_parent_window();
1045 if (window_handle != nullptr) {
1046 x11display_cat.info()
1047 << "Got parent_window " << *window_handle << "\n";
1048 WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
1049 if (os_handle != nullptr) {
1050 x11display_cat.info()
1051 << "os_handle type " << os_handle->get_type() << "\n";
1052
1053 if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1054 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1055 parent_window = x11_handle->get_handle();
1056 } else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1057 NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
1058 parent_window = (X11_Window)int_handle->get_handle();
1059 }
1060 }
1061 }
1062 _parent_window_handle = window_handle;
1063
1064 _event_mask =
1065 ButtonPressMask | ButtonReleaseMask |
1066 KeyPressMask | KeyReleaseMask |
1067 EnterWindowMask | LeaveWindowMask |
1068 PointerMotionMask |
1069 FocusChangeMask | StructureNotifyMask;
1070
1071 // Initialize window attributes
1072 XSetWindowAttributes wa;
1073 wa.background_pixel = XBlackPixel(_display, _screen);
1074 wa.border_pixel = 0;
1075 wa.colormap = _colormap;
1076 wa.event_mask = _event_mask;
1077 wa.override_redirect = _override_redirect;
1078
1079 unsigned long attrib_mask =
1080 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1081
1082 _xwindow = XCreateWindow
1083 (_display, parent_window,
1084 _properties.get_x_origin(), _properties.get_y_origin(),
1085 _properties.get_x_size(), _properties.get_y_size(),
1086 0, _visual_info->depth, InputOutput,
1087 _visual_info->visual, attrib_mask, &wa);
1088
1089 if (_xwindow == (X11_Window)0) {
1090 x11display_cat.error()
1091 << "failed to create X window.\n";
1092 return false;
1093 }
1094
1095 if (_properties.get_fixed_size()) {
1096 _fixed_size = _properties.get_size();
1097 }
1098
1099 set_wm_properties(_properties, false);
1100
1101 // We don't specify any fancy properties of the XIC. It would be nicer if
1102 // we could support fancy IM's that want preedit callbacks, etc., but that
1103 // can wait until we have an X server that actually supports these to test
1104 // it on.
1105 XIM im = x11_pipe->get_im();
1106 _ic = nullptr;
1107 if (im) {
1108 _ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1109 XNClientWindow, _xwindow, nullptr);
1110 if (_ic == (XIC)nullptr) {
1111 x11display_cat.warning()
1112 << "Couldn't create input context.\n";
1113 }
1114 }
1115
1116 if (_properties.get_cursor_hidden()) {
1117 XDefineCursor(_display, _xwindow, x11_pipe->get_hidden_cursor());
1118
1119 } else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1120 // Note that if the cursor fails to load, cursor will be None
1121 X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1122 XDefineCursor(_display, _xwindow, cursor);
1123 }
1124
1125 XMapWindow(_display, _xwindow);
1126
1127 if (_properties.get_raw_mice()) {
1128 open_raw_mice();
1129 } else {
1130 if (x11display_cat.is_debug()) {
1131 x11display_cat.debug()
1132 << "Raw mice not requested.\n";
1133 }
1134 }
1135
1136 // Create a WindowHandle for ourselves
1137 _window_handle = NativeWindowHandle::make_x11(_xwindow);
1138
1139 // And tell our parent window that we're now its child.
1140 if (_parent_window_handle != nullptr) {
1141 _parent_window_handle->attach_child(_window_handle);
1142 }
1143
1144 return true;
1145}
1146
1147/**
1148 * Asks the window manager to set the appropriate properties. In X, these
1149 * properties cannot be specified directly by the application; they must be
1150 * requested via the window manager, which may or may not choose to honor the
1151 * request.
1152 *
1153 * If already_mapped is true, the window has already been mapped (manifested)
1154 * on the display. This means we may need to use a different action in some
1155 * cases.
1156 *
1157 * Assumes the X11 lock is held.
1158 */
1159void x11GraphicsWindow::
1160set_wm_properties(const WindowProperties &properties, bool already_mapped) {
1161 x11GraphicsPipe *x11_pipe;
1162 DCAST_INTO_V(x11_pipe, _pipe);
1163
1164 // Name the window if there is a name
1165 XTextProperty window_name;
1166 XTextProperty *window_name_p = nullptr;
1167 if (properties.has_title()) {
1168 const char *name = properties.get_title().c_str();
1169 if (XStringListToTextProperty((char **)&name, 1, &window_name) != 0) {
1170 window_name_p = &window_name;
1171 }
1172 }
1173
1174 // The size hints request a window of a particular size andor a particular
1175 // placement onscreen.
1176 XSizeHints *size_hints_p = nullptr;
1177 if (properties.has_origin() || properties.has_size()) {
1178 size_hints_p = XAllocSizeHints();
1179 if (size_hints_p != nullptr) {
1180 if (properties.has_origin()) {
1181 size_hints_p->x = properties.get_x_origin();
1182 size_hints_p->y = properties.get_y_origin();
1183 size_hints_p->flags |= USPosition;
1184 }
1185 LVecBase2i size = _properties.get_size();
1186 if (properties.has_size()) {
1187 size = properties.get_size();
1188 size_hints_p->width = size.get_x();
1189 size_hints_p->height = size.get_y();
1190 size_hints_p->flags |= USSize;
1191 }
1192 if (properties.get_fixed_size()) {
1193 size_hints_p->min_width = size.get_x();
1194 size_hints_p->min_height = size.get_y();
1195 size_hints_p->max_width = size.get_x();
1196 size_hints_p->max_height = size.get_y();
1197 size_hints_p->flags |= (PMinSize | PMaxSize);
1198 }
1199 }
1200 }
1201
1202 // The window manager hints include requests to the window manager other
1203 // than those specific to window geometry.
1204 XWMHints *wm_hints_p = nullptr;
1205 wm_hints_p = XAllocWMHints();
1206 if (wm_hints_p != nullptr) {
1207 if (properties.has_minimized() && properties.get_minimized()) {
1208 wm_hints_p->initial_state = IconicState;
1209 } else {
1210 wm_hints_p->initial_state = NormalState;
1211 }
1212 wm_hints_p->flags = StateHint;
1213 }
1214
1215 // Two competing window manager interfaces have evolved. One of them allows
1216 // to set certain properties as a "type"; the other one as a "state". We'll
1217 // try to honor both.
1218 static const int max_type_data = 32;
1219 int32_t type_data[max_type_data];
1220 int next_type_data = 0;
1221
1222 static const int max_state_data = 32;
1223 int32_t state_data[max_state_data];
1224 int next_state_data = 0;
1225
1226 static const int max_set_data = 32;
1227 class SetAction {
1228 public:
1229 inline SetAction() { }
1230 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1231 Atom _state;
1232 Atom _action;
1233 };
1234 SetAction set_data[max_set_data];
1235 int next_set_data = 0;
1236
1237 if (properties.has_fullscreen()) {
1238 if (properties.get_fullscreen()) {
1239 // For a "fullscreen" request, we pass this through, hoping the window
1240 // manager will support EWMH.
1241 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1242
1243 // We also request it as a state.
1244 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1245 // Don't ask me why this has to be 10 and not _net_wm_state_add. It
1246 // doesn't seem to work otherwise.
1247 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1248
1249 } else {
1250 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1251 }
1252 }
1253
1254 // If we asked for a window without a border, there's no excellent way to
1255 // arrange that. For users whose window managers follow the EWMH
1256 // specification, we can ask for a "splash" screen, which is usually
1257 // undecorated. It's not exactly right, but the spec doesn't give us an
1258 // exactly-right option.
1259
1260 // For other users, we'll totally punt and just set the window's Class to
1261 // "Undecorated", and let the user configure hisher window manager not to
1262 // put a border around windows of this class.
1263 XClassHint *class_hints_p = nullptr;
1264 if (!x_wm_class.empty()) {
1265 // Unless the user wanted to use his own WM_CLASS, of course.
1266 class_hints_p = XAllocClassHint();
1267 class_hints_p->res_class = (char*) x_wm_class.c_str();
1268 if (!x_wm_class_name.empty()) {
1269 class_hints_p->res_name = (char*) x_wm_class_name.c_str();
1270 }
1271
1272 } else if (properties.get_undecorated() || properties.get_fullscreen()) {
1273 class_hints_p = XAllocClassHint();
1274 class_hints_p->res_class = (char*) "Undecorated";
1275 }
1276
1277 if (properties.get_undecorated() && !properties.get_fullscreen()) {
1278 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1279 }
1280
1281 if (properties.has_z_order()) {
1282 switch (properties.get_z_order()) {
1283 case WindowProperties::Z_bottom:
1284 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1285 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1286 x11_pipe->_net_wm_state_add);
1287 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1288 x11_pipe->_net_wm_state_remove);
1289 break;
1290
1291 case WindowProperties::Z_normal:
1292 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1293 x11_pipe->_net_wm_state_remove);
1294 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1295 x11_pipe->_net_wm_state_remove);
1296 break;
1297
1298 case WindowProperties::Z_top:
1299 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1300 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1301 x11_pipe->_net_wm_state_remove);
1302 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1303 x11_pipe->_net_wm_state_add);
1304 break;
1305 }
1306 }
1307
1308 nassertv(next_type_data < max_type_data);
1309 nassertv(next_state_data < max_state_data);
1310 nassertv(next_set_data < max_set_data);
1311
1312 // Add the process ID as a convenience for other applications.
1313 int32_t pid = getpid();
1314 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1315 XA_CARDINAL, 32, PropModeReplace,
1316 (unsigned char *)&pid, 1);
1317
1318 // Disable compositing effects in fullscreen mode.
1319 if (properties.has_fullscreen()) {
1320 int32_t compositor = properties.get_fullscreen() ? 1 : 0;
1321 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1322 XA_CARDINAL, 32, PropModeReplace,
1323 (unsigned char *)&compositor, 1);
1324 }
1325
1326 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1327 XA_ATOM, 32, PropModeReplace,
1328 (unsigned char *)type_data, next_type_data);
1329
1330 // Request the state properties all at once.
1331 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1332 XA_ATOM, 32, PropModeReplace,
1333 (unsigned char *)state_data, next_state_data);
1334
1335 if (already_mapped) {
1336 // We have to request state changes differently when the window has been
1337 // mapped. To do this, we need to send a client message to the root
1338 // window for each change.
1339
1340 x11GraphicsPipe *x11_pipe;
1341 DCAST_INTO_V(x11_pipe, _pipe);
1342
1343 for (int i = 0; i < next_set_data; ++i) {
1344 XClientMessageEvent event;
1345 memset(&event, 0, sizeof(event));
1346 event.type = ClientMessage;
1347 event.send_event = True;
1348 event.display = _display;
1349 event.window = _xwindow;
1350 event.message_type = x11_pipe->_net_wm_state;
1351 event.format = 32;
1352 event.data.l[0] = set_data[i]._action;
1353 event.data.l[1] = set_data[i]._state;
1354 event.data.l[2] = 0;
1355 event.data.l[3] = 1;
1356
1357 XSendEvent(_display, x11_pipe->get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1358 }
1359 }
1360
1361 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1362 nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1363
1364 if (size_hints_p != nullptr) {
1365 XFree(size_hints_p);
1366 }
1367 if (wm_hints_p != nullptr) {
1368 XFree(wm_hints_p);
1369 }
1370 if (class_hints_p != nullptr) {
1371 XFree(class_hints_p);
1372 }
1373
1374 // Also, indicate to the window manager that we'd like to get a chance to
1375 // close our windows cleanly, rather than being rudely disconnected from the
1376 // X server if the user requests a window close.
1377 Atom protocols[] = {
1378 _wm_delete_window,
1379 };
1380
1381 XSetWMProtocols(_display, _xwindow, protocols,
1382 sizeof(protocols) / sizeof(Atom));
1383}
1384
1385/**
1386 * Allocates a colormap appropriate to the visual and stores in in the
1387 * _colormap method.
1388 */
1389void x11GraphicsWindow::
1390setup_colormap(XVisualInfo *visual) {
1391 x11GraphicsPipe *x11_pipe;
1392 DCAST_INTO_V(x11_pipe, _pipe);
1393 X11_Window root_window = x11_pipe->get_root();
1394
1395 _colormap = XCreateColormap(_display, root_window,
1396 visual->visual, AllocNone);
1397}
1398
1399/**
1400 * Adds raw mice to the _input_devices list.
1401 * @deprecated obtain raw devices via the device manager instead.
1402 */
1403void x11GraphicsWindow::
1404open_raw_mice() {
1405#ifdef PHAVE_LINUX_INPUT_H
1406 bool any_present = false;
1407 bool any_mice = false;
1408
1409 for (int i=0; i<64; i++) {
1410 ostringstream fnb;
1411 fnb << "/dev/input/event" << i;
1412 string fn = fnb.str();
1413 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1414 if (fd >= 0) {
1415 EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd);
1416 nassertd(device != NULL) continue;
1417
1418 if (device->has_pointer()) {
1419 add_input_device(device);
1420
1421 x11display_cat.info()
1422 << "Raw mouse " << _input_devices.size()
1423 << " detected: " << device->get_name() << "\n";
1424
1425 any_mice = true;
1426 any_present = true;
1427 }
1428 } else {
1429 if (errno == ENOENT || errno == ENOTDIR) {
1430 break;
1431 } else {
1432 any_present = true;
1433 x11display_cat.error()
1434 << "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
1435 }
1436 }
1437 }
1438
1439 if (any_mice) {
1440 _properties.set_raw_mice(true);
1441
1442 } else if (!any_present) {
1443 x11display_cat.error() <<
1444 "Opening raw mice: files not found: /dev/input/event*\n";
1445
1446 } else {
1447 x11display_cat.error() <<
1448 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1449 }
1450#else
1451 x11display_cat.error() <<
1452 "Opening raw mice: panda not compiled with raw mouse support.\n";
1453#endif
1454}
1455
1456/**
1457 * Generates a keystroke corresponding to the indicated X KeyPress event.
1458 */
1459void x11GraphicsWindow::
1460handle_keystroke(XKeyEvent &event) {
1461 if (!_dga_mouse_enabled) {
1462 _input->set_pointer_in_window(event.x, event.y);
1463 }
1464
1465 if (_ic) {
1466 // First, get the keystroke as a wide-character sequence.
1467 static const int buffer_size = 256;
1468 wchar_t buffer[buffer_size];
1469 Status status;
1470 int len = XwcLookupString(_ic, &event, buffer, buffer_size, nullptr,
1471 &status);
1472 if (status == XBufferOverflow) {
1473 x11display_cat.error()
1474 << "Overflowed input buffer.\n";
1475 }
1476
1477 // Now each of the returned wide characters represents a keystroke.
1478 for (int i = 0; i < len; i++) {
1479 _input->keystroke(buffer[i]);
1480 }
1481
1482 } else {
1483 // Without an input context, just get the ascii keypress.
1484 ButtonHandle button = get_button(event, true);
1485 if (button.has_ascii_equivalent()) {
1486 _input->keystroke(button.get_ascii_equivalent());
1487 }
1488 }
1489}
1490
1491/**
1492 * Generates a keypress corresponding to the indicated X KeyPress event.
1493 */
1494void x11GraphicsWindow::
1495handle_keypress(XKeyEvent &event) {
1496 if (!_dga_mouse_enabled) {
1497 _input->set_pointer_in_window(event.x, event.y);
1498 }
1499
1500 // Now get the raw unshifted button.
1501 ButtonHandle button = get_button(event, false);
1502 if (button != ButtonHandle::none()) {
1503 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1504 _input->button_down(KeyboardButton::control());
1505 }
1506 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1507 _input->button_down(KeyboardButton::shift());
1508 }
1509 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1510 _input->button_down(KeyboardButton::alt());
1511 }
1512 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1513 _input->button_down(KeyboardButton::meta());
1514 }
1515 _input->button_down(button);
1516 }
1517}
1518
1519/**
1520 * Generates a keyrelease corresponding to the indicated X KeyRelease event.
1521 */
1522void x11GraphicsWindow::
1523handle_keyrelease(XKeyEvent &event) {
1524 if (!_dga_mouse_enabled) {
1525 _input->set_pointer_in_window(event.x, event.y);
1526 }
1527
1528 // Now get the raw unshifted button.
1529 ButtonHandle button = get_button(event, false);
1530 if (button != ButtonHandle::none()) {
1531 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1532 _input->button_up(KeyboardButton::control());
1533 }
1534 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1535 _input->button_up(KeyboardButton::shift());
1536 }
1537 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1538 _input->button_up(KeyboardButton::alt());
1539 }
1540 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1541 _input->button_up(KeyboardButton::meta());
1542 }
1543 _input->button_up(button);
1544 }
1545}
1546
1547/**
1548 * Returns the Panda ButtonHandle corresponding to the keyboard button
1549 * indicated by the given key event.
1550 */
1551ButtonHandle x11GraphicsWindow::
1552get_button(XKeyEvent &key_event, bool allow_shift) {
1553 KeySym key = XLookupKeysym(&key_event, 0);
1554
1555 if ((key_event.state & Mod2Mask) != 0) {
1556 // Mod2Mask corresponds to NumLock being in effect. In this case, we want
1557 // to get the alternate keysym associated with any keypad keys. Weird
1558 // system.
1559 KeySym k2;
1560 ButtonHandle button;
1561 switch (key) {
1562 case XK_KP_Space:
1563 case XK_KP_Tab:
1564 case XK_KP_Enter:
1565 case XK_KP_F1:
1566 case XK_KP_F2:
1567 case XK_KP_F3:
1568 case XK_KP_F4:
1569 case XK_KP_Equal:
1570 case XK_KP_Multiply:
1571 case XK_KP_Add:
1572 case XK_KP_Separator:
1573 case XK_KP_Subtract:
1574 case XK_KP_Divide:
1575 case XK_KP_Left:
1576 case XK_KP_Up:
1577 case XK_KP_Right:
1578 case XK_KP_Down:
1579 case XK_KP_Begin:
1580 case XK_KP_Prior:
1581 case XK_KP_Next:
1582 case XK_KP_Home:
1583 case XK_KP_End:
1584 case XK_KP_Insert:
1585 case XK_KP_Delete:
1586 case XK_KP_0:
1587 case XK_KP_1:
1588 case XK_KP_2:
1589 case XK_KP_3:
1590 case XK_KP_4:
1591 case XK_KP_5:
1592 case XK_KP_6:
1593 case XK_KP_7:
1594 case XK_KP_8:
1595 case XK_KP_9:
1596 k2 = XLookupKeysym(&key_event, 1);
1597 button = map_button(k2);
1598 if (button != ButtonHandle::none()) {
1599 return button;
1600 }
1601 // If that didn't produce a button we know, just fall through and handle
1602 // the normal, un-numlocked key.
1603 break;
1604
1605 default:
1606 break;
1607 }
1608 }
1609
1610 if (allow_shift) {
1611 // If shift is held down, get the shifted keysym.
1612 if ((key_event.state & ShiftMask) != 0) {
1613 KeySym k2 = XLookupKeysym(&key_event, 1);
1614 ButtonHandle button = map_button(k2);
1615 if (button != ButtonHandle::none()) {
1616 return button;
1617 }
1618 }
1619
1620 // If caps lock is down, shift lowercase letters to uppercase. We can do
1621 // this in just the ASCII set, because we handle international keyboards
1622 // elsewhere (via an input context).
1623 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1624 if (key >= XK_a && key <= XK_z) {
1625 key += (XK_A - XK_a);
1626 }
1627 }
1628 }
1629
1630 return map_button(key);
1631}
1632
1633/**
1634 * Maps from a single X keysym to Panda's ButtonHandle. Called by
1635 * get_button(), above.
1636 */
1637ButtonHandle x11GraphicsWindow::
1638map_button(KeySym key) const {
1639 switch (key) {
1640 case NoSymbol:
1641 return ButtonHandle::none();
1642 case XK_BackSpace:
1643 return KeyboardButton::backspace();
1644 case XK_Tab:
1645 case XK_KP_Tab:
1646 return KeyboardButton::tab();
1647 case XK_Return:
1648 case XK_KP_Enter:
1649 return KeyboardButton::enter();
1650 case XK_Escape:
1651 return KeyboardButton::escape();
1652 case XK_KP_Space:
1653 case XK_space:
1654 return KeyboardButton::space();
1655 case XK_exclam:
1656 return KeyboardButton::ascii_key('!');
1657 case XK_quotedbl:
1658 return KeyboardButton::ascii_key('"');
1659 case XK_numbersign:
1660 return KeyboardButton::ascii_key('#');
1661 case XK_dollar:
1662 return KeyboardButton::ascii_key('$');
1663 case XK_percent:
1664 return KeyboardButton::ascii_key('%');
1665 case XK_ampersand:
1666 return KeyboardButton::ascii_key('&');
1667 case XK_apostrophe: // == XK_quoteright
1668 case XK_dead_acute: // on int'l keyboards
1669 return KeyboardButton::ascii_key('\'');
1670 case XK_parenleft:
1671 return KeyboardButton::ascii_key('(');
1672 case XK_parenright:
1673 return KeyboardButton::ascii_key(')');
1674 case XK_asterisk:
1675 case XK_KP_Multiply:
1676 return KeyboardButton::ascii_key('*');
1677 case XK_plus:
1678 case XK_KP_Add:
1679 return KeyboardButton::ascii_key('+');
1680 case XK_comma:
1681 case XK_KP_Separator:
1682 return KeyboardButton::ascii_key(',');
1683 case XK_minus:
1684 case XK_KP_Subtract:
1685 return KeyboardButton::ascii_key('-');
1686 case XK_period:
1687 case XK_KP_Decimal:
1688 return KeyboardButton::ascii_key('.');
1689 case XK_slash:
1690 case XK_KP_Divide:
1691 return KeyboardButton::ascii_key('/');
1692 case XK_0:
1693 case XK_KP_0:
1694 return KeyboardButton::ascii_key('0');
1695 case XK_1:
1696 case XK_KP_1:
1697 return KeyboardButton::ascii_key('1');
1698 case XK_2:
1699 case XK_KP_2:
1700 return KeyboardButton::ascii_key('2');
1701 case XK_3:
1702 case XK_KP_3:
1703 return KeyboardButton::ascii_key('3');
1704 case XK_4:
1705 case XK_KP_4:
1706 return KeyboardButton::ascii_key('4');
1707 case XK_5:
1708 case XK_KP_5:
1709 return KeyboardButton::ascii_key('5');
1710 case XK_6:
1711 case XK_KP_6:
1712 return KeyboardButton::ascii_key('6');
1713 case XK_7:
1714 case XK_KP_7:
1715 return KeyboardButton::ascii_key('7');
1716 case XK_8:
1717 case XK_KP_8:
1718 return KeyboardButton::ascii_key('8');
1719 case XK_9:
1720 case XK_KP_9:
1721 return KeyboardButton::ascii_key('9');
1722 case XK_colon:
1723 return KeyboardButton::ascii_key(':');
1724 case XK_semicolon:
1725 return KeyboardButton::ascii_key(';');
1726 case XK_less:
1727 return KeyboardButton::ascii_key('<');
1728 case XK_equal:
1729 case XK_KP_Equal:
1730 return KeyboardButton::ascii_key('=');
1731 case XK_greater:
1732 return KeyboardButton::ascii_key('>');
1733 case XK_question:
1734 return KeyboardButton::ascii_key('?');
1735 case XK_at:
1736 return KeyboardButton::ascii_key('@');
1737 case XK_A:
1738 return KeyboardButton::ascii_key('A');
1739 case XK_B:
1740 return KeyboardButton::ascii_key('B');
1741 case XK_C:
1742 return KeyboardButton::ascii_key('C');
1743 case XK_D:
1744 return KeyboardButton::ascii_key('D');
1745 case XK_E:
1746 return KeyboardButton::ascii_key('E');
1747 case XK_F:
1748 return KeyboardButton::ascii_key('F');
1749 case XK_G:
1750 return KeyboardButton::ascii_key('G');
1751 case XK_H:
1752 return KeyboardButton::ascii_key('H');
1753 case XK_I:
1754 return KeyboardButton::ascii_key('I');
1755 case XK_J:
1756 return KeyboardButton::ascii_key('J');
1757 case XK_K:
1758 return KeyboardButton::ascii_key('K');
1759 case XK_L:
1760 return KeyboardButton::ascii_key('L');
1761 case XK_M:
1762 return KeyboardButton::ascii_key('M');
1763 case XK_N:
1764 return KeyboardButton::ascii_key('N');
1765 case XK_O:
1766 return KeyboardButton::ascii_key('O');
1767 case XK_P:
1768 return KeyboardButton::ascii_key('P');
1769 case XK_Q:
1770 return KeyboardButton::ascii_key('Q');
1771 case XK_R:
1772 return KeyboardButton::ascii_key('R');
1773 case XK_S:
1774 return KeyboardButton::ascii_key('S');
1775 case XK_T:
1776 return KeyboardButton::ascii_key('T');
1777 case XK_U:
1778 return KeyboardButton::ascii_key('U');
1779 case XK_V:
1780 return KeyboardButton::ascii_key('V');
1781 case XK_W:
1782 return KeyboardButton::ascii_key('W');
1783 case XK_X:
1784 return KeyboardButton::ascii_key('X');
1785 case XK_Y:
1786 return KeyboardButton::ascii_key('Y');
1787 case XK_Z:
1788 return KeyboardButton::ascii_key('Z');
1789 case XK_bracketleft:
1790 return KeyboardButton::ascii_key('[');
1791 case XK_backslash:
1792 return KeyboardButton::ascii_key('\\');
1793 case XK_bracketright:
1794 return KeyboardButton::ascii_key(']');
1795 case XK_asciicircum:
1796 return KeyboardButton::ascii_key('^');
1797 case XK_underscore:
1798 return KeyboardButton::ascii_key('_');
1799 case XK_grave: // == XK_quoteleft
1800 case XK_dead_grave: // on int'l keyboards
1801 return KeyboardButton::ascii_key('`');
1802 case XK_a:
1803 return KeyboardButton::ascii_key('a');
1804 case XK_b:
1805 return KeyboardButton::ascii_key('b');
1806 case XK_c:
1807 return KeyboardButton::ascii_key('c');
1808 case XK_d:
1809 return KeyboardButton::ascii_key('d');
1810 case XK_e:
1811 return KeyboardButton::ascii_key('e');
1812 case XK_f:
1813 return KeyboardButton::ascii_key('f');
1814 case XK_g:
1815 return KeyboardButton::ascii_key('g');
1816 case XK_h:
1817 return KeyboardButton::ascii_key('h');
1818 case XK_i:
1819 return KeyboardButton::ascii_key('i');
1820 case XK_j:
1821 return KeyboardButton::ascii_key('j');
1822 case XK_k:
1823 return KeyboardButton::ascii_key('k');
1824 case XK_l:
1825 return KeyboardButton::ascii_key('l');
1826 case XK_m:
1827 return KeyboardButton::ascii_key('m');
1828 case XK_n:
1829 return KeyboardButton::ascii_key('n');
1830 case XK_o:
1831 return KeyboardButton::ascii_key('o');
1832 case XK_p:
1833 return KeyboardButton::ascii_key('p');
1834 case XK_q:
1835 return KeyboardButton::ascii_key('q');
1836 case XK_r:
1837 return KeyboardButton::ascii_key('r');
1838 case XK_s:
1839 return KeyboardButton::ascii_key('s');
1840 case XK_t:
1841 return KeyboardButton::ascii_key('t');
1842 case XK_u:
1843 return KeyboardButton::ascii_key('u');
1844 case XK_v:
1845 return KeyboardButton::ascii_key('v');
1846 case XK_w:
1847 return KeyboardButton::ascii_key('w');
1848 case XK_x:
1849 return KeyboardButton::ascii_key('x');
1850 case XK_y:
1851 return KeyboardButton::ascii_key('y');
1852 case XK_z:
1853 return KeyboardButton::ascii_key('z');
1854 case XK_braceleft:
1855 return KeyboardButton::ascii_key('{');
1856 case XK_bar:
1857 return KeyboardButton::ascii_key('|');
1858 case XK_braceright:
1859 return KeyboardButton::ascii_key('}');
1860 case XK_asciitilde:
1861 return KeyboardButton::ascii_key('~');
1862 case XK_F1:
1863 case XK_KP_F1:
1864 return KeyboardButton::f1();
1865 case XK_F2:
1866 case XK_KP_F2:
1867 return KeyboardButton::f2();
1868 case XK_F3:
1869 case XK_KP_F3:
1870 return KeyboardButton::f3();
1871 case XK_F4:
1872 case XK_KP_F4:
1873 return KeyboardButton::f4();
1874 case XK_F5:
1875 return KeyboardButton::f5();
1876 case XK_F6:
1877 return KeyboardButton::f6();
1878 case XK_F7:
1879 return KeyboardButton::f7();
1880 case XK_F8:
1881 return KeyboardButton::f8();
1882 case XK_F9:
1883 return KeyboardButton::f9();
1884 case XK_F10:
1885 return KeyboardButton::f10();
1886 case XK_F11:
1887 return KeyboardButton::f11();
1888 case XK_F12:
1889 return KeyboardButton::f12();
1890 case XK_KP_Left:
1891 case XK_Left:
1892 return KeyboardButton::left();
1893 case XK_KP_Up:
1894 case XK_Up:
1895 return KeyboardButton::up();
1896 case XK_KP_Right:
1897 case XK_Right:
1898 return KeyboardButton::right();
1899 case XK_KP_Down:
1900 case XK_Down:
1901 return KeyboardButton::down();
1902 case XK_KP_Prior:
1903 case XK_Prior:
1904 return KeyboardButton::page_up();
1905 case XK_KP_Next:
1906 case XK_Next:
1907 return KeyboardButton::page_down();
1908 case XK_KP_Home:
1909 case XK_Home:
1910 return KeyboardButton::home();
1911 case XK_KP_End:
1912 case XK_End:
1913 return KeyboardButton::end();
1914 case XK_KP_Insert:
1915 case XK_Insert:
1916 return KeyboardButton::insert();
1917 case XK_KP_Delete:
1918 case XK_Delete:
1919 return KeyboardButton::del();
1920 case XK_Num_Lock:
1921 return KeyboardButton::num_lock();
1922 case XK_Scroll_Lock:
1923 return KeyboardButton::scroll_lock();
1924 case XK_Print:
1925 return KeyboardButton::print_screen();
1926 case XK_Pause:
1927 return KeyboardButton::pause();
1928 case XK_Menu:
1929 return KeyboardButton::menu();
1930 case XK_Shift_L:
1931 return KeyboardButton::lshift();
1932 case XK_Shift_R:
1933 return KeyboardButton::rshift();
1934 case XK_Control_L:
1935 return KeyboardButton::lcontrol();
1936 case XK_Control_R:
1937 return KeyboardButton::rcontrol();
1938 case XK_Alt_L:
1939 return KeyboardButton::lalt();
1940 case XK_Alt_R:
1941 return KeyboardButton::ralt();
1942 case XK_Meta_L:
1943 case XK_Super_L:
1944 return KeyboardButton::lmeta();
1945 case XK_Meta_R:
1946 case XK_Super_R:
1947 return KeyboardButton::rmeta();
1948 case XK_Caps_Lock:
1949 return KeyboardButton::caps_lock();
1950 case XK_Shift_Lock:
1951 return KeyboardButton::shift_lock();
1952 }
1953 if (x11display_cat.is_debug()) {
1954 x11display_cat.debug()
1955 << "Unrecognized keysym 0x" << std::hex << key << std::dec << "\n";
1956 }
1957 return ButtonHandle::none();
1958}
1959
1960/**
1961 * Maps from a single X keycode to Panda's ButtonHandle.
1962 */
1963ButtonHandle x11GraphicsWindow::
1964map_raw_button(KeyCode key) const {
1965#ifdef PHAVE_LINUX_INPUT_H
1966 // Most X11 servers are configured to use the evdev driver, which
1967 // adds 8 to the underlying evdev keycodes (not sure why).
1968 // In any case, this means we can use the same mapping as our raw
1969 // input code, which uses evdev directly.
1970 int index = key - 8;
1971 if (index > 0 && index < 128) {
1972 return EvdevInputDevice::map_button(index);
1973 }
1974#endif
1975 return ButtonHandle::none();
1976}
1977
1978/**
1979 * Returns the Panda ButtonHandle corresponding to the mouse button indicated
1980 * by the given button event.
1981 */
1982ButtonHandle x11GraphicsWindow::
1983get_mouse_button(XButtonEvent &button_event) {
1984 int index = button_event.button;
1985 if (index == x_wheel_up_button) {
1986 return MouseButton::wheel_up();
1987 } else if (index == x_wheel_down_button) {
1988 return MouseButton::wheel_down();
1989 } else if (index == x_wheel_left_button) {
1990 return MouseButton::wheel_left();
1991 } else if (index == x_wheel_right_button) {
1992 return MouseButton::wheel_right();
1993 } else if (index >= 8) {
1994 return MouseButton::button(index - 5);
1995 } else {
1996 return MouseButton::button(index - 1);
1997 }
1998}
1999
2000/**
2001 * Returns a ButtonMap containing the association between raw buttons and
2002 * virtual buttons.
2003 */
2004ButtonMap *x11GraphicsWindow::
2005get_keyboard_map() const {
2006 // NB. This could be improved by using the Xkb API. XkbDescPtr desc =
2007 // XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd);
2008 ButtonMap *map = new ButtonMap;
2009
2010 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
2011
2012 for (int k = 9; k <= 135; ++k) {
2013 if (k >= 78 && k <= 91) {
2014 // Ignore numpad keys for now. These are not mapped to separate button
2015 // handles in Panda, so we don't want their mappings to conflict with
2016 // the regular numeric keys.
2017 continue;
2018 }
2019
2020 ButtonHandle raw_button = map_raw_button(k);
2021 if (raw_button == ButtonHandle::none()) {
2022 continue;
2023 }
2024
2025 KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2026 ButtonHandle button = map_button(sym);
2027 std::string label;
2028
2029 // Compose a label for some keys; I have not yet been able to find an API
2030 // that does this effectively.
2031 if (sym >= XK_exclam && sym <= XK_asciitilde) {
2032 label = toupper((char)sym);
2033 }
2034 else if (sym >= XK_F1 && sym <= XK_F35) {
2035 label = "F" + format_string(sym - XK_F1 + 1);
2036 }
2037 else if (sym > 0x1000000 && sym < 0x1110000) {
2038 // Unicode code point. Encode as UTF-8.
2039 char32_t ch = sym & 0x0ffffff;
2040 if ((ch & ~0x7f) == 0) {
2041 label = string(1, (char)ch);
2042 }
2043 else if ((ch & ~0x7ff) == 0) {
2044 label =
2045 string(1, (char)((ch >> 6) | 0xc0)) +
2046 string(1, (char)((ch & 0x3f) | 0x80));
2047 }
2048 else if ((ch & ~0xffff) == 0) {
2049 label =
2050 string(1, (char)((ch >> 12) | 0xe0)) +
2051 string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2052 string(1, (char)((ch & 0x3f) | 0x80));
2053 }
2054 else {
2055 label =
2056 string(1, (char)((ch >> 18) | 0xf0)) +
2057 string(1, (char)(((ch >> 12) & 0x3f) | 0x80)) +
2058 string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2059 string(1, (char)((ch & 0x3f) | 0x80));
2060 }
2061 }
2062 else if ((sym >= XK_exclamdown && sym <= XK_umacron)
2063 || (sym >= XK_OE && sym <= XK_Ydiaeresis)
2064 || (sym >= XK_Serbian_dje && sym <= XK_Cyrillic_HARDSIGN)
2065 || (sym >= XK_kana_fullstop && sym <= XK_semivoicedsound)
2066 || (sym >= XK_Arabic_comma && sym <= XK_Arabic_sukun)
2067 || (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_omega)
2068 || (sym >= XK_hebrew_doublelowline && sym <= XK_hebrew_taw)
2069 || (sym >= XK_Thai_kokai && sym <= XK_Thai_lekkao)
2070 || (sym >= XK_Hangul_Kiyeog && sym <= XK_Hangul_J_YeorinHieuh)
2071 || sym == XK_EuroSign
2072 || sym == XK_Korean_Won) {
2073 // A non-unicode-based keysym. Translate this to the label.
2074 char buffer[255];
2075 int nbytes = XkbTranslateKeySym(_display, &sym, 0, buffer, 255, 0);
2076 if (nbytes > 0) {
2077 label.assign(buffer, nbytes);
2078 }
2079 }
2080
2081 if (button == ButtonHandle::none() && label.empty()) {
2082 // No label and no mapping; this is useless.
2083 continue;
2084 }
2085
2086 map->map_button(raw_button, button, label);
2087 }
2088
2089 return map;
2090}
2091
2092/**
2093 * This function is used as a predicate to XCheckIfEvent() to determine if the
2094 * indicated queued X event is relevant and should be returned to this window.
2095 */
2096Bool x11GraphicsWindow::
2097check_event(X11_Display *display, XEvent *event, char *arg) {
2098 const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
2099
2100 // We accept any event that is sent to our window.
2101 return (event->xany.window == self->_xwindow);
2102}
2103
2104/**
2105 * Loads and returns a Cursor corresponding to the indicated filename. If the
2106 * file cannot be loaded, returns None.
2107 */
2108X11_Cursor x11GraphicsWindow::
2109get_cursor(const Filename &filename) {
2110 x11GraphicsPipe *x11_pipe;
2111 DCAST_INTO_R(x11_pipe, _pipe, None);
2112
2113 if (x11_pipe->_xcursor_size == -1) {
2114 x11display_cat.info()
2115 << "libXcursor.so.1 not available; cannot change mouse cursor.\n";
2116 return None;
2117 }
2118
2119 // First, look for the unresolved filename in our index.
2120 pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
2121 if (fi != _cursor_filenames.end()) {
2122 return fi->second;
2123 }
2124
2125 // If it wasn't found, resolve the filename and search for that.
2127 Filename resolved (filename);
2128 if (!vfs->resolve_filename(resolved, get_model_path())) {
2129 // The filename doesn't exist.
2130 x11display_cat.warning()
2131 << "Could not find cursor filename " << filename << "\n";
2132 return None;
2133 }
2134 fi = _cursor_filenames.find(resolved);
2135 if (fi != _cursor_filenames.end()) {
2136 return fi->second;
2137 }
2138
2139 // Open the file through the virtual file system.
2140 istream *str = vfs->open_read_file(resolved, true);
2141 if (str == nullptr) {
2142 x11display_cat.warning()
2143 << "Could not open cursor file " << filename << "\n";
2144 return None;
2145 }
2146
2147 // Check the first four bytes to see what kind of file it is.
2148 char magic[4];
2149 str->read(magic, 4);
2150 if (!str->good()) {
2151 x11display_cat.warning()
2152 << "Could not read from cursor file " << filename << "\n";
2153 return None;
2154 }
2155
2156 // Put back the read bytes. Do not use seekg, because this will
2157 // corrupt the stream if it points to encrypted/compressed file
2158 str->putback(magic[3]);
2159 str->putback(magic[2]);
2160 str->putback(magic[1]);
2161 str->putback(magic[0]);
2162
2163 X11_Cursor h = None;
2164 if (memcmp(magic, "Xcur", 4) == 0) {
2165 // X11 cursor.
2166 x11display_cat.debug()
2167 << "Loading X11 cursor " << filename << "\n";
2168 XcursorFile xcfile;
2169 xcfile.closure = str;
2170 xcfile.read = &xcursor_read;
2171 xcfile.write = &xcursor_write;
2172 xcfile.seek = &xcursor_seek;
2173
2174 XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2175 if (images != nullptr) {
2176 h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2177 x11_pipe->_XcursorImagesDestroy(images);
2178 }
2179
2180 } else if (memcmp(magic, "\0\0\1\0", 4) == 0
2181 || memcmp(magic, "\0\0\2\0", 4) == 0) {
2182 // Windows .ico or .cur file.
2183 x11display_cat.debug()
2184 << "Loading Windows cursor " << filename << "\n";
2185 h = read_ico(*str);
2186 }
2187
2188 // Delete the istream.
2189 vfs->close_read_file(str);
2190
2191 if (h == None) {
2192 x11display_cat.warning()
2193 << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
2194 }
2195
2196 _cursor_filenames[resolved] = h;
2197 return h;
2198}
2199
2200/**
2201 * Reads a Windows .ico or .cur file from the indicated stream and returns it
2202 * as an X11 Cursor. If the file cannot be loaded, returns None.
2203 */
2204X11_Cursor x11GraphicsWindow::
2205read_ico(istream &ico) {
2206 x11GraphicsPipe *x11_pipe;
2207 DCAST_INTO_R(x11_pipe, _pipe, None);
2208
2209 // Local structs, this is just POD, make input easier
2210 typedef struct {
2211 uint16_t reserved, type, count;
2212 } IcoHeader;
2213
2214 typedef struct {
2215 uint8_t width, height, colorCount, reserved;
2216 uint16_t xhot, yhot;
2217 uint32_t bitmapSize, offset;
2218 } IcoEntry;
2219
2220 typedef struct {
2221 uint32_t headerSize, width, height;
2222 uint16_t planes, bitsPerPixel;
2223 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2224 } IcoInfoHeader;
2225
2226 typedef struct {
2227 uint8_t blue, green, red, reserved;
2228 } IcoColor;
2229
2230 int i, entry = 0;
2231 unsigned int j, k, mask, shift;
2232 size_t colorCount, bitsPerPixel;
2233 IcoHeader header;
2234 IcoInfoHeader infoHeader;
2235 IcoEntry *entries = nullptr;
2236 IcoColor color, *palette = nullptr;
2237
2238 size_t xorBmpSize, andBmpSize;
2239 char *curXor, *curAnd;
2240 char *xorBmp = nullptr, *andBmp = nullptr;
2241 XcursorImage *image = nullptr;
2242 X11_Cursor ret = None;
2243
2244 int def_size = x11_pipe->_xcursor_size;
2245
2246 // Get our header, note that ICO = type 1 and CUR = type 2.
2247 ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
2248 if (!ico.good()) goto cleanup;
2249 if (header.type != 1 && header.type != 2) goto cleanup;
2250 if (header.count < 1) goto cleanup;
2251
2252 // Read the entry table into memory, select the largest entry.
2253 entries = new IcoEntry[header.count];
2254 ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
2255 if (!ico.good()) goto cleanup;
2256 for (i = 1; i < header.count; i++) {
2257 if (entries[i].width == def_size && entries[i].height == def_size) {
2258 // Wait, this is the default cursor size. This is perfect.
2259 entry = i;
2260 break;
2261 }
2262 if (entries[i].width > entries[entry].width ||
2263 entries[i].height > entries[entry].height)
2264 entry = i;
2265 }
2266
2267 // Seek to the image in the ICO.
2268 ico.seekg(entries[entry].offset);
2269 if (!ico.good()) goto cleanup;
2270
2271 if (ico.peek() == 0x89) {
2272 // Hang on, this is actually a PNG header.
2273 PNMImage img;
2275 if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2276 goto cleanup;
2277 }
2278 img.set_maxval(255);
2279
2280 image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size());
2281
2282 xel *ptr = img.get_array();
2283 xelval *alpha = img.get_alpha_array();
2284 size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2285 unsigned int *dest = image->pixels;
2286
2287 if (alpha != nullptr) {
2288 for (size_t p = 0; p < num_pixels; ++p) {
2289 *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2290 ++ptr;
2291 ++alpha;
2292 }
2293 } else {
2294 for (size_t p = 0; p < num_pixels; ++p) {
2295 *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2296 ++ptr;
2297 }
2298 }
2299
2300 } else {
2301 ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2302 if (!ico.good()) goto cleanup;
2303 bitsPerPixel = infoHeader.bitsPerPixel;
2304
2305 if (infoHeader.compression != 0) goto cleanup;
2306
2307 // Load the color palette, if one exists.
2308 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2309 colorCount = 1 << bitsPerPixel;
2310 palette = new IcoColor[colorCount];
2311 ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2312 if (!ico.good()) goto cleanup;
2313 }
2314
2315 int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2316
2317 // Read in the pixel data.
2318 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2319 andBmpSize = and_stride * (infoHeader.height / 2);
2320 curXor = xorBmp = new char[xorBmpSize];
2321 curAnd = andBmp = new char[andBmpSize];
2322 ico.read(xorBmp, xorBmpSize);
2323 if (!ico.good()) goto cleanup;
2324 ico.read(andBmp, andBmpSize);
2325 if (!ico.good()) goto cleanup;
2326
2327 image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2328
2329 // Support all the formats that GIMP supports.
2330 switch (bitsPerPixel) {
2331 case 1:
2332 case 4:
2333 case 8:
2334 // For colors less that a byte wide, shift and mask the palette indices
2335 // off each element of the xorBmp and append them to the image.
2336 mask = ((1 << bitsPerPixel) - 1);
2337 for (i = image->height - 1; i >= 0; i--) {
2338 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2339 for (k = 0; k < 8 / bitsPerPixel; k++) {
2340 shift = 8 - ((k + 1) * bitsPerPixel);
2341 color = palette[(*curXor & (mask << shift)) >> shift];
2342 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2343 (color.green << 8) +
2344 (color.blue);
2345 }
2346
2347 curXor++;
2348 }
2349
2350 // Set the alpha byte properly according to the andBmp.
2351 for (j = 0; j < image->width; j += 8) {
2352 for (k = 0; k < 8; k++) {
2353 shift = 7 - k;
2354 image->pixels[(i * image->width) + j + k] |=
2355 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2356 }
2357
2358 curAnd++;
2359 }
2360 }
2361 break;
2362
2363 case 24:
2364 // Pack each of the three bytes into a single color, BGR -> 0RGB
2365 for (i = image->height - 1; i >= 0; i--) {
2366 for (j = 0; j < image->width; j++) {
2367 shift = 7 - (j & 0x7);
2368 uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2369 image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2370 | ((uint8_t)curXor[1] << 8u)
2371 | ((uint8_t)curXor[2] << 16u)
2372 | alpha;
2373 curXor += 3;
2374 }
2375 curAnd += and_stride;
2376 }
2377 break;
2378
2379 case 32:
2380 // Pack each of the four bytes into a single color, BGRA -> ARGB
2381 for (i = image->height - 1; i >= 0; i--) {
2382 for (j = 0; j < image->width; j++) {
2383 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2384 (*(curXor + 2) << 16) +
2385 (*(curXor + 1) << 8) +
2386 (*curXor);
2387 curXor += 4;
2388 }
2389 }
2390 break;
2391
2392 default:
2393 goto cleanup;
2394 }
2395 }
2396
2397 // If this is an actual CUR not an ICO set up the hotspot properly.
2398 if (header.type == 2) {
2399 image->xhot = entries[entry].xhot;
2400 image->yhot = entries[entry].yhot;
2401 } else {
2402 image->xhot = 0;
2403 image->yhot = 0;
2404 }
2405
2406 ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2407
2408cleanup:
2409 x11_pipe->_XcursorImageDestroy(image);
2410 delete[] entries;
2411 delete[] palette;
2412 delete[] xorBmp;
2413 delete[] andBmp;
2414
2415 return ret;
2416}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A ButtonHandle represents a single button from any device, including keyboard buttons and mouse butto...
Definition: buttonHandle.h:26
get_ascii_equivalent
Returns the character code associated with the button, or '\0' if no ASCII code was associated.
Definition: buttonHandle.h:63
has_ascii_equivalent
Returns true if the button was created with an ASCII equivalent code (e.g.
Definition: buttonHandle.h:63
This class represents a map containing all of the buttons of a (keyboard) device, though it can also ...
Definition: buttonMap.h:30
void map_button(ButtonHandle raw_button, ButtonHandle button, const std::string &label="")
Registers a new button mapping.
Definition: buttonMap.cxx:23
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
Definition: clockObject.h:92
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
virtual bool is_any_clear_active() const
Returns true if any of the clear types (so far there are just color or depth) have been set active,...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
get_display_width
Returns the width of the entire display, if it is known.
Definition: graphicsPipe.h:95
get_display_height
Returns the height of the entire display, if it is known.
Definition: graphicsPipe.h:96
Encapsulates all the communication with a particular instance of a given rendering backend.
This is a virtual input device that represents the keyboard and mouse pair that is associated with a ...
void focus_lost(double time=ClockObject::get_global_clock() ->get_frame_time())
This should be called when the window focus is lost, so that we may miss upcoming button events (espe...
void button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
void set_pointer_out_of_window(double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is no longer within ...
void keystroke(int keycode, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated keystroke has been generated.
void raw_button_down(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been depressed.
PointerData get_pointer() const
Returns the PointerData associated with the input device's pointer.
void raw_button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
void set_pointer_in_window(double x, double y, double time=ClockObject::get_global_clock() ->get_frame_time())
To be called by a particular kind of GraphicsWindow to indicate that the pointer is within the window...
void button_up(ButtonHandle button, double time=ClockObject::get_global_clock() ->get_frame_time())
Records that the indicated button has been released.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
get_close_request_event
Returns the name of the event set via set_close_request_event().
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
static ButtonHandle ascii_key(char ascii_equivalent)
Returns the ButtonHandle associated with the particular ASCII character, if there is one,...
Similar to MutexHolder, but for a light mutex.
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
void release() const
Releases the lightReMutex.
Similar to MutexHolder, but for a light reentrant mutex.
static ButtonHandle button(int button_number)
Returns the ButtonHandle associated with the particular numbered mouse button (zero-based),...
Definition: mouseButton.cxx:32
static ButtonHandle wheel_left()
Returns the ButtonHandle generated when the mouse is scrolled to the left.
static ButtonHandle wheel_up()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch upwards.
Definition: mouseButton.cxx:84
static ButtonHandle wheel_right()
Returns the ButtonHandle generated when the mouse is scrolled to the right.
static ButtonHandle wheel_down()
Returns the ButtonHandle generated when the mouse wheel is rolled one notch downwards.
Definition: mouseButton.cxx:93
This class maintains the set of all known PNMFileTypes in the universe.
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
int get_x_size() const
Returns the number of pixels in the X direction.
int get_y_size() const
Returns the number of pixels in the Y direction.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
Definition: pnmImage.cxx:794
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:278
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1098
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1115
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
get_in_window
If this returns false, the pointer is not currently present in the window and the values returned by ...
Definition: pointerData.h:56
A thread; that is, a lightweight process.
Definition: thread.h:46
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
A hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This object represents a window on the desktop, not necessarily a Panda window.
Definition: windowHandle.h:34
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
Definition: windowHandle.h:44
A container for the various kinds of properties we might ask to have on a graphics window before we o...
clear_cursor_filename
Removes the cursor_filename specification from the properties.
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
get_title
Returns the window's title.
get_undecorated
Returns true if the window has no border.
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
clear_z_order
Removes the z_order specification from the properties.
get_minimized
Returns true if the window is minimized.
clear_mouse_mode
Removes the mouse_mode specification from the properties.
has_fullscreen
Returns true if set_fullscreen() has been specified.
has_size
Returns true if the window size has been specified, false otherwise.
clear_title
Removes the title specification from the properties.
get_size
Returns size in pixels of the useful part of the window, not including decorations.
clear_origin
Removes the origin specification from the properties.
has_minimized
Returns true if set_minimized() has been specified.
set_size
Specifies the requested size of the window, in pixels.
get_foreground
Returns true if the window is in the foreground.
has_fixed_size
Returns true if set_fixed_size() has been specified.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
clear_fixed_size
Removes the fixed_size specification from the properties.
clear_fullscreen
Removes the fullscreen specification from the properties.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
clear_size
Removes the size specification from the properties.
set_open
Specifies whether the window should be open.
get_z_order
Returns the window's z_order.
get_fullscreen
Returns true if the window is in fullscreen mode.
has_title
Returns true if the window title has been specified, false otherwise.
get_mouse_mode
See set_mouse_mode().
has_origin
Returns true if the window origin has been specified, false otherwise.
has_foreground
Returns true if set_foreground() has been specified.
clear_foreground
Removes the foreground specification from the properties.
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
get_cursor_hidden
Returns true if the mouse cursor is invisible.
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
has_z_order
Returns true if the window z_order has been specified, false otherwise.
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This graphics pipe represents the interface for creating graphics windows on an X-based client.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
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.
int get_screen() const
Returns the X screen number associated with the pipe.
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
bool supports_relative_mouse() const
Returns true if relative mouse mode is supported on this display.
bool enable_relative_mouse()
Enables relative mouse mode for this display.
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
void disable_relative_mouse()
Disables relative mouse mode for this display.
Interfaces to the X11 window system.
virtual bool begin_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread before beginning rendering for a given frame.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
virtual void end_frame(FrameMode mode, Thread *current_thread)
This function will be called within the draw thread after rendering is completed for a given frame.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
Definition: mouseData.h:23
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.