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