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