15 #include "x11GraphicsWindow.h"
16 #include "config_x11display.h"
17 #include "x11GraphicsPipe.h"
19 #include "graphicsPipe.h"
20 #include "keyboardButton.h"
21 #include "mouseButton.h"
22 #include "buttonMap.h"
23 #include "clockObject.h"
24 #include "pStatTimer.h"
25 #include "textEncoder.h"
26 #include "throw_event.h"
27 #include "lightReMutexHolder.h"
28 #include "nativeWindowHandle.h"
29 #include "virtualFileSystem.h"
36 #ifdef PHAVE_LINUX_INPUT_H
37 #include <linux/input.h>
41 static int xcursor_read(XcursorFile *file,
unsigned char *buf,
int len) {
42 istream* str = (istream*) file->closure;
43 str->read((
char*) buf, len);
47 static int xcursor_write(XcursorFile *file,
unsigned char *buf,
int len) {
49 nassertr_always(
false, 0);
53 static int xcursor_seek(XcursorFile *file,
long offset,
int whence) {
54 istream* str = (istream*) file->closure;
57 str->seekg(offset, istream::beg);
60 str->seekg(offset, istream::cur);
63 str->seekg(offset, istream::end);
72 #define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)&7)))
87 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
90 DCAST_INTO_V(x11_pipe, _pipe);
93 _xwindow = (X11_Window)NULL;
100 _have_xrandr = XRRQueryExtension(_display, &event, &error);
102 _have_xrandr =
false;
105 _awaiting_configure =
false;
106 _dga_mouse_enabled =
false;
107 _wm_delete_window = x11_pipe->_wm_delete_window;
111 add_input_device(device);
120 ~x11GraphicsWindow() {
123 for (it = _cursor_filenames.begin(); it != _cursor_filenames.end(); it++) {
124 XFreeCursor(_display, it->second);
144 if (!_properties.get_foreground() ||
145 !_input_devices[0].get_pointer().get_in_window()) {
151 const MouseData &md = _input_devices[0].get_pointer();
152 if (!md.get_in_window() || md.get_x() != x || md.get_y() != y) {
153 if (!_dga_mouse_enabled) {
154 XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
156 _input_devices[0].set_pointer_in_window(x, y);
161 if ((device < 1)||(device >= _input_devices.size())) {
164 _input_devices[device].set_pointer_in_window(x, y);
180 PStatTimer timer(_make_current_pcollector, current_thread);
182 begin_frame_spam(mode);
186 if (_awaiting_configure) {
195 _gsg->reset_if_new();
197 if (mode == FM_render) {
199 clear_cube_map_selection();
203 return _gsg->begin_frame(current_thread);
215 end_frame_spam(mode);
218 if (mode == FM_render) {
223 _gsg->end_frame(current_thread);
225 if (mode == FM_render) {
227 clear_cube_map_selection();
247 if (_xwindow == (X11_Window)0) {
254 XKeyEvent keyrelease_event;
255 bool got_keyrelease_event =
false;
257 XConfigureEvent configure_event;
258 bool got_configure_event =
false;
261 bool changed_properties =
false;
263 while (XCheckIfEvent(_display, &event, check_event, (
char *)
this)) {
264 if (XFilterEvent(&event, None)) {
268 if (got_keyrelease_event) {
274 got_keyrelease_event =
false;
276 if (event.type == KeyPress &&
277 event.xkey.keycode == keyrelease_event.keycode &&
278 (event.xkey.time - keyrelease_event.time <= 1)) {
281 handle_keystroke(event.xkey);
285 handle_keypress(event.xkey);
291 handle_keyrelease(keyrelease_event);
297 switch (event.type) {
301 case ConfigureNotify:
305 configure_event =
event.xconfigure;
306 got_configure_event =
true;
311 button = get_mouse_button(event.xbutton);
312 if (!_dga_mouse_enabled) {
313 _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
315 _input_devices[0].button_down(button);
319 button = get_mouse_button(event.xbutton);
320 if (!_dga_mouse_enabled) {
321 _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
323 _input_devices[0].button_up(button);
327 if (_dga_mouse_enabled) {
328 const MouseData &md = _input_devices[0].get_raw_pointer();
329 _input_devices[0].set_pointer_in_window(md.get_x() +
event.xmotion.x_root, md.get_y() +
event.xmotion.y_root);
331 _input_devices[0].set_pointer_in_window(event.xmotion.x, event.xmotion.y);
336 handle_keystroke(event.xkey);
337 handle_keypress(event.xkey);
344 keyrelease_event =
event.xkey;
345 got_keyrelease_event =
true;
349 if (_dga_mouse_enabled) {
350 const MouseData &md = _input_devices[0].get_raw_pointer();
351 _input_devices[0].set_pointer_in_window(md.get_x(), md.get_y());
353 _input_devices[0].set_pointer_in_window(event.xcrossing.x, event.xcrossing.y);
358 _input_devices[0].set_pointer_out_of_window();
363 changed_properties =
true;
367 _input_devices[0].focus_lost();
369 changed_properties =
true;
374 changed_properties =
true;
379 changed_properties =
true;
382 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
386 if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
390 if (!close_request_event.empty()) {
393 throw_event(close_request_event);
402 system_changed_properties(properties);
411 x11display_cat.info()
412 <<
"DestroyNotify\n";
416 x11display_cat.warning()
417 <<
"unhandled X event type " <<
event.type <<
"\n";
421 if (got_configure_event) {
423 _awaiting_configure =
false;
431 properties.
set_origin(configure_event.x, configure_event.y);
432 properties.
set_size(configure_event.width, configure_event.height);
434 if (_properties.get_fixed_size()) {
441 if (configure_event.width != _fixed_size.get_x() ||
442 configure_event.height != _fixed_size.get_y()) {
443 XWindowChanges changes;
444 changes.width = _fixed_size.get_x();
445 changes.height = _fixed_size.get_y();
446 int value_mask = (CWWidth | CWHeight);
447 XConfigureWindow(_display, _xwindow, value_mask, &changes);
450 changed_properties =
true;
453 if (changed_properties) {
454 system_changed_properties(properties);
457 if (got_keyrelease_event) {
460 handle_keyrelease(keyrelease_event);
490 DCAST_INTO_V(x11_pipe, _pipe);
494 bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen();
497 if (is_fullscreen != want_fullscreen || (is_fullscreen && properties.
has_size())) {
498 if (want_fullscreen) {
501 XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->
get_root());
502 SizeID old_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation);
503 SizeID new_size_id = (SizeID) -1;
504 int num_sizes = 0, reqsizex, reqsizey;
509 reqsizex = _properties.get_x_size();
510 reqsizey = _properties.get_y_size();
513 xrrs = XRRSizes(_display, 0, &num_sizes);
514 for (
int i = 0; i < num_sizes; ++i) {
515 if (xrrs[i].width == reqsizex &&
516 xrrs[i].height == reqsizey) {
520 if (new_size_id == (SizeID) -1) {
521 x11display_cat.error()
522 <<
"Videocard has no supported display resolutions at specified res ("
523 << reqsizex <<
" x " << reqsizey <<
")\n";
525 if (new_size_id != old_size_id) {
527 XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
528 if (_orig_size_id == (SizeID) -1) {
530 _orig_size_id = old_size_id;
545 if (_have_xrandr && _orig_size_id != (SizeID) -1) {
546 XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->
get_root());
547 XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), _orig_size_id, _orig_rotation, CurrentTime);
548 _orig_size_id = (SizeID) -1;
552 if (!properties.
has_origin() && _properties.has_origin()) {
553 properties.
set_origin(_properties.get_x_origin(), _properties.get_y_origin());
564 if (x_origin == -2) {
567 if (y_origin == -2) {
571 if (x_origin == -2) {
574 if (y_origin == -2) {
592 set_wm_properties(properties,
true);
597 _properties.set_title(properties.
get_title());
611 XWindowChanges changes;
614 if (_properties.get_fullscreen()) {
617 value_mask |= CWX | CWY;
623 if (changes.x != -1) value_mask |= CWX;
624 if (changes.y != -1) value_mask |= CWY;
632 _fixed_size = _properties.get_size();
638 value_mask |= (CWWidth | CWHeight);
640 if (_properties.get_fixed_size()) {
641 _fixed_size = properties.
get_size();
653 case WindowProperties::Z_bottom:
654 changes.stack_mode = Below;
657 case WindowProperties::Z_normal:
658 changes.stack_mode = TopIf;
661 case WindowProperties::Z_top:
662 changes.stack_mode = Above;
666 value_mask |= (CWStackMode);
680 _properties.set_cursor_filename(cursor_filename);
684 _properties.set_cursor_filename(filename);
686 if (_properties.get_cursor_hidden()) {
689 }
else if (!cursor_filename.empty()) {
691 X11_Cursor cursor = get_cursor(cursor_filename);
692 XDefineCursor(_display, _xwindow, cursor);
695 XDefineCursor(_display, _xwindow, None);
701 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
703 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
708 if (properties.has_mouse_mode()) {
710 case WindowProperties::M_absolute:
711 XUngrabPointer(_display, CurrentTime);
713 if (_dga_mouse_enabled) {
714 x11display_cat.info() <<
"Disabling relative mouse using XF86DGA extension\n";
715 XF86DGADirectVideo(_display, _screen, 0);
716 _dga_mouse_enabled =
false;
719 _properties.set_mouse_mode(WindowProperties::M_absolute);
723 case WindowProperties::M_relative:
725 if (!_dga_mouse_enabled) {
726 int major_ver, minor_ver;
727 if (XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) {
729 X11_Cursor cursor = None;
730 if (_properties.get_cursor_hidden()) {
732 DCAST_INTO_V(x11_pipe, _pipe);
736 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
737 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
738 x11display_cat.error() <<
"Failed to grab pointer!\n";
740 x11display_cat.info() <<
"Enabling relative mouse using XF86DGA extension\n";
741 XF86DGADirectVideo(_display, _screen, XF86DGADirectMouse);
743 _properties.set_mouse_mode(WindowProperties::M_relative);
745 _dga_mouse_enabled =
true;
750 XQueryPointer(_display, _xwindow, &event.xbutton.root,
751 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
752 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
753 _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y);
756 x11display_cat.info() <<
"XF86DGA extension not available\n";
757 _dga_mouse_enabled =
false;
763 case WindowProperties::M_confined:
766 if (_dga_mouse_enabled) {
767 XF86DGADirectVideo(_display, _screen, 0);
768 _dga_mouse_enabled =
false;
771 X11_Cursor cursor = None;
772 if (_properties.get_cursor_hidden()) {
774 DCAST_INTO_V(x11_pipe, _pipe);
778 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
779 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
780 x11display_cat.error() <<
"Failed to grab pointer!\n";
782 _properties.set_mouse_mode(WindowProperties::M_confined);
790 if (value_mask != 0) {
793 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
796 _awaiting_configure =
true;
805 void x11GraphicsWindow::
806 mouse_mode_absolute() {
815 void x11GraphicsWindow::
816 mouse_mode_relative() {
826 void x11GraphicsWindow::
832 if (_ic != (XIC)NULL) {
837 if (_xwindow != (X11_Window)NULL) {
838 XDestroyWindow(_display, _xwindow);
839 _xwindow = (X11_Window)NULL;
849 if (_have_xrandr && _orig_size_id != (SizeID) -1) {
853 DCAST_INTO_V(x11_pipe, _pipe);
859 root = RootWindow(_display, _screen);
861 XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, root);
862 XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
867 GraphicsWindow::close_window();
877 bool x11GraphicsWindow::
879 if (_visual_info == NULL) {
881 x11display_cat.error()
882 <<
"No X visual: cannot open window.\n";
887 DCAST_INTO_R(x11_pipe, _pipe,
false);
889 if (!_properties.has_origin()) {
890 _properties.set_origin(0, 0);
892 if (!_properties.has_size()) {
893 _properties.set_size(100, 100);
897 if (_properties.get_fullscreen() && _have_xrandr) {
898 XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->
get_root());
899 if (_orig_size_id == (SizeID) -1) {
900 _orig_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation);
902 int num_sizes, new_size_id = -1;
904 xrrs = XRRSizes(_display, 0, &num_sizes);
905 for (
int i = 0; i < num_sizes; ++i) {
906 if (xrrs[i].width == _properties.get_x_size() &&
907 xrrs[i].height == _properties.get_y_size()) {
911 if (new_size_id == -1) {
912 x11display_cat.error()
913 <<
"Videocard has no supported display resolutions at specified res ("
914 << _properties.get_x_size() <<
" x " << _properties.get_y_size() <<
")\n";
918 if (new_size_id != _orig_size_id) {
919 XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
926 X11_Window parent_window = x11_pipe->
get_root();
927 WindowHandle *window_handle = _properties.get_parent_window();
928 if (window_handle != NULL) {
929 x11display_cat.info()
930 <<
"Got parent_window " << *window_handle <<
"\n";
932 if (os_handle != NULL) {
933 x11display_cat.info()
934 <<
"os_handle type " << os_handle->get_type() <<
"\n";
936 if (os_handle->
is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
937 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
938 parent_window = x11_handle->get_handle();
939 }
else if (os_handle->
is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
941 parent_window = (X11_Window)int_handle->get_handle();
945 _parent_window_handle = window_handle;
948 ButtonPressMask | ButtonReleaseMask |
949 KeyPressMask | KeyReleaseMask |
950 EnterWindowMask | LeaveWindowMask |
952 FocusChangeMask | StructureNotifyMask;
955 XSetWindowAttributes wa;
956 wa.background_pixel = XBlackPixel(_display, _screen);
958 wa.colormap = _colormap;
959 wa.event_mask = _event_mask;
961 unsigned long attrib_mask =
962 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
964 _xwindow = XCreateWindow
965 (_display, parent_window,
966 _properties.get_x_origin(), _properties.get_y_origin(),
967 _properties.get_x_size(), _properties.get_y_size(),
968 0, _visual_info->depth, InputOutput,
969 _visual_info->visual, attrib_mask, &wa);
971 if (_xwindow == (X11_Window)0) {
972 x11display_cat.error()
973 <<
"failed to create X window.\n";
977 if (_properties.get_fixed_size()) {
978 _fixed_size = _properties.get_size();
981 set_wm_properties(_properties,
false);
987 XIM im = x11_pipe->
get_im();
992 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
994 if (_ic == (XIC)NULL) {
995 x11display_cat.warning()
996 <<
"Couldn't create input context.\n";
1000 if (_properties.get_cursor_hidden()) {
1003 }
else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1005 X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1006 XDefineCursor(_display, _xwindow, cursor);
1009 XMapWindow(_display, _xwindow);
1011 if (_properties.get_raw_mice()) {
1014 if (x11display_cat.is_debug()) {
1015 x11display_cat.debug()
1016 <<
"Raw mice not requested.\n";
1021 _window_handle = NativeWindowHandle::make_x11(_xwindow);
1025 _parent_window_handle->attach_child(_window_handle);
1044 void x11GraphicsWindow::
1045 set_wm_properties(
const WindowProperties &properties,
bool already_mapped) {
1047 DCAST_INTO_V(x11_pipe, _pipe);
1050 XTextProperty window_name;
1051 XTextProperty *window_name_p = (XTextProperty *)NULL;
1053 const char *name = properties.
get_title().c_str();
1054 if (XStringListToTextProperty((
char **)&name, 1, &window_name) != 0) {
1055 window_name_p = &window_name;
1061 XSizeHints *size_hints_p = NULL;
1063 size_hints_p = XAllocSizeHints();
1064 if (size_hints_p != (XSizeHints *)NULL) {
1066 if (_properties.get_fullscreen()) {
1067 size_hints_p->x = 0;
1068 size_hints_p->y = 0;
1073 size_hints_p->flags |= USPosition;
1078 size_hints_p->width = size.get_x();
1079 size_hints_p->height = size.get_y();
1080 size_hints_p->flags |= USSize;
1083 size_hints_p->min_width = size.get_x();
1084 size_hints_p->min_height = size.get_y();
1085 size_hints_p->max_width = size.get_x();
1086 size_hints_p->max_height = size.get_y();
1087 size_hints_p->flags |= (PMinSize | PMaxSize);
1094 XWMHints *wm_hints_p = NULL;
1095 wm_hints_p = XAllocWMHints();
1096 if (wm_hints_p != (XWMHints *)NULL) {
1098 wm_hints_p->initial_state = IconicState;
1100 wm_hints_p->initial_state = NormalState;
1102 wm_hints_p->flags = StateHint;
1108 static const int max_type_data = 32;
1109 PN_int32 type_data[max_type_data];
1110 int next_type_data = 0;
1112 static const int max_state_data = 32;
1113 PN_int32 state_data[max_state_data];
1114 int next_state_data = 0;
1116 static const int max_set_data = 32;
1119 inline SetAction() { }
1120 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1124 SetAction set_data[max_set_data];
1125 int next_set_data = 0;
1131 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1134 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1137 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1140 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1153 XClassHint *class_hints_p = NULL;
1154 if (!x_wm_class.empty()) {
1156 class_hints_p = XAllocClassHint();
1157 class_hints_p->res_class = (
char*) x_wm_class.c_str();
1158 if (!x_wm_class_name.empty()) {
1159 class_hints_p->res_name = (
char*) x_wm_class_name.c_str();
1163 class_hints_p = XAllocClassHint();
1164 class_hints_p->res_class = (
char*)
"Undecorated";
1168 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1173 case WindowProperties::Z_bottom:
1174 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1175 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1176 x11_pipe->_net_wm_state_add);
1177 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1178 x11_pipe->_net_wm_state_remove);
1181 case WindowProperties::Z_normal:
1182 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1183 x11_pipe->_net_wm_state_remove);
1184 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1185 x11_pipe->_net_wm_state_remove);
1188 case WindowProperties::Z_top:
1189 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1190 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1191 x11_pipe->_net_wm_state_remove);
1192 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1193 x11_pipe->_net_wm_state_add);
1198 nassertv(next_type_data < max_type_data);
1199 nassertv(next_state_data < max_state_data);
1200 nassertv(next_set_data < max_set_data);
1203 PN_int32 pid = getpid();
1204 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1205 XA_CARDINAL, 32, PropModeReplace,
1206 (
unsigned char *)&pid, 1);
1208 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1209 XA_ATOM, 32, PropModeReplace,
1210 (
unsigned char *)type_data, next_type_data);
1213 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1214 XA_ATOM, 32, PropModeReplace,
1215 (
unsigned char *)state_data, next_state_data);
1217 if (already_mapped) {
1223 DCAST_INTO_V(x11_pipe, _pipe);
1225 for (
int i = 0; i < next_set_data; ++i) {
1226 XClientMessageEvent event;
1227 memset(&event, 0,
sizeof(event));
1228 event.type = ClientMessage;
1229 event.send_event = True;
1230 event.display = _display;
1231 event.window = _xwindow;
1232 event.message_type = x11_pipe->_net_wm_state;
1234 event.data.l[0] = set_data[i]._action;
1235 event.data.l[1] = set_data[i]._state;
1236 event.data.l[2] = 0;
1237 event.data.l[3] = 1;
1239 XSendEvent(_display, x11_pipe->
get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1243 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1244 NULL, 0, size_hints_p, wm_hints_p, class_hints_p);
1246 if (size_hints_p != (XSizeHints *)NULL) {
1247 XFree(size_hints_p);
1249 if (wm_hints_p != (XWMHints *)NULL) {
1252 if (class_hints_p != (XClassHint *)NULL) {
1253 XFree(class_hints_p);
1260 Atom protocols[] = {
1264 XSetWMProtocols(_display, _xwindow, protocols,
1265 sizeof(protocols) /
sizeof(Atom));
1274 void x11GraphicsWindow::
1275 setup_colormap(XVisualInfo *visual) {
1277 DCAST_INTO_V(x11_pipe, _pipe);
1278 X11_Window root_window = x11_pipe->
get_root();
1280 _colormap = XCreateColormap(_display, root_window,
1281 visual->visual, AllocNone);
1289 void x11GraphicsWindow::
1291 #ifdef PHAVE_LINUX_INPUT_H
1292 bool any_present =
false;
1293 bool any_mice =
false;
1295 for (
int i=0; i<64; i++) {
1296 uint8_t evtypes[EV_MAX/8 + 1];
1298 fnb <<
"/dev/input/event" << i;
1299 string fn = fnb.str();
1300 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1306 if ((ioctl(fd, EVIOCGNAME(
sizeof(name)), name) < 0)||
1307 (ioctl(fd, EVIOCGPHYS(
sizeof(phys)), phys) < 0)||
1308 (ioctl(fd, EVIOCGPHYS(
sizeof(uniq)), uniq) < 0)||
1309 (ioctl(fd, EVIOCGBIT(0, EV_MAX), &evtypes) < 0)) {
1311 x11display_cat.error() <<
1312 "Opening raw mice: ioctl failed on " << fn <<
"\n";
1314 if (test_bit(EV_REL, evtypes) || test_bit(EV_ABS, evtypes)) {
1315 for (
char *p=name; *p; p++) {
1316 if (((*p<
'a')||(*p>
'z')) && ((*p<
'A')||(*p>
'Z')) && ((*p<
'0')||(*p>
'9'))) {
1320 for (
char *p=uniq; *p; p++) {
1321 if (((*p<
'a')||(*p>
'z')) && ((*p<
'A')||(*p>
'Z')) && ((*p<
'0')||(*p>
'9'))) {
1325 string full_id = ((string)name) +
"." + uniq;
1326 MouseDeviceInfo inf;
1328 inf._input_device_index = _input_devices.
size();
1329 inf._io_buffer =
"";
1330 _mouse_device_info.push_back(inf);
1333 add_input_device(device);
1334 x11display_cat.info() <<
"Raw mouse " <<
1335 inf._input_device_index <<
" detected: " << full_id <<
"\n";
1342 if ((errno == ENOENT)||(errno == ENOTDIR)) {
1346 x11display_cat.error() <<
1347 "Opening raw mice: " << strerror(errno) <<
" " << fn <<
"\n";
1353 x11display_cat.error() <<
1354 "Opening raw mice: files not found: /dev/input/event*\n";
1355 }
else if (!any_mice) {
1356 x11display_cat.error() <<
1357 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1360 x11display_cat.error() <<
1361 "Opening raw mice: panda not compiled with raw mouse support.\n";
1370 void x11GraphicsWindow::
1372 #ifdef PHAVE_LINUX_INPUT_H
1373 for (
int di = 0; di < _mouse_device_info.size(); ++di) {
1374 MouseDeviceInfo &inf = _mouse_device_info[di];
1380 int nread = read(inf._fd, tbuf,
sizeof(tbuf));
1382 inf._io_buffer += string(tbuf, nread);
1384 if ((nread < 0) && ((errno == EWOULDBLOCK) || (errno==EAGAIN))) {
1395 int nevents = inf._io_buffer.size() /
sizeof(
struct input_event);
1399 const input_event *events = (
const input_event *)(inf._io_buffer.c_str());
1403 for (
int i = 0; i < nevents; i++) {
1404 if (events[i].type == EV_REL) {
1405 if (events[i].code == REL_X) x += events[i].value;
1406 if (events[i].code == REL_Y) y += events[i].value;
1407 }
else if (events[i].type == EV_ABS) {
1408 if (events[i].code == ABS_X) x = events[i].value;
1409 if (events[i].code == ABS_Y) y = events[i].value;
1410 }
else if (events[i].type == EV_KEY) {
1411 if ((events[i].code >= BTN_MOUSE) && (events[i].code < BTN_MOUSE + 8)) {
1412 int btn = events[i].code - BTN_MOUSE;
1414 if (events[i].value) {
1422 inf._io_buffer.erase(0, nevents *
sizeof(
struct input_event));
1434 void x11GraphicsWindow::
1435 handle_keystroke(XKeyEvent &event) {
1436 if (!_dga_mouse_enabled) {
1437 _input_devices[0].set_pointer_in_window(event.x, event.y);
1442 static const int buffer_size = 256;
1443 wchar_t buffer[buffer_size];
1445 int len = XwcLookupString(_ic, &event, buffer, buffer_size, NULL,
1447 if (status == XBufferOverflow) {
1448 x11display_cat.error()
1449 <<
"Overflowed input buffer.\n";
1454 for (
int i = 0; i < len; i++) {
1455 _input_devices[0].keystroke(buffer[i]);
1473 void x11GraphicsWindow::
1474 handle_keypress(XKeyEvent &event) {
1475 if (!_dga_mouse_enabled) {
1476 _input_devices[0].set_pointer_in_window(event.x, event.y);
1482 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1483 _input_devices[0].button_down(KeyboardButton::control());
1485 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1486 _input_devices[0].button_down(KeyboardButton::shift());
1488 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1489 _input_devices[0].button_down(KeyboardButton::alt());
1491 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1492 _input_devices[0].button_down(KeyboardButton::meta());
1494 _input_devices[0].button_down(button);
1497 ButtonHandle raw_button = map_raw_button(event.keycode);
1499 _input_devices[0].raw_button_down(raw_button);
1509 void x11GraphicsWindow::
1510 handle_keyrelease(XKeyEvent &event) {
1511 if (!_dga_mouse_enabled) {
1512 _input_devices[0].set_pointer_in_window(event.x, event.y);
1518 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1519 _input_devices[0].button_up(KeyboardButton::control());
1521 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1522 _input_devices[0].button_up(KeyboardButton::shift());
1524 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1525 _input_devices[0].button_up(KeyboardButton::alt());
1527 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1528 _input_devices[0].button_up(KeyboardButton::meta());
1530 _input_devices[0].button_up(button);
1533 ButtonHandle raw_button = map_raw_button(event.keycode);
1535 _input_devices[0].raw_button_up(raw_button);
1546 get_button(XKeyEvent &key_event,
bool allow_shift) {
1547 KeySym key = XLookupKeysym(&key_event, 0);
1549 if ((key_event.state & Mod2Mask) != 0) {
1564 case XK_KP_Multiply:
1566 case XK_KP_Separator:
1567 case XK_KP_Subtract:
1590 k2 = XLookupKeysym(&key_event, 1);
1591 button = map_button(k2);
1606 if ((key_event.state & ShiftMask) != 0) {
1607 KeySym k2 = XLookupKeysym(&key_event, 1);
1617 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1618 if (key >= XK_a and key <= XK_z) {
1619 key += (XK_A - XK_a);
1624 return map_button(key);
1634 map_button(KeySym key)
const {
1639 return KeyboardButton::backspace();
1642 return KeyboardButton::tab();
1645 return KeyboardButton::enter();
1647 return KeyboardButton::escape();
1650 return KeyboardButton::space();
1671 case XK_KP_Multiply:
1677 case XK_KP_Separator:
1680 case XK_KP_Subtract:
1785 case XK_bracketleft:
1789 case XK_bracketright:
1791 case XK_asciicircum:
1860 return KeyboardButton::f1();
1863 return KeyboardButton::f2();
1866 return KeyboardButton::f3();
1869 return KeyboardButton::f4();
1871 return KeyboardButton::f5();
1873 return KeyboardButton::f6();
1875 return KeyboardButton::f7();
1877 return KeyboardButton::f8();
1879 return KeyboardButton::f9();
1881 return KeyboardButton::f10();
1883 return KeyboardButton::f11();
1885 return KeyboardButton::f12();
1888 return KeyboardButton::left();
1891 return KeyboardButton::up();
1894 return KeyboardButton::right();
1897 return KeyboardButton::down();
1900 return KeyboardButton::page_up();
1903 return KeyboardButton::page_down();
1906 return KeyboardButton::home();
1909 return KeyboardButton::end();
1912 return KeyboardButton::insert();
1915 return KeyboardButton::del();
1917 return KeyboardButton::num_lock();
1918 case XK_Scroll_Lock:
1919 return KeyboardButton::scroll_lock();
1921 return KeyboardButton::print_screen();
1923 return KeyboardButton::pause();
1925 return KeyboardButton::menu();
1927 return KeyboardButton::lshift();
1929 return KeyboardButton::rshift();
1931 return KeyboardButton::lcontrol();
1933 return KeyboardButton::rcontrol();
1935 return KeyboardButton::lalt();
1937 return KeyboardButton::ralt();
1940 return KeyboardButton::lmeta();
1943 return KeyboardButton::rmeta();
1945 return KeyboardButton::caps_lock();
1947 return KeyboardButton::shift_lock();
1949 if (x11display_cat.is_debug()) {
1950 x11display_cat.debug()
1951 <<
"Unrecognized keysym 0x" << hex << key << dec <<
"\n";
1962 map_raw_button(KeyCode key)
const {
1964 case 9:
return KeyboardButton::escape();
1977 case 22:
return KeyboardButton::backspace();
1978 case 23:
return KeyboardButton::tab();
1991 case 36:
return KeyboardButton::enter();
1992 case 37:
return KeyboardButton::lcontrol();
2005 case 50:
return KeyboardButton::lshift();
2017 case 62:
return KeyboardButton::rshift();
2019 case 64:
return KeyboardButton::lalt();
2020 case 65:
return KeyboardButton::space();
2021 case 66:
return KeyboardButton::caps_lock();
2022 case 67:
return KeyboardButton::f1();
2023 case 68:
return KeyboardButton::f2();
2024 case 69:
return KeyboardButton::f3();
2025 case 70:
return KeyboardButton::f4();
2026 case 71:
return KeyboardButton::f5();
2027 case 72:
return KeyboardButton::f6();
2028 case 73:
return KeyboardButton::f7();
2029 case 74:
return KeyboardButton::f8();
2030 case 75:
return KeyboardButton::f9();
2031 case 76:
return KeyboardButton::f10();
2032 case 77:
return KeyboardButton::num_lock();
2033 case 78:
return KeyboardButton::scroll_lock();
2048 case 95:
return KeyboardButton::f11();
2049 case 96:
return KeyboardButton::f12();
2051 case 104:
return KeyboardButton::enter();
2052 case 105:
return KeyboardButton::rcontrol();
2054 case 107:
return KeyboardButton::print_screen();
2055 case 108:
return KeyboardButton::ralt();
2057 case 110:
return KeyboardButton::home();
2058 case 111:
return KeyboardButton::up();
2059 case 112:
return KeyboardButton::page_up();
2060 case 113:
return KeyboardButton::left();
2061 case 114:
return KeyboardButton::right();
2062 case 115:
return KeyboardButton::end();
2063 case 116:
return KeyboardButton::down();
2064 case 117:
return KeyboardButton::page_down();
2065 case 118:
return KeyboardButton::insert();
2066 case 119:
return KeyboardButton::del();
2068 case 127:
return KeyboardButton::pause();
2070 case 133:
return KeyboardButton::lmeta();
2071 case 134:
return KeyboardButton::rmeta();
2072 case 135:
return KeyboardButton::menu();
2084 get_mouse_button(XButtonEvent &button_event) {
2085 int index = button_event.button;
2086 if (index == x_wheel_up_button) {
2088 }
else if (index == x_wheel_down_button) {
2090 }
else if (index == x_wheel_left_button) {
2092 }
else if (index == x_wheel_right_button) {
2106 get_keyboard_map()
const {
2111 for (
int k = 9; k <= 135; ++k) {
2117 KeySym sym = XKeycodeToKeysym(_display, k, 0);
2137 Bool x11GraphicsWindow::
2138 check_event(X11_Display *display, XEvent *event,
char *arg) {
2142 return (event->xany.window == self->_xwindow);
2152 X11_Cursor x11GraphicsWindow::
2153 get_cursor(
const Filename &filename) {
2154 #ifndef HAVE_XCURSOR
2155 x11display_cat.info()
2156 <<
"XCursor support not enabled in build; cannot change mouse cursor.\n";
2158 #else // HAVE_XCURSOR
2161 if (fi != _cursor_filenames.end()) {
2170 x11display_cat.warning()
2171 <<
"Could not find cursor filename " << filename <<
"\n";
2174 fi = _cursor_filenames.find(resolved);
2175 if (fi != _cursor_filenames.end()) {
2182 x11display_cat.warning()
2183 <<
"Could not open cursor file " << filename <<
"\n";
2189 str->read(magic, 4);
2191 x11display_cat.warning()
2192 <<
"Could not read from cursor file " << filename <<
"\n";
2195 str->seekg(0, istream::beg);
2197 X11_Cursor h = None;
2198 if (memcmp(magic,
"Xcur", 4) == 0) {
2200 x11display_cat.debug()
2201 <<
"Loading X11 cursor " << filename <<
"\n";
2203 xcfile.closure = str;
2204 xcfile.read = &xcursor_read;
2205 xcfile.write = &xcursor_write;
2206 xcfile.seek = &xcursor_seek;
2208 XcursorImages *images = XcursorXcFileLoadImages(&xcfile, XcursorGetDefaultSize(_display));
2209 if (images != NULL) {
2210 h = XcursorImagesLoadCursor(_display, images);
2211 XcursorImagesDestroy(images);
2214 }
else if (memcmp(magic,
"\0\0\1\0", 4) == 0
2215 || memcmp(magic,
"\0\0\2\0", 4) == 0) {
2217 x11display_cat.debug()
2218 <<
"Loading Windows cursor " << filename <<
"\n";
2226 x11display_cat.warning()
2227 <<
"X11 cursor filename '" << resolved <<
"' could not be loaded!\n";
2230 _cursor_filenames[resolved] = h;
2232 #endif // HAVE_XCURSOR
2243 X11_Cursor x11GraphicsWindow::
2244 read_ico(istream &ico) {
2247 uint16_t reserved, type, count;
2251 uint8_t width, height, colorCount, reserved;
2252 uint16_t xhot, yhot;
2253 uint32_t bitmapSize, offset;
2257 uint32_t headerSize, width, height;
2258 uint16_t planes, bitsPerPixel;
2259 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2263 uint8_t blue, green, red, reserved;
2267 unsigned int j, k, mask, shift;
2268 size_t colorCount, bitsPerPixel;
2270 IcoInfoHeader infoHeader;
2271 IcoEntry *entries = NULL;
2272 IcoColor
color, *palette = NULL;
2274 size_t xorBmpSize, andBmpSize;
2275 char *curXor, *curAnd;
2276 char *xorBmp = NULL, *andBmp = NULL;
2277 XcursorImage *image = NULL;
2278 X11_Cursor ret = None;
2280 int def_size = XcursorGetDefaultSize(_display);
2283 ico.read(reinterpret_cast<char *>(&header),
sizeof(IcoHeader));
2284 if (!ico.good())
goto cleanup;
2285 if (header.type != 1 && header.type != 2)
goto cleanup;
2286 if (header.count < 1)
goto cleanup;
2289 entries =
new IcoEntry[header.count];
2290 ico.read(reinterpret_cast<char *>(entries), header.count *
sizeof(IcoEntry));
2291 if (!ico.good())
goto cleanup;
2292 for (i = 1; i < header.count; i++) {
2293 if (entries[i].width == def_size && entries[i].height == def_size) {
2298 if (entries[i].width > entries[entry].width ||
2299 entries[i].height > entries[entry].height)
2304 ico.seekg(entries[entry].offset);
2305 if (!ico.good())
goto cleanup;
2306 ico.read(reinterpret_cast<char *>(&infoHeader),
sizeof(IcoInfoHeader));
2307 if (!ico.good())
goto cleanup;
2308 bitsPerPixel = infoHeader.bitsPerPixel;
2311 if (infoHeader.compression != 0)
goto cleanup;
2314 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2315 colorCount = 1 << bitsPerPixel;
2316 palette =
new IcoColor[colorCount];
2317 ico.read(reinterpret_cast<char *>(palette), colorCount *
sizeof(IcoColor));
2318 if (!ico.good())
goto cleanup;
2322 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2323 andBmpSize = (infoHeader.width * (infoHeader.height / 2)) / 8;
2324 curXor = xorBmp =
new char[xorBmpSize];
2325 curAnd = andBmp =
new char[andBmpSize];
2326 ico.read(xorBmp, xorBmpSize);
2327 if (!ico.good())
goto cleanup;
2328 ico.read(andBmp, andBmpSize);
2329 if (!ico.good())
goto cleanup;
2332 image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2333 if (header.type == 2) { image->xhot = entries[entry].xhot; image->yhot = entries[entry].yhot; }
2337 switch (bitsPerPixel) {
2343 mask = ((1 << bitsPerPixel) - 1);
2344 for (i = image->height - 1; i >= 0; i--) {
2345 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2346 for (k = 0; k < 8 / bitsPerPixel; k++) {
2347 shift = 8 - ((k + 1) * bitsPerPixel);
2348 color = palette[(*curXor & (mask << shift)) >> shift];
2349 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2350 (color.green << 8) +
2358 for (j = 0; j < image->width; j += 8) {
2359 for (k = 0; k < 8; k++) {
2361 image->pixels[(i * image->width) + j + k] |=
2362 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2372 for (i = image->height - 1; i >= 0; i--) {
2373 for (j = 0; j < image->width; j++) {
2374 image->pixels[(i * image->width) + j] = (*(curXor + 2) << 16) +
2375 (*(curXor + 1) << 8) + (*curXor);
2380 for (j = 0; j < image->width; j += 8) {
2381 for (k = 0; k < 8; k++) {
2383 image->pixels[(i * image->width) + j + k] |=
2384 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2395 for (i = image->height - 1; i >= 0; i--) {
2396 for (j = 0; j < image->width; j++) {
2397 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2398 (*(curXor + 2) << 16) +
2399 (*(curXor + 1) << 8) +
2411 ret = XcursorImageLoadCursor(_display, image);
2414 XcursorImageDestroy(image);
2422 #endif // HAVE_XCURSOR
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
This graphics pipe represents the interface for creating graphics windows on an X-based client...
This is our own Panda specialization on the default STL map.
bool has_cursor_filename() const
Returns true if set_cursor_filename() has been specified.
This object represents a window on the desktop, not necessarily a Panda window.
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
void clear_foreground()
Removes the foreground specification from the properties.
bool has_cursor_hidden() const
Returns true if set_cursor_hidden() has been specified.
const string & get_title() const
Returns the window's title.
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
void clear_origin()
Removes the origin specification from the properties.
bool get_minimized() const
Returns true if the window is minimized.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
This is the base class for all two-component vectors and points.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
void set_size(const LVector2i &size)
Specifies the requested size of the window, in pixels.
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
void clear_title()
Removes the title specification from the properties.
int get_screen() const
Returns the X screen number associated with the pipe.
bool has_z_order() const
Returns true if the window z_order has been specified, false otherwise.
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
bool get_fullscreen() const
Returns true if the window is in fullscreen mode.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
int get_display_height() const
Returns the height of the entire display, if it is known.
const LVector2i & get_size() const
Returns size in pixels of the useful part of the window, not including decorations.
MouseMode get_mouse_mode() const
See set_mouse_mode().
A container for the various kinds of properties we might ask to have on a graphics window before we o...
string get_close_request_event() const
Returns the name of the event set via set_close_request_event().
The name of a file, such as a texture file or an Egg file.
void clear_mouse_mode()
Removes the mouse_mode specification from the properties.
const Filename & get_cursor_filename() const
Returns the icon filename associated with the mouse cursor.
void clear_size()
Removes the size specification from the properties.
bool has_title() const
Returns true if the window title has been specified, false otherwise.
An object to create GraphicsOutputs that share a particular 3-D API.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
bool get_foreground() const
Returns true if the window is in the foreground.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension=string()) const
Searches the given search path for the filename.
void set_origin(const LPoint2i &origin)
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
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...
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
bool has_foreground() const
Returns true if set_foreground() has been specified.
void clear_fixed_size()
Removes the fixed_size specification from the properties.
This is a base class for the various different classes that represent the result of a frame of render...
Similar to MutexHolder, but for a light reentrant mutex.
bool has_minimized() const
Returns true if set_minimized() has been specified.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
bool has_origin() const
Returns true if the window origin has been specified, false otherwise.
bool get_fixed_size() const
Returns true if the window cannot be resized by the user, false otherwise.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
bool has_size() const
Returns true if the window size has been specified, false otherwise.
int get_display_width() const
Returns the width of the entire display, if it is known.
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
bool get_cursor_hidden() const
Returns true if the mouse cursor is invisible.
void set_foreground(bool foreground)
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
void clear_z_order()
Removes the z_order specification from the properties.
A thread; that is, a lightweight process.
void clear_cursor_hidden()
Removes the cursor_hidden specification from the properties.
void set_minimized(bool minimized)
Specifies whether the window should be created minimized (true), or normal (false).
bool has_fixed_size() const
Returns true if set_fixed_size() has been specified.
Encapsulates all the communication with a particular instance of a given rendering backend...
This class is the main interface to controlling the render process.
ZOrder get_z_order() const
Returns the window's z_order.
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.
TypeHandle is the identifier used to differentiate C++ class types.
OSHandle * get_os_handle() const
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
void clear_cursor_filename()
Removes the cursor_filename specification from the properties.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
bool get_undecorated() const
Returns true if the window has no border.
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...
void set_open(bool open)
Specifies whether the window should be open.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
void clear_fullscreen()
Removes the fullscreen specification from the properties.
Interfaces to the X11 window system.
bool has_fullscreen() const
Returns true if set_fullscreen() has been specified.
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...
static int size()
Returns 3: the number of components of a LVecBase3.