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
1193 // The size hints request a window of a particular size andor a particular
1194 // placement onscreen.
1195 XSizeHints *size_hints_p = nullptr;
1196 if (properties.has_origin() || properties.has_size()) {
1197 size_hints_p = XAllocSizeHints();
1198 if (size_hints_p != nullptr) {
1199 if (properties.has_origin()) {
1200 size_hints_p->x = properties.get_x_origin();
1201 size_hints_p->y = properties.get_y_origin();
1202 size_hints_p->flags |= USPosition;
1203 }
1204 LVecBase2i size = _properties.get_size();
1205 if (properties.has_size()) {
1206 size = properties.get_size();
1207 size_hints_p->width = size.get_x();
1208 size_hints_p->height = size.get_y();
1209 size_hints_p->flags |= USSize;
1210 }
1211 if (properties.get_fixed_size()) {
1212 size_hints_p->min_width = size.get_x();
1213 size_hints_p->min_height = size.get_y();
1214 size_hints_p->max_width = size.get_x();
1215 size_hints_p->max_height = size.get_y();
1216 size_hints_p->flags |= (PMinSize | PMaxSize);
1217 }
1218 }
1219 }
1220
1221 // The window manager hints include requests to the window manager other
1222 // than those specific to window geometry.
1223 XWMHints *wm_hints_p = nullptr;
1224 wm_hints_p = XAllocWMHints();
1225 if (wm_hints_p != nullptr) {
1226 if (properties.has_minimized() && properties.get_minimized()) {
1227 wm_hints_p->initial_state = IconicState;
1228 } else {
1229 wm_hints_p->initial_state = NormalState;
1230 }
1231 wm_hints_p->flags = StateHint;
1232 }
1233
1234 // Two competing window manager interfaces have evolved. One of them allows
1235 // to set certain properties as a "type"; the other one as a "state". We'll
1236 // try to honor both.
1237 static const int max_type_data = 32;
1238 int32_t type_data[max_type_data];
1239 int next_type_data = 0;
1240
1241 static const int max_state_data = 32;
1242 int32_t state_data[max_state_data];
1243 int next_state_data = 0;
1244
1245 static const int max_set_data = 32;
1246 class SetAction {
1247 public:
1248 inline SetAction() { }
1249 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1250 Atom _state;
1251 Atom _action;
1252 };
1253 SetAction set_data[max_set_data];
1254 int next_set_data = 0;
1255
1256 if (properties.has_fullscreen()) {
1257 if (properties.get_fullscreen()) {
1258 // For a "fullscreen" request, we pass this through, hoping the window
1259 // manager will support EWMH.
1260 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1261
1262 // We also request it as a state.
1263 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1264 // Don't ask me why this has to be 10 and not _net_wm_state_add. It
1265 // doesn't seem to work otherwise.
1266 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1267
1268 } else {
1269 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1270 }
1271 }
1272
1273 // If we asked for a window without a border, there's no excellent way to
1274 // arrange that. For users whose window managers follow the EWMH
1275 // specification, we can ask for a "splash" screen, which is usually
1276 // undecorated. It's not exactly right, but the spec doesn't give us an
1277 // exactly-right option.
1278
1279 // For other users, we'll totally punt and just set the window's Class to
1280 // "Undecorated", and let the user configure hisher window manager not to
1281 // put a border around windows of this class.
1282 XClassHint *class_hints_p = nullptr;
1283 if (!x_wm_class.empty()) {
1284 // Unless the user wanted to use his own WM_CLASS, of course.
1285 class_hints_p = XAllocClassHint();
1286 class_hints_p->res_class = (char*) x_wm_class.c_str();
1287 if (!x_wm_class_name.empty()) {
1288 class_hints_p->res_name = (char*) x_wm_class_name.c_str();
1289 }
1290
1291 } else if (properties.get_undecorated() || properties.get_fullscreen()) {
1292 class_hints_p = XAllocClassHint();
1293 class_hints_p->res_class = (char*) "Undecorated";
1294 }
1295
1296 if (properties.get_undecorated() && !properties.get_fullscreen()) {
1297 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1298 }
1299
1300 if (properties.has_z_order()) {
1301 switch (properties.get_z_order()) {
1302 case WindowProperties::Z_bottom:
1303 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1304 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1305 x11_pipe->_net_wm_state_add);
1306 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1307 x11_pipe->_net_wm_state_remove);
1308 break;
1309
1310 case WindowProperties::Z_normal:
1311 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1312 x11_pipe->_net_wm_state_remove);
1313 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1314 x11_pipe->_net_wm_state_remove);
1315 break;
1316
1317 case WindowProperties::Z_top:
1318 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1319 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1320 x11_pipe->_net_wm_state_remove);
1321 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1322 x11_pipe->_net_wm_state_add);
1323 break;
1324 }
1325 }
1326
1327 nassertv(next_type_data < max_type_data);
1328 nassertv(next_state_data < max_state_data);
1329 nassertv(next_set_data < max_set_data);
1330
1331 // Add the process ID as a convenience for other applications.
1332 int32_t pid = getpid();
1333 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1334 XA_CARDINAL, 32, PropModeReplace,
1335 (unsigned char *)&pid, 1);
1336
1337 // Disable compositing effects in fullscreen mode.
1338 if (properties.has_fullscreen()) {
1339 int32_t compositor = properties.get_fullscreen() ? 1 : 0;
1340 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1341 XA_CARDINAL, 32, PropModeReplace,
1342 (unsigned char *)&compositor, 1);
1343 }
1344
1345 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1346 XA_ATOM, 32, PropModeReplace,
1347 (unsigned char *)type_data, next_type_data);
1348
1349 // Request the state properties all at once.
1350 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1351 XA_ATOM, 32, PropModeReplace,
1352 (unsigned char *)state_data, next_state_data);
1353
1354 if (already_mapped) {
1355 // We have to request state changes differently when the window has been
1356 // mapped. To do this, we need to send a client message to the root
1357 // window for each change.
1358
1359 x11GraphicsPipe *x11_pipe;
1360 DCAST_INTO_V(x11_pipe, _pipe);
1361
1362 for (int i = 0; i < next_set_data; ++i) {
1363 XClientMessageEvent event;
1364 memset(&event, 0, sizeof(event));
1365 event.type = ClientMessage;
1366 event.send_event = True;
1367 event.display = _display;
1368 event.window = _xwindow;
1369 event.message_type = x11_pipe->_net_wm_state;
1370 event.format = 32;
1371 event.data.l[0] = set_data[i]._action;
1372 event.data.l[1] = set_data[i]._state;
1373 event.data.l[2] = 0;
1374 event.data.l[3] = 1;
1375
1376 XSendEvent(_display, x11_pipe->get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1377 }
1378 }
1379
1380 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1381 nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1382
1383 if (size_hints_p != nullptr) {
1384 XFree(size_hints_p);
1385 }
1386 if (wm_hints_p != nullptr) {
1387 XFree(wm_hints_p);
1388 }
1389 if (class_hints_p != nullptr) {
1390 XFree(class_hints_p);
1391 }
1392
1393 // Also, indicate to the window manager that we'd like to get a chance to
1394 // close our windows cleanly, rather than being rudely disconnected from the
1395 // X server if the user requests a window close.
1396 Atom protocols[] = {
1397 _wm_delete_window,
1398 };
1399
1400 XSetWMProtocols(_display, _xwindow, protocols,
1401 sizeof(protocols) / sizeof(Atom));
1402}
1403
1404/**
1405 * Allocates a colormap appropriate to the visual and stores in in the
1406 * _colormap method.
1407 */
1408void x11GraphicsWindow::
1409setup_colormap(XVisualInfo *visual) {
1410 x11GraphicsPipe *x11_pipe;
1411 DCAST_INTO_V(x11_pipe, _pipe);
1412 X11_Window root_window = x11_pipe->get_root();
1413
1414 _colormap = XCreateColormap(_display, root_window,
1415 visual->visual, AllocNone);
1416}
1417
1418/**
1419 * Adds raw mice to the _input_devices list.
1420 * @deprecated obtain raw devices via the device manager instead.
1421 */
1422void x11GraphicsWindow::
1423open_raw_mice() {
1424#ifdef PHAVE_LINUX_INPUT_H
1425 bool any_present = false;
1426 bool any_mice = false;
1427
1428 for (int i=0; i<64; i++) {
1429 ostringstream fnb;
1430 fnb << "/dev/input/event" << i;
1431 string fn = fnb.str();
1432 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1433 if (fd >= 0) {
1434 EvdevInputDevice *device = new EvdevInputDevice(nullptr, fd);
1435 nassertd(device != NULL) continue;
1436
1437 if (device->has_pointer()) {
1438 add_input_device(device);
1439
1440 x11display_cat.info()
1441 << "Raw mouse " << _input_devices.size()
1442 << " detected: " << device->get_name() << "\n";
1443
1444 any_mice = true;
1445 any_present = true;
1446 }
1447 } else {
1448 if (errno == ENOENT || errno == ENOTDIR) {
1449 break;
1450 } else {
1451 any_present = true;
1452 x11display_cat.error()
1453 << "Opening raw mice: " << strerror(errno) << " " << fn << "\n";
1454 }
1455 }
1456 }
1457
1458 if (any_mice) {
1459 _properties.set_raw_mice(true);
1460
1461 } else if (!any_present) {
1462 x11display_cat.error() <<
1463 "Opening raw mice: files not found: /dev/input/event*\n";
1464
1465 } else {
1466 x11display_cat.error() <<
1467 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1468 }
1469#else
1470 x11display_cat.error() <<
1471 "Opening raw mice: panda not compiled with raw mouse support.\n";
1472#endif
1473}
1474
1475/**
1476 * Generates a keystroke corresponding to the indicated X KeyPress event.
1477 */
1478void x11GraphicsWindow::
1479handle_keystroke(XKeyEvent &event) {
1480 if (!_dga_mouse_enabled) {
1481 _input->set_pointer_in_window(event.x, event.y);
1482 }
1483
1484 if (_ic) {
1485 // First, get the keystroke as a wide-character sequence.
1486 static const int buffer_size = 256;
1487 wchar_t buffer[buffer_size];
1488 Status status;
1489 int len = XwcLookupString(_ic, &event, buffer, buffer_size, nullptr,
1490 &status);
1491 if (status == XBufferOverflow) {
1492 x11display_cat.error()
1493 << "Overflowed input buffer.\n";
1494 }
1495
1496 // Now each of the returned wide characters represents a keystroke.
1497 for (int i = 0; i < len; i++) {
1498 _input->keystroke(buffer[i]);
1499 }
1500
1501 } else {
1502 // Without an input context, just get the ascii keypress.
1503 ButtonHandle button = get_button(event, true);
1504 if (button.has_ascii_equivalent()) {
1505 _input->keystroke(button.get_ascii_equivalent());
1506 }
1507 }
1508}
1509
1510/**
1511 * Generates a keypress corresponding to the indicated X KeyPress event.
1512 */
1513void x11GraphicsWindow::
1514handle_keypress(XKeyEvent &event) {
1515 if (!_dga_mouse_enabled) {
1516 _input->set_pointer_in_window(event.x, event.y);
1517 }
1518
1519 // Now get the raw unshifted button.
1520 ButtonHandle button = get_button(event, false);
1521 if (button != ButtonHandle::none()) {
1522 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1523 _input->button_down(KeyboardButton::control());
1524 }
1525 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1526 _input->button_down(KeyboardButton::shift());
1527 }
1528 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1529 _input->button_down(KeyboardButton::alt());
1530 }
1531 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1532 _input->button_down(KeyboardButton::meta());
1533 }
1534 _input->button_down(button);
1535 }
1536}
1537
1538/**
1539 * Generates a keyrelease corresponding to the indicated X KeyRelease event.
1540 */
1541void x11GraphicsWindow::
1542handle_keyrelease(XKeyEvent &event) {
1543 if (!_dga_mouse_enabled) {
1544 _input->set_pointer_in_window(event.x, event.y);
1545 }
1546
1547 // Now get the raw unshifted button.
1548 ButtonHandle button = get_button(event, false);
1549 if (button != ButtonHandle::none()) {
1550 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1551 _input->button_up(KeyboardButton::control());
1552 }
1553 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1554 _input->button_up(KeyboardButton::shift());
1555 }
1556 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1557 _input->button_up(KeyboardButton::alt());
1558 }
1559 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1560 _input->button_up(KeyboardButton::meta());
1561 }
1562 _input->button_up(button);
1563 }
1564}
1565
1566/**
1567 * Returns the Panda ButtonHandle corresponding to the keyboard button
1568 * indicated by the given key event.
1569 */
1570ButtonHandle x11GraphicsWindow::
1571get_button(XKeyEvent &key_event, bool allow_shift) {
1572 KeySym key = XLookupKeysym(&key_event, 0);
1573
1574 if ((key_event.state & Mod2Mask) != 0) {
1575 // Mod2Mask corresponds to NumLock being in effect. In this case, we want
1576 // to get the alternate keysym associated with any keypad keys. Weird
1577 // system.
1578 KeySym k2;
1579 ButtonHandle button;
1580 switch (key) {
1581 case XK_KP_Space:
1582 case XK_KP_Tab:
1583 case XK_KP_Enter:
1584 case XK_KP_F1:
1585 case XK_KP_F2:
1586 case XK_KP_F3:
1587 case XK_KP_F4:
1588 case XK_KP_Equal:
1589 case XK_KP_Multiply:
1590 case XK_KP_Add:
1591 case XK_KP_Separator:
1592 case XK_KP_Subtract:
1593 case XK_KP_Divide:
1594 case XK_KP_Left:
1595 case XK_KP_Up:
1596 case XK_KP_Right:
1597 case XK_KP_Down:
1598 case XK_KP_Begin:
1599 case XK_KP_Prior:
1600 case XK_KP_Next:
1601 case XK_KP_Home:
1602 case XK_KP_End:
1603 case XK_KP_Insert:
1604 case XK_KP_Delete:
1605 case XK_KP_0:
1606 case XK_KP_1:
1607 case XK_KP_2:
1608 case XK_KP_3:
1609 case XK_KP_4:
1610 case XK_KP_5:
1611 case XK_KP_6:
1612 case XK_KP_7:
1613 case XK_KP_8:
1614 case XK_KP_9:
1615 k2 = XLookupKeysym(&key_event, 1);
1616 button = map_button(k2);
1617 if (button != ButtonHandle::none()) {
1618 return button;
1619 }
1620 // If that didn't produce a button we know, just fall through and handle
1621 // the normal, un-numlocked key.
1622 break;
1623
1624 default:
1625 break;
1626 }
1627 }
1628
1629 if (allow_shift) {
1630 // If shift is held down, get the shifted keysym.
1631 if ((key_event.state & ShiftMask) != 0) {
1632 KeySym k2 = XLookupKeysym(&key_event, 1);
1633 ButtonHandle button = map_button(k2);
1634 if (button != ButtonHandle::none()) {
1635 return button;
1636 }
1637 }
1638
1639 // If caps lock is down, shift lowercase letters to uppercase. We can do
1640 // this in just the ASCII set, because we handle international keyboards
1641 // elsewhere (via an input context).
1642 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1643 if (key >= XK_a && key <= XK_z) {
1644 key += (XK_A - XK_a);
1645 }
1646 }
1647 }
1648
1649 return map_button(key);
1650}
1651
1652/**
1653 * Maps from a single X keysym to Panda's ButtonHandle. Called by
1654 * get_button(), above.
1655 */
1656ButtonHandle x11GraphicsWindow::
1657map_button(KeySym key) const {
1658 switch (key) {
1659 case NoSymbol:
1660 return ButtonHandle::none();
1661 case XK_BackSpace:
1662 return KeyboardButton::backspace();
1663 case XK_Tab:
1664 case XK_KP_Tab:
1665 return KeyboardButton::tab();
1666 case XK_Return:
1667 case XK_KP_Enter:
1668 return KeyboardButton::enter();
1669 case XK_Escape:
1670 return KeyboardButton::escape();
1671 case XK_KP_Space:
1672 case XK_space:
1673 return KeyboardButton::space();
1674 case XK_exclam:
1675 return KeyboardButton::ascii_key('!');
1676 case XK_quotedbl:
1677 return KeyboardButton::ascii_key('"');
1678 case XK_numbersign:
1679 return KeyboardButton::ascii_key('#');
1680 case XK_dollar:
1681 return KeyboardButton::ascii_key('$');
1682 case XK_percent:
1683 return KeyboardButton::ascii_key('%');
1684 case XK_ampersand:
1685 return KeyboardButton::ascii_key('&');
1686 case XK_apostrophe: // == XK_quoteright
1687 case XK_dead_acute: // on int'l keyboards
1688 return KeyboardButton::ascii_key('\'');
1689 case XK_parenleft:
1690 return KeyboardButton::ascii_key('(');
1691 case XK_parenright:
1692 return KeyboardButton::ascii_key(')');
1693 case XK_asterisk:
1694 case XK_KP_Multiply:
1695 return KeyboardButton::ascii_key('*');
1696 case XK_plus:
1697 case XK_KP_Add:
1698 return KeyboardButton::ascii_key('+');
1699 case XK_comma:
1700 case XK_KP_Separator:
1701 return KeyboardButton::ascii_key(',');
1702 case XK_minus:
1703 case XK_KP_Subtract:
1704 return KeyboardButton::ascii_key('-');
1705 case XK_period:
1706 case XK_KP_Decimal:
1707 return KeyboardButton::ascii_key('.');
1708 case XK_slash:
1709 case XK_KP_Divide:
1710 return KeyboardButton::ascii_key('/');
1711 case XK_0:
1712 case XK_KP_0:
1713 return KeyboardButton::ascii_key('0');
1714 case XK_1:
1715 case XK_KP_1:
1716 return KeyboardButton::ascii_key('1');
1717 case XK_2:
1718 case XK_KP_2:
1719 return KeyboardButton::ascii_key('2');
1720 case XK_3:
1721 case XK_KP_3:
1722 return KeyboardButton::ascii_key('3');
1723 case XK_4:
1724 case XK_KP_4:
1725 return KeyboardButton::ascii_key('4');
1726 case XK_5:
1727 case XK_KP_5:
1728 return KeyboardButton::ascii_key('5');
1729 case XK_6:
1730 case XK_KP_6:
1731 return KeyboardButton::ascii_key('6');
1732 case XK_7:
1733 case XK_KP_7:
1734 return KeyboardButton::ascii_key('7');
1735 case XK_8:
1736 case XK_KP_8:
1737 return KeyboardButton::ascii_key('8');
1738 case XK_9:
1739 case XK_KP_9:
1740 return KeyboardButton::ascii_key('9');
1741 case XK_colon:
1742 return KeyboardButton::ascii_key(':');
1743 case XK_semicolon:
1744 return KeyboardButton::ascii_key(';');
1745 case XK_less:
1746 return KeyboardButton::ascii_key('<');
1747 case XK_equal:
1748 case XK_KP_Equal:
1749 return KeyboardButton::ascii_key('=');
1750 case XK_greater:
1751 return KeyboardButton::ascii_key('>');
1752 case XK_question:
1753 return KeyboardButton::ascii_key('?');
1754 case XK_at:
1755 return KeyboardButton::ascii_key('@');
1756 case XK_A:
1757 return KeyboardButton::ascii_key('A');
1758 case XK_B:
1759 return KeyboardButton::ascii_key('B');
1760 case XK_C:
1761 return KeyboardButton::ascii_key('C');
1762 case XK_D:
1763 return KeyboardButton::ascii_key('D');
1764 case XK_E:
1765 return KeyboardButton::ascii_key('E');
1766 case XK_F:
1767 return KeyboardButton::ascii_key('F');
1768 case XK_G:
1769 return KeyboardButton::ascii_key('G');
1770 case XK_H:
1771 return KeyboardButton::ascii_key('H');
1772 case XK_I:
1773 return KeyboardButton::ascii_key('I');
1774 case XK_J:
1775 return KeyboardButton::ascii_key('J');
1776 case XK_K:
1777 return KeyboardButton::ascii_key('K');
1778 case XK_L:
1779 return KeyboardButton::ascii_key('L');
1780 case XK_M:
1781 return KeyboardButton::ascii_key('M');
1782 case XK_N:
1783 return KeyboardButton::ascii_key('N');
1784 case XK_O:
1785 return KeyboardButton::ascii_key('O');
1786 case XK_P:
1787 return KeyboardButton::ascii_key('P');
1788 case XK_Q:
1789 return KeyboardButton::ascii_key('Q');
1790 case XK_R:
1791 return KeyboardButton::ascii_key('R');
1792 case XK_S:
1793 return KeyboardButton::ascii_key('S');
1794 case XK_T:
1795 return KeyboardButton::ascii_key('T');
1796 case XK_U:
1797 return KeyboardButton::ascii_key('U');
1798 case XK_V:
1799 return KeyboardButton::ascii_key('V');
1800 case XK_W:
1801 return KeyboardButton::ascii_key('W');
1802 case XK_X:
1803 return KeyboardButton::ascii_key('X');
1804 case XK_Y:
1805 return KeyboardButton::ascii_key('Y');
1806 case XK_Z:
1807 return KeyboardButton::ascii_key('Z');
1808 case XK_bracketleft:
1809 return KeyboardButton::ascii_key('[');
1810 case XK_backslash:
1811 return KeyboardButton::ascii_key('\\');
1812 case XK_bracketright:
1813 return KeyboardButton::ascii_key(']');
1814 case XK_asciicircum:
1815 return KeyboardButton::ascii_key('^');
1816 case XK_underscore:
1817 return KeyboardButton::ascii_key('_');
1818 case XK_grave: // == XK_quoteleft
1819 case XK_dead_grave: // on int'l keyboards
1820 return KeyboardButton::ascii_key('`');
1821 case XK_a:
1822 return KeyboardButton::ascii_key('a');
1823 case XK_b:
1824 return KeyboardButton::ascii_key('b');
1825 case XK_c:
1826 return KeyboardButton::ascii_key('c');
1827 case XK_d:
1828 return KeyboardButton::ascii_key('d');
1829 case XK_e:
1830 return KeyboardButton::ascii_key('e');
1831 case XK_f:
1832 return KeyboardButton::ascii_key('f');
1833 case XK_g:
1834 return KeyboardButton::ascii_key('g');
1835 case XK_h:
1836 return KeyboardButton::ascii_key('h');
1837 case XK_i:
1838 return KeyboardButton::ascii_key('i');
1839 case XK_j:
1840 return KeyboardButton::ascii_key('j');
1841 case XK_k:
1842 return KeyboardButton::ascii_key('k');
1843 case XK_l:
1844 return KeyboardButton::ascii_key('l');
1845 case XK_m:
1846 return KeyboardButton::ascii_key('m');
1847 case XK_n:
1848 return KeyboardButton::ascii_key('n');
1849 case XK_o:
1850 return KeyboardButton::ascii_key('o');
1851 case XK_p:
1852 return KeyboardButton::ascii_key('p');
1853 case XK_q:
1854 return KeyboardButton::ascii_key('q');
1855 case XK_r:
1856 return KeyboardButton::ascii_key('r');
1857 case XK_s:
1858 return KeyboardButton::ascii_key('s');
1859 case XK_t:
1860 return KeyboardButton::ascii_key('t');
1861 case XK_u:
1862 return KeyboardButton::ascii_key('u');
1863 case XK_v:
1864 return KeyboardButton::ascii_key('v');
1865 case XK_w:
1866 return KeyboardButton::ascii_key('w');
1867 case XK_x:
1868 return KeyboardButton::ascii_key('x');
1869 case XK_y:
1870 return KeyboardButton::ascii_key('y');
1871 case XK_z:
1872 return KeyboardButton::ascii_key('z');
1873 case XK_braceleft:
1874 return KeyboardButton::ascii_key('{');
1875 case XK_bar:
1876 return KeyboardButton::ascii_key('|');
1877 case XK_braceright:
1878 return KeyboardButton::ascii_key('}');
1879 case XK_asciitilde:
1880 return KeyboardButton::ascii_key('~');
1881 case XK_F1:
1882 case XK_KP_F1:
1883 return KeyboardButton::f1();
1884 case XK_F2:
1885 case XK_KP_F2:
1886 return KeyboardButton::f2();
1887 case XK_F3:
1888 case XK_KP_F3:
1889 return KeyboardButton::f3();
1890 case XK_F4:
1891 case XK_KP_F4:
1892 return KeyboardButton::f4();
1893 case XK_F5:
1894 return KeyboardButton::f5();
1895 case XK_F6:
1896 return KeyboardButton::f6();
1897 case XK_F7:
1898 return KeyboardButton::f7();
1899 case XK_F8:
1900 return KeyboardButton::f8();
1901 case XK_F9:
1902 return KeyboardButton::f9();
1903 case XK_F10:
1904 return KeyboardButton::f10();
1905 case XK_F11:
1906 return KeyboardButton::f11();
1907 case XK_F12:
1908 return KeyboardButton::f12();
1909 case XK_KP_Left:
1910 case XK_Left:
1911 return KeyboardButton::left();
1912 case XK_KP_Up:
1913 case XK_Up:
1914 return KeyboardButton::up();
1915 case XK_KP_Right:
1916 case XK_Right:
1917 return KeyboardButton::right();
1918 case XK_KP_Down:
1919 case XK_Down:
1920 return KeyboardButton::down();
1921 case XK_KP_Prior:
1922 case XK_Prior:
1923 return KeyboardButton::page_up();
1924 case XK_KP_Next:
1925 case XK_Next:
1926 return KeyboardButton::page_down();
1927 case XK_KP_Home:
1928 case XK_Home:
1929 return KeyboardButton::home();
1930 case XK_KP_End:
1931 case XK_End:
1932 return KeyboardButton::end();
1933 case XK_KP_Insert:
1934 case XK_Insert:
1935 return KeyboardButton::insert();
1936 case XK_KP_Delete:
1937 case XK_Delete:
1938 return KeyboardButton::del();
1939 case XK_Num_Lock:
1940 return KeyboardButton::num_lock();
1941 case XK_Scroll_Lock:
1942 return KeyboardButton::scroll_lock();
1943 case XK_Print:
1944 return KeyboardButton::print_screen();
1945 case XK_Pause:
1946 return KeyboardButton::pause();
1947 case XK_Menu:
1948 return KeyboardButton::menu();
1949 case XK_Shift_L:
1950 return KeyboardButton::lshift();
1951 case XK_Shift_R:
1952 return KeyboardButton::rshift();
1953 case XK_Control_L:
1954 return KeyboardButton::lcontrol();
1955 case XK_Control_R:
1956 return KeyboardButton::rcontrol();
1957 case XK_Alt_L:
1958 return KeyboardButton::lalt();
1959 case XK_Alt_R:
1960 return KeyboardButton::ralt();
1961 case XK_Meta_L:
1962 case XK_Super_L:
1963 return KeyboardButton::lmeta();
1964 case XK_Meta_R:
1965 case XK_Super_R:
1966 return KeyboardButton::rmeta();
1967 case XK_Caps_Lock:
1968 return KeyboardButton::caps_lock();
1969 case XK_Shift_Lock:
1970 return KeyboardButton::shift_lock();
1971 }
1972 if (x11display_cat.is_debug()) {
1973 x11display_cat.debug()
1974 << "Unrecognized keysym 0x" << std::hex << key << std::dec << "\n";
1975 }
1976 return ButtonHandle::none();
1977}
1978
1979/**
1980 * Maps from a single X keycode to Panda's ButtonHandle.
1981 */
1982ButtonHandle x11GraphicsWindow::
1983map_raw_button(KeyCode key) const {
1984#ifdef PHAVE_LINUX_INPUT_H
1985 // Most X11 servers are configured to use the evdev driver, which
1986 // adds 8 to the underlying evdev keycodes (not sure why).
1987 // In any case, this means we can use the same mapping as our raw
1988 // input code, which uses evdev directly.
1989 int index = key - 8;
1990 if (index > 0 && index < 128) {
1991 return EvdevInputDevice::map_button(index);
1992 }
1993#endif
1994 return ButtonHandle::none();
1995}
1996
1997/**
1998 * Returns the Panda ButtonHandle corresponding to the mouse button indicated
1999 * by the given button event.
2000 */
2001ButtonHandle x11GraphicsWindow::
2002get_mouse_button(XButtonEvent &button_event) {
2003 int index = button_event.button;
2004 if (index == x_wheel_up_button) {
2005 return MouseButton::wheel_up();
2006 } else if (index == x_wheel_down_button) {
2007 return MouseButton::wheel_down();
2008 } else if (index == x_wheel_left_button) {
2009 return MouseButton::wheel_left();
2010 } else if (index == x_wheel_right_button) {
2011 return MouseButton::wheel_right();
2012 } else if (index >= 8) {
2013 return MouseButton::button(index - 5);
2014 } else {
2015 return MouseButton::button(index - 1);
2016 }
2017}
2018
2019/**
2020 * Returns a ButtonMap containing the association between raw buttons and
2021 * virtual buttons.
2022 */
2023ButtonMap *x11GraphicsWindow::
2024get_keyboard_map() const {
2025 // NB. This could be improved by using the Xkb API. XkbDescPtr desc =
2026 // XkbGetMap(_display, XkbAllMapComponentsMask, XkbUseCoreKbd);
2027 ButtonMap *map = new ButtonMap;
2028
2029 LightReMutexHolder holder(x11GraphicsPipe::_x_mutex);
2030
2031 for (int k = 9; k <= 135; ++k) {
2032 if (k >= 78 && k <= 91) {
2033 // Ignore numpad keys for now. These are not mapped to separate button
2034 // handles in Panda, so we don't want their mappings to conflict with
2035 // the regular numeric keys.
2036 continue;
2037 }
2038
2039 ButtonHandle raw_button = map_raw_button(k);
2040 if (raw_button == ButtonHandle::none()) {
2041 continue;
2042 }
2043
2044 KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2045 ButtonHandle button = map_button(sym);
2046 std::string label;
2047
2048 // Compose a label for some keys; I have not yet been able to find an API
2049 // that does this effectively.
2050 if (sym >= XK_exclam && sym <= XK_asciitilde) {
2051 label = toupper((char)sym);
2052 }
2053 else if (sym >= XK_F1 && sym <= XK_F35) {
2054 label = "F" + format_string(sym - XK_F1 + 1);
2055 }
2056 else if (sym > 0x1000000 && sym < 0x1110000) {
2057 // Unicode code point. Encode as UTF-8.
2058 char32_t ch = sym & 0x0ffffff;
2059 if ((ch & ~0x7f) == 0) {
2060 label = string(1, (char)ch);
2061 }
2062 else if ((ch & ~0x7ff) == 0) {
2063 label =
2064 string(1, (char)((ch >> 6) | 0xc0)) +
2065 string(1, (char)((ch & 0x3f) | 0x80));
2066 }
2067 else if ((ch & ~0xffff) == 0) {
2068 label =
2069 string(1, (char)((ch >> 12) | 0xe0)) +
2070 string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2071 string(1, (char)((ch & 0x3f) | 0x80));
2072 }
2073 else {
2074 label =
2075 string(1, (char)((ch >> 18) | 0xf0)) +
2076 string(1, (char)(((ch >> 12) & 0x3f) | 0x80)) +
2077 string(1, (char)(((ch >> 6) & 0x3f) | 0x80)) +
2078 string(1, (char)((ch & 0x3f) | 0x80));
2079 }
2080 }
2081 else if ((sym >= XK_exclamdown && sym <= XK_umacron)
2082 || (sym >= XK_OE && sym <= XK_Ydiaeresis)
2083 || (sym >= XK_Serbian_dje && sym <= XK_Cyrillic_HARDSIGN)
2084 || (sym >= XK_kana_fullstop && sym <= XK_semivoicedsound)
2085 || (sym >= XK_Arabic_comma && sym <= XK_Arabic_sukun)
2086 || (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_omega)
2087 || (sym >= XK_hebrew_doublelowline && sym <= XK_hebrew_taw)
2088 || (sym >= XK_Thai_kokai && sym <= XK_Thai_lekkao)
2089 || (sym >= XK_Hangul_Kiyeog && sym <= XK_Hangul_J_YeorinHieuh)
2090 || sym == XK_EuroSign
2091 || sym == XK_Korean_Won) {
2092 // A non-unicode-based keysym. Translate this to the label.
2093 char buffer[255];
2094 int nbytes = XkbTranslateKeySym(_display, &sym, 0, buffer, 255, 0);
2095 if (nbytes > 0) {
2096 label.assign(buffer, nbytes);
2097 }
2098 }
2099
2100 if (button == ButtonHandle::none() && label.empty()) {
2101 // No label and no mapping; this is useless.
2102 continue;
2103 }
2104
2105 map->map_button(raw_button, button, label);
2106 }
2107
2108 return map;
2109}
2110
2111/**
2112 * This function is used as a predicate to XCheckIfEvent() to determine if the
2113 * indicated queued X event is relevant and should be returned to this window.
2114 */
2115Bool x11GraphicsWindow::
2116check_event(X11_Display *display, XEvent *event, char *arg) {
2117 const x11GraphicsWindow *self = (x11GraphicsWindow *)arg;
2118
2119 // We accept any event that is sent to our window.
2120 return (event->xany.window == self->_xwindow);
2121}
2122
2123/**
2124 * Loads and returns a Cursor corresponding to the indicated filename. If the
2125 * file cannot be loaded, returns None.
2126 */
2127X11_Cursor x11GraphicsWindow::
2128get_cursor(const Filename &filename) {
2129 x11GraphicsPipe *x11_pipe;
2130 DCAST_INTO_R(x11_pipe, _pipe, None);
2131
2132 if (x11_pipe->_xcursor_size == -1) {
2133 x11display_cat.info()
2134 << "libXcursor.so.1 not available; cannot change mouse cursor.\n";
2135 return None;
2136 }
2137
2138 // First, look for the unresolved filename in our index.
2139 pmap<Filename, X11_Cursor>::iterator fi = _cursor_filenames.find(filename);
2140 if (fi != _cursor_filenames.end()) {
2141 return fi->second;
2142 }
2143
2144 // If it wasn't found, resolve the filename and search for that.
2146 Filename resolved (filename);
2147 if (!vfs->resolve_filename(resolved, get_model_path())) {
2148 // The filename doesn't exist.
2149 x11display_cat.warning()
2150 << "Could not find cursor filename " << filename << "\n";
2151 return None;
2152 }
2153 fi = _cursor_filenames.find(resolved);
2154 if (fi != _cursor_filenames.end()) {
2155 return fi->second;
2156 }
2157
2158 // Open the file through the virtual file system.
2159 istream *str = vfs->open_read_file(resolved, true);
2160 if (str == nullptr) {
2161 x11display_cat.warning()
2162 << "Could not open cursor file " << filename << "\n";
2163 return None;
2164 }
2165
2166 // Check the first four bytes to see what kind of file it is.
2167 char magic[4];
2168 str->read(magic, 4);
2169 if (!str->good()) {
2170 x11display_cat.warning()
2171 << "Could not read from cursor file " << filename << "\n";
2172 return None;
2173 }
2174
2175 // Put back the read bytes. Do not use seekg, because this will
2176 // corrupt the stream if it points to encrypted/compressed file
2177 str->putback(magic[3]);
2178 str->putback(magic[2]);
2179 str->putback(magic[1]);
2180 str->putback(magic[0]);
2181
2182 X11_Cursor h = None;
2183 if (memcmp(magic, "Xcur", 4) == 0) {
2184 // X11 cursor.
2185 x11display_cat.debug()
2186 << "Loading X11 cursor " << filename << "\n";
2187 XcursorFile xcfile;
2188 xcfile.closure = str;
2189 xcfile.read = &xcursor_read;
2190 xcfile.write = &xcursor_write;
2191 xcfile.seek = &xcursor_seek;
2192
2193 XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2194 if (images != nullptr) {
2195 h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2196 x11_pipe->_XcursorImagesDestroy(images);
2197 }
2198
2199 } else if (memcmp(magic, "\0\0\1\0", 4) == 0
2200 || memcmp(magic, "\0\0\2\0", 4) == 0) {
2201 // Windows .ico or .cur file.
2202 x11display_cat.debug()
2203 << "Loading Windows cursor " << filename << "\n";
2204 h = read_ico(*str);
2205 }
2206
2207 // Delete the istream.
2208 vfs->close_read_file(str);
2209
2210 if (h == None) {
2211 x11display_cat.warning()
2212 << "X11 cursor filename '" << resolved << "' could not be loaded!\n";
2213 }
2214
2215 _cursor_filenames[resolved] = h;
2216 return h;
2217}
2218
2219/**
2220 * Reads a Windows .ico or .cur file from the indicated stream and returns it
2221 * as an X11 Cursor. If the file cannot be loaded, returns None.
2222 */
2223X11_Cursor x11GraphicsWindow::
2224read_ico(istream &ico) {
2225 x11GraphicsPipe *x11_pipe;
2226 DCAST_INTO_R(x11_pipe, _pipe, None);
2227
2228 // Local structs, this is just POD, make input easier
2229 typedef struct {
2230 uint16_t reserved, type, count;
2231 } IcoHeader;
2232
2233 typedef struct {
2234 uint8_t width, height, colorCount, reserved;
2235 uint16_t xhot, yhot;
2236 uint32_t bitmapSize, offset;
2237 } IcoEntry;
2238
2239 typedef struct {
2240 uint32_t headerSize, width, height;
2241 uint16_t planes, bitsPerPixel;
2242 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2243 } IcoInfoHeader;
2244
2245 typedef struct {
2246 uint8_t blue, green, red, reserved;
2247 } IcoColor;
2248
2249 int i, entry = 0;
2250 unsigned int j, k, mask, shift;
2251 size_t colorCount, bitsPerPixel;
2252 IcoHeader header;
2253 IcoInfoHeader infoHeader;
2254 IcoEntry *entries = nullptr;
2255 IcoColor color, *palette = nullptr;
2256
2257 size_t xorBmpSize, andBmpSize;
2258 char *curXor, *curAnd;
2259 char *xorBmp = nullptr, *andBmp = nullptr;
2260 XcursorImage *image = nullptr;
2261 X11_Cursor ret = None;
2262
2263 int def_size = x11_pipe->_xcursor_size;
2264
2265 // Get our header, note that ICO = type 1 and CUR = type 2.
2266 ico.read(reinterpret_cast<char *>(&header), sizeof(IcoHeader));
2267 if (!ico.good()) goto cleanup;
2268 if (header.type != 1 && header.type != 2) goto cleanup;
2269 if (header.count < 1) goto cleanup;
2270
2271 // Read the entry table into memory, select the largest entry.
2272 entries = new IcoEntry[header.count];
2273 ico.read(reinterpret_cast<char *>(entries), header.count * sizeof(IcoEntry));
2274 if (!ico.good()) goto cleanup;
2275 for (i = 1; i < header.count; i++) {
2276 if (entries[i].width == def_size && entries[i].height == def_size) {
2277 // Wait, this is the default cursor size. This is perfect.
2278 entry = i;
2279 break;
2280 }
2281 if (entries[i].width > entries[entry].width ||
2282 entries[i].height > entries[entry].height)
2283 entry = i;
2284 }
2285
2286 // Seek to the image in the ICO.
2287 ico.seekg(entries[entry].offset);
2288 if (!ico.good()) goto cleanup;
2289
2290 if (ico.peek() == 0x89) {
2291 // Hang on, this is actually a PNG header.
2292 PNMImage img;
2294 if (!img.read(ico, "", reg->get_type_from_extension("png"))) {
2295 goto cleanup;
2296 }
2297 img.set_maxval(255);
2298
2299 image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size());
2300
2301 xel *ptr = img.get_array();
2302 xelval *alpha = img.get_alpha_array();
2303 size_t num_pixels = (size_t)img.get_x_size() * (size_t)img.get_y_size();
2304 unsigned int *dest = image->pixels;
2305
2306 if (alpha != nullptr) {
2307 for (size_t p = 0; p < num_pixels; ++p) {
2308 *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2309 ++ptr;
2310 ++alpha;
2311 }
2312 } else {
2313 for (size_t p = 0; p < num_pixels; ++p) {
2314 *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2315 ++ptr;
2316 }
2317 }
2318
2319 } else {
2320 ico.read(reinterpret_cast<char *>(&infoHeader), sizeof(IcoInfoHeader));
2321 if (!ico.good()) goto cleanup;
2322 bitsPerPixel = infoHeader.bitsPerPixel;
2323
2324 if (infoHeader.compression != 0) goto cleanup;
2325
2326 // Load the color palette, if one exists.
2327 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2328 colorCount = 1 << bitsPerPixel;
2329 palette = new IcoColor[colorCount];
2330 ico.read(reinterpret_cast<char *>(palette), colorCount * sizeof(IcoColor));
2331 if (!ico.good()) goto cleanup;
2332 }
2333
2334 int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2335
2336 // Read in the pixel data.
2337 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2338 andBmpSize = and_stride * (infoHeader.height / 2);
2339 curXor = xorBmp = new char[xorBmpSize];
2340 curAnd = andBmp = new char[andBmpSize];
2341 ico.read(xorBmp, xorBmpSize);
2342 if (!ico.good()) goto cleanup;
2343 ico.read(andBmp, andBmpSize);
2344 if (!ico.good()) goto cleanup;
2345
2346 image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2347
2348 // Support all the formats that GIMP supports.
2349 switch (bitsPerPixel) {
2350 case 1:
2351 case 4:
2352 case 8:
2353 // For colors less that a byte wide, shift and mask the palette indices
2354 // off each element of the xorBmp and append them to the image.
2355 mask = ((1 << bitsPerPixel) - 1);
2356 for (i = image->height - 1; i >= 0; i--) {
2357 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2358 for (k = 0; k < 8 / bitsPerPixel; k++) {
2359 shift = 8 - ((k + 1) * bitsPerPixel);
2360 color = palette[(*curXor & (mask << shift)) >> shift];
2361 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2362 (color.green << 8) +
2363 (color.blue);
2364 }
2365
2366 curXor++;
2367 }
2368
2369 // Set the alpha byte properly according to the andBmp.
2370 for (j = 0; j < image->width; j += 8) {
2371 for (k = 0; k < 8; k++) {
2372 shift = 7 - k;
2373 image->pixels[(i * image->width) + j + k] |=
2374 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2375 }
2376
2377 curAnd++;
2378 }
2379 }
2380 break;
2381
2382 case 24:
2383 // Pack each of the three bytes into a single color, BGR -> 0RGB
2384 for (i = image->height - 1; i >= 0; i--) {
2385 for (j = 0; j < image->width; j++) {
2386 shift = 7 - (j & 0x7);
2387 uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2388 image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2389 | ((uint8_t)curXor[1] << 8u)
2390 | ((uint8_t)curXor[2] << 16u)
2391 | alpha;
2392 curXor += 3;
2393 }
2394 curAnd += and_stride;
2395 }
2396 break;
2397
2398 case 32:
2399 // Pack each of the four bytes into a single color, BGRA -> ARGB
2400 for (i = image->height - 1; i >= 0; i--) {
2401 for (j = 0; j < image->width; j++) {
2402 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2403 (*(curXor + 2) << 16) +
2404 (*(curXor + 1) << 8) +
2405 (*curXor);
2406 curXor += 4;
2407 }
2408 }
2409 break;
2410
2411 default:
2412 goto cleanup;
2413 }
2414 }
2415
2416 // If this is an actual CUR not an ICO set up the hotspot properly.
2417 if (header.type == 2) {
2418 image->xhot = entries[entry].xhot;
2419 image->yhot = entries[entry].yhot;
2420 } else {
2421 image->xhot = 0;
2422 image->yhot = 0;
2423 }
2424
2425 ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2426
2427cleanup:
2428 x11_pipe->_XcursorImageDestroy(image);
2429 delete[] entries;
2430 delete[] palette;
2431 delete[] xorBmp;
2432 delete[] andBmp;
2433
2434 return ret;
2435}
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.