38 using std::ostringstream;
43 int (*read)(XcursorFile *,
unsigned char *, int);
44 int (*write)(XcursorFile *,
unsigned char *, int);
45 int (*seek)(XcursorFile *, long, int);
48 typedef struct _XcursorImage {
59 static int xcursor_read(XcursorFile *file,
unsigned char *buf,
int len) {
60 istream* str = (istream*) file->closure;
61 str->read((
char*) buf, len);
65 static int xcursor_write(XcursorFile *file,
unsigned char *buf,
int len) {
67 nassertr_always(
false, 0);
71 static int xcursor_seek(XcursorFile *file,
long offset,
int whence) {
72 istream* str = (istream*) file->closure;
75 str->seekg(offset, istream::beg);
78 str->seekg(offset, istream::cur);
81 str->seekg(offset, istream::end);
100 GraphicsWindow(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
103 DCAST_INTO_V(x11_pipe, _pipe);
106 _xwindow = (X11_Window)
nullptr;
108 _visual_info =
nullptr;
111 if (x11_pipe->_have_xrandr) {
114 _XRRGetScreenInfo = x11_pipe->_XRRGetScreenInfo;
115 _XRRSetScreenConfig = x11_pipe->_XRRSetScreenConfig;
118 _awaiting_configure =
false;
119 _dga_mouse_enabled =
false;
120 _override_redirect = False;
121 _wm_delete_window = x11_pipe->_wm_delete_window;
124 add_input_device(device);
132 ~x11GraphicsWindow() {
133 if (!_cursor_filenames.empty()) {
135 for (
auto item : _cursor_filenames) {
136 XFreeCursor(_display, item.second);
151 nassertr(device >= 0 && device < (
int)_input_devices.size(),
MouseData());
157 if (device == 0 && !_dga_mouse_enabled && result._in_window &&
158 x11GraphicsPipe::_x_mutex.
try_lock()) {
160 if (_xwindow != None &&
161 XQueryPointer(_display, _xwindow, &event.xbutton.root,
162 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
163 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state)) {
165 result._xpos =
event.xbutton.x;
166 result._ypos =
event.xbutton.y;
169 x11GraphicsPipe::_x_mutex.
release();
196 if (!md.
get_in_window() || md.get_x() != x || md.get_y() != y) {
197 if (!_dga_mouse_enabled) {
199 XWarpPointer(_display, None, _xwindow, 0, 0, 0, 0, x, y);
235 PStatTimer timer(_make_current_pcollector, current_thread);
237 begin_frame_spam(mode);
238 if (_gsg ==
nullptr) {
241 if (_awaiting_configure) {
250 _gsg->reset_if_new();
252 if (mode == FM_render) {
254 clear_cube_map_selection();
258 return _gsg->begin_frame(current_thread);
268 end_frame_spam(mode);
269 nassertv(_gsg !=
nullptr);
271 if (mode == FM_render) {
276 _gsg->end_frame(current_thread);
278 if (mode == FM_render) {
280 clear_cube_map_selection();
297 if (_xwindow == (X11_Window)0) {
302 XKeyEvent keyrelease_event;
303 bool got_keyrelease_event =
false;
305 XConfigureEvent configure_event;
306 bool got_configure_event =
false;
309 bool changed_properties =
false;
311 while (XCheckIfEvent(_display, &event, check_event, (
char *)
this)) {
312 if (XFilterEvent(&event, None)) {
316 if (got_keyrelease_event) {
321 got_keyrelease_event =
false;
323 if (event.type == KeyPress &&
324 event.xkey.keycode == keyrelease_event.keycode &&
325 (event.xkey.time - keyrelease_event.time <= 1)) {
328 handle_keystroke(event.xkey);
332 handle_keypress(event.xkey);
338 handle_keyrelease(keyrelease_event);
344 switch (event.type) {
348 case ConfigureNotify:
351 configure_event =
event.xconfigure;
352 got_configure_event =
true;
357 button = get_mouse_button(event.xbutton);
358 if (!_dga_mouse_enabled) {
365 button = get_mouse_button(event.xbutton);
366 if (!_dga_mouse_enabled) {
373 if (_dga_mouse_enabled) {
382 handle_keystroke(event.xkey);
383 handle_keypress(event.xkey);
390 keyrelease_event =
event.xkey;
391 got_keyrelease_event =
true;
395 if (_dga_mouse_enabled) {
409 changed_properties =
true;
415 changed_properties =
true;
420 changed_properties =
true;
425 changed_properties =
true;
428 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
432 if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
436 if (!close_request_event.empty()) {
439 throw_event(close_request_event);
448 system_changed_properties(properties);
456 x11display_cat.info()
457 <<
"DestroyNotify\n";
461 x11display_cat.warning()
462 <<
"unhandled X event type " <<
event.type <<
"\n";
466 if (got_configure_event) {
468 _awaiting_configure =
false;
475 properties.
set_origin(configure_event.x, configure_event.y);
476 properties.
set_size(configure_event.width, configure_event.height);
478 if (_properties.get_fixed_size()) {
484 if (configure_event.width != _fixed_size.get_x() ||
485 configure_event.height != _fixed_size.get_y()) {
486 XWindowChanges changes;
487 changes.width = _fixed_size.get_x();
488 changes.height = _fixed_size.get_y();
489 int value_mask = (CWWidth | CWHeight);
490 XConfigureWindow(_display, _xwindow, value_mask, &changes);
496 if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
497 X11_Cursor cursor = None;
498 if (_properties.get_cursor_hidden()) {
500 DCAST_INTO_V(x11_pipe, _pipe);
504 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
505 _xwindow, cursor, CurrentTime);
508 changed_properties =
true;
512 _properties.get_mouse_mode() == WindowProperties::M_confined ||
513 _dga_mouse_enabled)) {
515 DCAST_INTO_V(x11_pipe, _pipe);
520 X11_Cursor cursor = None;
521 if (_properties.get_cursor_hidden()) {
525 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
526 _xwindow, cursor, CurrentTime);
527 if (_dga_mouse_enabled) {
533 if (_dga_mouse_enabled) {
535 }
else if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
536 XUngrabPointer(_display, CurrentTime);
541 if (changed_properties) {
542 system_changed_properties(properties);
545 if (got_keyrelease_event) {
548 handle_keyrelease(keyrelease_event);
566 if (_pipe ==
nullptr) {
573 DCAST_INTO_V(x11_pipe, _pipe);
579 bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen();
583 if (want_fullscreen) {
584 if (x11_pipe->_have_xrandr) {
585 XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->
get_root());
586 SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
587 SizeID new_size_id = (SizeID) -1;
588 int num_sizes = 0, reqsizex, reqsizey;
593 reqsizex = _properties.get_x_size();
594 reqsizey = _properties.get_y_size();
597 xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
598 for (
int i = 0; i < num_sizes; ++i) {
599 if (xrrs[i].width == reqsizex &&
600 xrrs[i].height == reqsizey) {
604 if (new_size_id == (SizeID) -1) {
605 x11display_cat.error()
606 <<
"Videocard has no supported display resolutions at specified res (" 607 << reqsizex <<
" x " << reqsizey <<
")\n";
609 if (new_size_id != old_size_id) {
611 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
612 if (_orig_size_id == (SizeID) -1) {
614 _orig_size_id = old_size_id;
627 if (_orig_size_id != (SizeID) -1) {
628 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->
get_root());
629 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), _orig_size_id, _orig_rotation, CurrentTime);
630 _orig_size_id = (SizeID) -1;
633 if (!properties.
has_origin() && _properties.has_origin()) {
634 properties.
set_origin(_properties.get_x_origin(), _properties.get_y_origin());
645 if (x_origin == -2) {
648 if (y_origin == -2) {
652 if (x_origin == -2) {
655 if (y_origin == -2) {
673 set_wm_properties(properties,
true);
678 _properties.set_title(properties.
get_title());
692 XWindowChanges changes;
695 if (_properties.get_fullscreen()) {
698 value_mask |= CWX | CWY;
704 if (changes.x != -1) value_mask |= CWX;
705 if (changes.y != -1) value_mask |= CWY;
713 _fixed_size = _properties.get_size();
719 value_mask |= (CWWidth | CWHeight);
721 if (_properties.get_fixed_size()) {
722 _fixed_size = properties.
get_size();
733 case WindowProperties::Z_bottom:
734 changes.stack_mode = Below;
737 case WindowProperties::Z_normal:
738 changes.stack_mode = TopIf;
741 case WindowProperties::Z_top:
742 changes.stack_mode = Above;
746 value_mask |= (CWStackMode);
760 _properties.set_cursor_filename(cursor_filename);
764 _properties.set_cursor_filename(filename);
766 if (_properties.get_cursor_hidden()) {
769 }
else if (!cursor_filename.empty()) {
771 X11_Cursor cursor = get_cursor(cursor_filename);
772 XDefineCursor(_display, _xwindow, cursor);
775 XDefineCursor(_display, _xwindow, None);
779 if (!properties.has_mouse_mode() &&
780 _properties.get_mouse_mode() != WindowProperties::M_absolute) {
787 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
789 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
794 if (properties.has_mouse_mode()) {
796 case WindowProperties::M_absolute:
797 XUngrabPointer(_display, CurrentTime);
798 if (_dga_mouse_enabled) {
800 _dga_mouse_enabled =
false;
802 _properties.set_mouse_mode(WindowProperties::M_absolute);
806 case WindowProperties::M_relative:
807 if (!_dga_mouse_enabled) {
809 X11_Cursor cursor = None;
810 if (_properties.get_cursor_hidden()) {
812 DCAST_INTO_V(x11_pipe, _pipe);
816 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
817 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
818 x11display_cat.error() <<
"Failed to grab pointer!\n";
822 _properties.set_mouse_mode(WindowProperties::M_relative);
824 _dga_mouse_enabled =
true;
829 XQueryPointer(_display, _xwindow, &event.xbutton.root,
830 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
831 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
835 x11display_cat.info()
836 <<
"XF86DGA extension not available, cannot enable relative mouse mode\n";
837 _dga_mouse_enabled =
false;
842 case WindowProperties::M_confined:
845 DCAST_INTO_V(x11_pipe, _pipe);
847 if (_dga_mouse_enabled) {
849 _dga_mouse_enabled =
false;
851 X11_Cursor cursor = None;
852 if (_properties.get_cursor_hidden()) {
856 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
857 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
858 x11display_cat.error() <<
"Failed to grab pointer!\n";
860 _properties.set_mouse_mode(WindowProperties::M_confined);
868 if (value_mask != 0) {
871 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
874 _awaiting_configure =
true;
881 void x11GraphicsWindow::
882 mouse_mode_absolute() {
889 void x11GraphicsWindow::
890 mouse_mode_relative() {
897 void x11GraphicsWindow::
899 if (_gsg !=
nullptr) {
904 if (_ic != (XIC)
nullptr) {
909 if (_xwindow != (X11_Window)
nullptr) {
910 XDestroyWindow(_display, _xwindow);
911 _xwindow = (X11_Window)
nullptr;
920 if (_orig_size_id != (SizeID) -1) {
922 if (_pipe !=
nullptr) {
924 DCAST_INTO_V(x11_pipe, _pipe);
929 root = RootWindow(_display, _screen);
931 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
932 _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
936 GraphicsWindow::close_window();
943 bool x11GraphicsWindow::
945 if (_visual_info ==
nullptr) {
947 x11display_cat.error()
948 <<
"No X visual: cannot open window.\n";
953 DCAST_INTO_R(x11_pipe, _pipe,
false);
955 if (!_properties.has_origin()) {
956 _properties.set_origin(0, 0);
958 if (!_properties.has_size()) {
959 _properties.set_size(100, 100);
965 if (_properties.get_fullscreen() && x11_pipe->_have_xrandr) {
966 XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->
get_root());
967 if (_orig_size_id == (SizeID) -1) {
968 _orig_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
970 int num_sizes, new_size_id = -1;
972 xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
973 for (
int i = 0; i < num_sizes; ++i) {
974 if (xrrs[i].width == _properties.get_x_size() &&
975 xrrs[i].height == _properties.get_y_size()) {
979 if (new_size_id == -1) {
980 x11display_cat.error()
981 <<
"Videocard has no supported display resolutions at specified res (" 982 << _properties.get_x_size() <<
" x " << _properties.get_y_size() <<
")\n";
986 if (new_size_id != _orig_size_id) {
987 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
993 X11_Window parent_window = x11_pipe->
get_root();
994 WindowHandle *window_handle = _properties.get_parent_window();
995 if (window_handle !=
nullptr) {
996 x11display_cat.info()
997 <<
"Got parent_window " << *window_handle <<
"\n";
999 if (os_handle !=
nullptr) {
1000 x11display_cat.info()
1001 <<
"os_handle type " << os_handle->get_type() <<
"\n";
1003 if (os_handle->
is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1004 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1005 parent_window = x11_handle->get_handle();
1006 }
else if (os_handle->
is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1008 parent_window = (X11_Window)int_handle->get_handle();
1012 _parent_window_handle = window_handle;
1015 ButtonPressMask | ButtonReleaseMask |
1016 KeyPressMask | KeyReleaseMask |
1017 EnterWindowMask | LeaveWindowMask |
1019 FocusChangeMask | StructureNotifyMask;
1022 XSetWindowAttributes wa;
1023 wa.background_pixel = XBlackPixel(_display, _screen);
1024 wa.border_pixel = 0;
1025 wa.colormap = _colormap;
1026 wa.event_mask = _event_mask;
1027 wa.override_redirect = _override_redirect;
1029 unsigned long attrib_mask =
1030 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1032 _xwindow = XCreateWindow
1033 (_display, parent_window,
1034 _properties.get_x_origin(), _properties.get_y_origin(),
1035 _properties.get_x_size(), _properties.get_y_size(),
1036 0, _visual_info->depth, InputOutput,
1037 _visual_info->visual, attrib_mask, &wa);
1039 if (_xwindow == (X11_Window)0) {
1040 x11display_cat.error()
1041 <<
"failed to create X window.\n";
1045 if (_properties.get_fixed_size()) {
1046 _fixed_size = _properties.get_size();
1049 set_wm_properties(_properties,
false);
1055 XIM im = x11_pipe->
get_im();
1060 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1062 if (_ic == (XIC)
nullptr) {
1063 x11display_cat.warning()
1064 <<
"Couldn't create input context.\n";
1068 if (_properties.get_cursor_hidden()) {
1071 }
else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1073 X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1074 XDefineCursor(_display, _xwindow, cursor);
1077 XMapWindow(_display, _xwindow);
1079 if (_properties.get_raw_mice()) {
1082 if (x11display_cat.is_debug()) {
1083 x11display_cat.debug()
1084 <<
"Raw mice not requested.\n";
1089 _window_handle = NativeWindowHandle::make_x11(_xwindow);
1092 if (_parent_window_handle !=
nullptr) {
1093 _parent_window_handle->attach_child(_window_handle);
1111 void x11GraphicsWindow::
1112 set_wm_properties(
const WindowProperties &properties,
bool already_mapped) {
1114 DCAST_INTO_V(x11_pipe, _pipe);
1117 XTextProperty window_name;
1118 XTextProperty *window_name_p =
nullptr;
1120 const char *name = properties.
get_title().c_str();
1121 if (XStringListToTextProperty((
char **)&name, 1, &window_name) != 0) {
1122 window_name_p = &window_name;
1128 XSizeHints *size_hints_p =
nullptr;
1130 size_hints_p = XAllocSizeHints();
1131 if (size_hints_p !=
nullptr) {
1133 if (_properties.get_fullscreen()) {
1134 size_hints_p->x = 0;
1135 size_hints_p->y = 0;
1140 size_hints_p->flags |= USPosition;
1142 LVecBase2i size = _properties.get_size();
1145 size_hints_p->width = size.get_x();
1146 size_hints_p->height = size.get_y();
1147 size_hints_p->flags |= USSize;
1150 size_hints_p->min_width = size.get_x();
1151 size_hints_p->min_height = size.get_y();
1152 size_hints_p->max_width = size.get_x();
1153 size_hints_p->max_height = size.get_y();
1154 size_hints_p->flags |= (PMinSize | PMaxSize);
1161 XWMHints *wm_hints_p =
nullptr;
1162 wm_hints_p = XAllocWMHints();
1163 if (wm_hints_p !=
nullptr) {
1165 wm_hints_p->initial_state = IconicState;
1167 wm_hints_p->initial_state = NormalState;
1169 wm_hints_p->flags = StateHint;
1175 static const int max_type_data = 32;
1176 int32_t type_data[max_type_data];
1177 int next_type_data = 0;
1179 static const int max_state_data = 32;
1180 int32_t state_data[max_state_data];
1181 int next_state_data = 0;
1183 static const int max_set_data = 32;
1186 inline SetAction() { }
1187 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1191 SetAction set_data[max_set_data];
1192 int next_set_data = 0;
1198 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1201 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1204 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1207 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1220 XClassHint *class_hints_p =
nullptr;
1221 if (!x_wm_class.empty()) {
1223 class_hints_p = XAllocClassHint();
1224 class_hints_p->res_class = (
char*) x_wm_class.c_str();
1225 if (!x_wm_class_name.empty()) {
1226 class_hints_p->res_name = (
char*) x_wm_class_name.c_str();
1230 class_hints_p = XAllocClassHint();
1231 class_hints_p->res_class = (
char*)
"Undecorated";
1235 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1240 case WindowProperties::Z_bottom:
1241 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1242 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1243 x11_pipe->_net_wm_state_add);
1244 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1245 x11_pipe->_net_wm_state_remove);
1248 case WindowProperties::Z_normal:
1249 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1250 x11_pipe->_net_wm_state_remove);
1251 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1252 x11_pipe->_net_wm_state_remove);
1255 case WindowProperties::Z_top:
1256 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1257 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1258 x11_pipe->_net_wm_state_remove);
1259 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1260 x11_pipe->_net_wm_state_add);
1265 nassertv(next_type_data < max_type_data);
1266 nassertv(next_state_data < max_state_data);
1267 nassertv(next_set_data < max_set_data);
1270 int32_t pid = getpid();
1271 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1272 XA_CARDINAL, 32, PropModeReplace,
1273 (
unsigned char *)&pid, 1);
1278 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1279 XA_CARDINAL, 32, PropModeReplace,
1280 (
unsigned char *)&compositor, 1);
1283 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1284 XA_ATOM, 32, PropModeReplace,
1285 (
unsigned char *)type_data, next_type_data);
1288 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1289 XA_ATOM, 32, PropModeReplace,
1290 (
unsigned char *)state_data, next_state_data);
1292 if (already_mapped) {
1298 DCAST_INTO_V(x11_pipe, _pipe);
1300 for (
int i = 0; i < next_set_data; ++i) {
1301 XClientMessageEvent event;
1302 memset(&event, 0,
sizeof(event));
1303 event.type = ClientMessage;
1304 event.send_event = True;
1305 event.display = _display;
1306 event.window = _xwindow;
1307 event.message_type = x11_pipe->_net_wm_state;
1309 event.data.l[0] = set_data[i]._action;
1310 event.data.l[1] = set_data[i]._state;
1311 event.data.l[2] = 0;
1312 event.data.l[3] = 1;
1314 XSendEvent(_display, x11_pipe->
get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1318 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1319 nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1321 if (size_hints_p !=
nullptr) {
1322 XFree(size_hints_p);
1324 if (wm_hints_p !=
nullptr) {
1327 if (class_hints_p !=
nullptr) {
1328 XFree(class_hints_p);
1334 Atom protocols[] = {
1338 XSetWMProtocols(_display, _xwindow, protocols,
1339 sizeof(protocols) /
sizeof(Atom));
1346 void x11GraphicsWindow::
1347 setup_colormap(XVisualInfo *visual) {
1349 DCAST_INTO_V(x11_pipe, _pipe);
1350 X11_Window root_window = x11_pipe->
get_root();
1352 _colormap = XCreateColormap(_display, root_window,
1353 visual->visual, AllocNone);
1360 void x11GraphicsWindow::
1362 #ifdef PHAVE_LINUX_INPUT_H 1363 bool any_present =
false;
1364 bool any_mice =
false;
1366 for (
int i=0; i<64; i++) {
1368 fnb <<
"/dev/input/event" << i;
1369 string fn = fnb.str();
1370 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1372 EvdevInputDevice *device =
new EvdevInputDevice(
nullptr, fd);
1373 nassertd(device != NULL)
continue;
1375 if (device->has_pointer()) {
1376 add_input_device(device);
1378 x11display_cat.info()
1379 <<
"Raw mouse " << _input_devices.size()
1380 <<
" detected: " << device->get_name() <<
"\n";
1386 if (errno == ENOENT || errno == ENOTDIR) {
1390 x11display_cat.error()
1391 <<
"Opening raw mice: " << strerror(errno) <<
" " << fn <<
"\n";
1397 _properties.set_raw_mice(
true);
1399 }
else if (!any_present) {
1400 x11display_cat.error() <<
1401 "Opening raw mice: files not found: /dev/input/event*\n";
1404 x11display_cat.error() <<
1405 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1408 x11display_cat.error() <<
1409 "Opening raw mice: panda not compiled with raw mouse support.\n";
1416 void x11GraphicsWindow::
1417 handle_keystroke(XKeyEvent &event) {
1418 if (!_dga_mouse_enabled) {
1424 static const int buffer_size = 256;
1425 wchar_t buffer[buffer_size];
1427 int len = XwcLookupString(_ic, &event, buffer, buffer_size,
nullptr,
1429 if (status == XBufferOverflow) {
1430 x11display_cat.error()
1431 <<
"Overflowed input buffer.\n";
1435 for (
int i = 0; i < len; i++) {
1451 void x11GraphicsWindow::
1452 handle_keypress(XKeyEvent &event) {
1453 if (!_dga_mouse_enabled) {
1459 if (button != ButtonHandle::none()) {
1460 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1463 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1466 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1469 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1475 if (event.keycode >= 9 && event.keycode <= 135) {
1476 ButtonHandle raw_button = map_raw_button(event.keycode);
1477 if (raw_button != ButtonHandle::none()) {
1486 void x11GraphicsWindow::
1487 handle_keyrelease(XKeyEvent &event) {
1488 if (!_dga_mouse_enabled) {
1494 if (button != ButtonHandle::none()) {
1495 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1496 _input->
button_up(KeyboardButton::control());
1498 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1499 _input->
button_up(KeyboardButton::shift());
1501 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1502 _input->
button_up(KeyboardButton::alt());
1504 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1505 _input->
button_up(KeyboardButton::meta());
1510 if (event.keycode >= 9 && event.keycode <= 135) {
1511 ButtonHandle raw_button = map_raw_button(event.keycode);
1512 if (raw_button != ButtonHandle::none()) {
1523 get_button(XKeyEvent &key_event,
bool allow_shift) {
1524 KeySym key = XLookupKeysym(&key_event, 0);
1526 if ((key_event.state & Mod2Mask) != 0) {
1541 case XK_KP_Multiply:
1543 case XK_KP_Separator:
1544 case XK_KP_Subtract:
1567 k2 = XLookupKeysym(&key_event, 1);
1568 button = map_button(k2);
1569 if (button != ButtonHandle::none()) {
1583 if ((key_event.state & ShiftMask) != 0) {
1584 KeySym k2 = XLookupKeysym(&key_event, 1);
1586 if (button != ButtonHandle::none()) {
1594 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1595 if (key >= XK_a && key <= XK_z) {
1596 key += (XK_A - XK_a);
1601 return map_button(key);
1609 map_button(KeySym key)
const {
1612 return ButtonHandle::none();
1614 return KeyboardButton::backspace();
1617 return KeyboardButton::tab();
1620 return KeyboardButton::enter();
1622 return KeyboardButton::escape();
1625 return KeyboardButton::space();
1646 case XK_KP_Multiply:
1652 case XK_KP_Separator:
1655 case XK_KP_Subtract:
1760 case XK_bracketleft:
1764 case XK_bracketright:
1766 case XK_asciicircum:
1835 return KeyboardButton::f1();
1838 return KeyboardButton::f2();
1841 return KeyboardButton::f3();
1844 return KeyboardButton::f4();
1846 return KeyboardButton::f5();
1848 return KeyboardButton::f6();
1850 return KeyboardButton::f7();
1852 return KeyboardButton::f8();
1854 return KeyboardButton::f9();
1856 return KeyboardButton::f10();
1858 return KeyboardButton::f11();
1860 return KeyboardButton::f12();
1863 return KeyboardButton::left();
1866 return KeyboardButton::up();
1869 return KeyboardButton::right();
1872 return KeyboardButton::down();
1875 return KeyboardButton::page_up();
1878 return KeyboardButton::page_down();
1881 return KeyboardButton::home();
1884 return KeyboardButton::end();
1887 return KeyboardButton::insert();
1890 return KeyboardButton::del();
1892 return KeyboardButton::num_lock();
1893 case XK_Scroll_Lock:
1894 return KeyboardButton::scroll_lock();
1896 return KeyboardButton::print_screen();
1898 return KeyboardButton::pause();
1900 return KeyboardButton::menu();
1902 return KeyboardButton::lshift();
1904 return KeyboardButton::rshift();
1906 return KeyboardButton::lcontrol();
1908 return KeyboardButton::rcontrol();
1910 return KeyboardButton::lalt();
1912 return KeyboardButton::ralt();
1915 return KeyboardButton::lmeta();
1918 return KeyboardButton::rmeta();
1920 return KeyboardButton::caps_lock();
1922 return KeyboardButton::shift_lock();
1924 if (x11display_cat.is_debug()) {
1925 x11display_cat.debug()
1926 <<
"Unrecognized keysym 0x" << std::hex << key << std::dec <<
"\n";
1928 return ButtonHandle::none();
1935 map_raw_button(KeyCode key)
const {
1936 #ifdef PHAVE_LINUX_INPUT_H 1941 int index = key - 8;
1943 return EvdevInputDevice::map_button(index);
1946 return ButtonHandle::none();
1954 get_mouse_button(XButtonEvent &button_event) {
1955 int index = button_event.button;
1956 if (index == x_wheel_up_button) {
1958 }
else if (index == x_wheel_down_button) {
1960 }
else if (index == x_wheel_left_button) {
1962 }
else if (index == x_wheel_right_button) {
1974 get_keyboard_map()
const {
1979 for (
int k = 9; k <= 135; ++k) {
1981 if (raw_button == ButtonHandle::none()) {
1985 KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
1987 if (button == ButtonHandle::none()) {
2001 Bool x11GraphicsWindow::
2002 check_event(X11_Display *display, XEvent *event,
char *arg) {
2006 return (event->xany.window == self->_xwindow);
2013 X11_Cursor x11GraphicsWindow::
2014 get_cursor(
const Filename &filename) {
2016 DCAST_INTO_R(x11_pipe, _pipe, None);
2018 if (x11_pipe->_xcursor_size == -1) {
2019 x11display_cat.info()
2020 <<
"libXcursor.so.1 not available; cannot change mouse cursor.\n";
2026 if (fi != _cursor_filenames.end()) {
2035 x11display_cat.warning()
2036 <<
"Could not find cursor filename " << filename <<
"\n";
2039 fi = _cursor_filenames.find(resolved);
2040 if (fi != _cursor_filenames.end()) {
2046 if (str ==
nullptr) {
2047 x11display_cat.warning()
2048 <<
"Could not open cursor file " << filename <<
"\n";
2054 str->read(magic, 4);
2056 x11display_cat.warning()
2057 <<
"Could not read from cursor file " << filename <<
"\n";
2063 str->putback(magic[3]);
2064 str->putback(magic[2]);
2065 str->putback(magic[1]);
2066 str->putback(magic[0]);
2068 X11_Cursor h = None;
2069 if (memcmp(magic,
"Xcur", 4) == 0) {
2071 x11display_cat.debug()
2072 <<
"Loading X11 cursor " << filename <<
"\n";
2074 xcfile.closure = str;
2075 xcfile.read = &xcursor_read;
2076 xcfile.write = &xcursor_write;
2077 xcfile.seek = &xcursor_seek;
2079 XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2080 if (images !=
nullptr) {
2081 h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2082 x11_pipe->_XcursorImagesDestroy(images);
2085 }
else if (memcmp(magic,
"\0\0\1\0", 4) == 0
2086 || memcmp(magic,
"\0\0\2\0", 4) == 0) {
2088 x11display_cat.debug()
2089 <<
"Loading Windows cursor " << filename <<
"\n";
2097 x11display_cat.warning()
2098 <<
"X11 cursor filename '" << resolved <<
"' could not be loaded!\n";
2101 _cursor_filenames[resolved] = h;
2109 X11_Cursor x11GraphicsWindow::
2110 read_ico(istream &ico) {
2112 DCAST_INTO_R(x11_pipe, _pipe, None);
2116 uint16_t reserved, type, count;
2120 uint8_t width, height, colorCount, reserved;
2121 uint16_t xhot, yhot;
2122 uint32_t bitmapSize, offset;
2126 uint32_t headerSize, width, height;
2127 uint16_t planes, bitsPerPixel;
2128 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2132 uint8_t blue, green, red, reserved;
2136 unsigned int j, k, mask, shift;
2137 size_t colorCount, bitsPerPixel;
2139 IcoInfoHeader infoHeader;
2140 IcoEntry *entries =
nullptr;
2141 IcoColor color, *palette =
nullptr;
2143 size_t xorBmpSize, andBmpSize;
2144 char *curXor, *curAnd;
2145 char *xorBmp =
nullptr, *andBmp =
nullptr;
2146 XcursorImage *image =
nullptr;
2147 X11_Cursor ret = None;
2149 int def_size = x11_pipe->_xcursor_size;
2152 ico.read(reinterpret_cast<char *>(&header),
sizeof(IcoHeader));
2153 if (!ico.good())
goto cleanup;
2154 if (header.type != 1 && header.type != 2)
goto cleanup;
2155 if (header.count < 1)
goto cleanup;
2158 entries =
new IcoEntry[header.count];
2159 ico.read(reinterpret_cast<char *>(entries), header.count *
sizeof(IcoEntry));
2160 if (!ico.good())
goto cleanup;
2161 for (i = 1; i < header.count; i++) {
2162 if (entries[i].width == def_size && entries[i].height == def_size) {
2167 if (entries[i].width > entries[entry].width ||
2168 entries[i].height > entries[entry].height)
2173 ico.seekg(entries[entry].offset);
2174 if (!ico.good())
goto cleanup;
2176 if (ico.peek() == 0x89) {
2190 unsigned int *dest = image->pixels;
2192 if (alpha !=
nullptr) {
2193 for (
size_t p = 0; p < num_pixels; ++p) {
2194 *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2199 for (
size_t p = 0; p < num_pixels; ++p) {
2200 *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2206 ico.read(reinterpret_cast<char *>(&infoHeader),
sizeof(IcoInfoHeader));
2207 if (!ico.good())
goto cleanup;
2208 bitsPerPixel = infoHeader.bitsPerPixel;
2210 if (infoHeader.compression != 0)
goto cleanup;
2213 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2214 colorCount = 1 << bitsPerPixel;
2215 palette =
new IcoColor[colorCount];
2216 ico.read(reinterpret_cast<char *>(palette), colorCount *
sizeof(IcoColor));
2217 if (!ico.good())
goto cleanup;
2220 int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2223 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2224 andBmpSize = and_stride * (infoHeader.height / 2);
2225 curXor = xorBmp =
new char[xorBmpSize];
2226 curAnd = andBmp =
new char[andBmpSize];
2227 ico.read(xorBmp, xorBmpSize);
2228 if (!ico.good())
goto cleanup;
2229 ico.read(andBmp, andBmpSize);
2230 if (!ico.good())
goto cleanup;
2232 image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2235 switch (bitsPerPixel) {
2241 mask = ((1 << bitsPerPixel) - 1);
2242 for (i = image->height - 1; i >= 0; i--) {
2243 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2244 for (k = 0; k < 8 / bitsPerPixel; k++) {
2245 shift = 8 - ((k + 1) * bitsPerPixel);
2246 color = palette[(*curXor & (mask << shift)) >> shift];
2247 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2248 (color.green << 8) +
2256 for (j = 0; j < image->width; j += 8) {
2257 for (k = 0; k < 8; k++) {
2259 image->pixels[(i * image->width) + j + k] |=
2260 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2270 for (i = image->height - 1; i >= 0; i--) {
2271 for (j = 0; j < image->width; j++) {
2272 shift = 7 - (j & 0x7);
2273 uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2274 image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2275 | ((uint8_t)curXor[1] << 8u)
2276 | ((uint8_t)curXor[2] << 16u)
2280 curAnd += and_stride;
2286 for (i = image->height - 1; i >= 0; i--) {
2287 for (j = 0; j < image->width; j++) {
2288 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2289 (*(curXor + 2) << 16) +
2290 (*(curXor + 1) << 8) +
2303 if (header.type == 2) {
2304 image->xhot = entries[entry].xhot;
2305 image->yhot = entries[entry].yhot;
2311 ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2314 x11_pipe->_XcursorImageDestroy(image);
has_cursor_filename
Returns true if set_cursor_filename() has been specified.
set_mouse_mode
Specifies the mode in which the window is to operate its mouse pointer.
bool is_any_specified() const
Returns true if any properties have been specified, false otherwise.
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
clear_z_order
Removes the z_order specification from the properties.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
get_mouse_mode
See set_mouse_mode().
has_minimized
Returns true if set_minimized() has been specified.
XIM get_im() const
Returns the input method opened for the pipe, or NULL if the input method could not be opened for som...
virtual bool move_pointer(int device, int x, int y)
Forces the pointer to the indicated position within the window, if possible.
get_display_height
Returns the height of the entire display, if it is known.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_in_window
If this returns false, the pointer is not currently present in the window and the values returned by ...
This object represents a window on the desktop, not necessarily a Panda window.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_fixed_size
Returns true if the window cannot be resized by the user, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
set_size
Specifies the requested size of the window, in pixels.
get_cursor_hidden
Returns true if the mouse cursor is invisible.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
BEGIN_PUBLISH typedef PointerData MouseData
Deprecated alias for PointerData.
get_foreground
Returns true if the window is in the foreground.
clear_cursor_filename
Removes the cursor_filename specification from the properties.
A hierarchy of directories and files that appears to be one continuous file system,...
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,...
set_minimized
Specifies whether the window should be created minimized (true), or normal (false).
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_cursor_filename
Returns the icon filename associated with the mouse cursor.
bool read(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Reads the indicated image filename.
clear_fixed_size
Removes the fixed_size specification from the properties.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
xel * get_array()
Directly access the underlying PNMImage array.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
clear_origin
Removes the origin specification from the properties.
get_os_handle
Returns the OS-specific handle stored internally to the WindowHandle wrapper.
int get_y_origin() const
Returns the y coordinate of the window's top-left corner, not including decorations.
void release() const
Releases the lightReMutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_title
Returns the window's title.
A window, fullscreen or on a desktop, into which a graphics device sends its output for interactive d...
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
has_fullscreen
Returns true if set_fullscreen() has been specified.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
get_close_request_event
Returns the name of the event set via set_close_request_event().
has_z_order
Returns true if the window z_order has been specified, false otherwise.
A container for the various kinds of properties we might ask to have on a graphics window before we o...
virtual void clear(Thread *current_thread)
Clears the entire framebuffer before rendering, according to the settings of get_color_clear_active()...
get_undecorated
Returns true if the window has no border.
has_origin
Returns true if the window origin has been specified, false otherwise.
has_fixed_size
Returns true if set_fixed_size() has been specified.
void disable_relative_mouse()
Disables relative mouse mode for this display.
The name of a file, such as a texture file or an Egg file.
int get_screen() const
Returns the X screen number associated with the pipe.
set_origin
Specifies the origin on the screen (in pixels, relative to the top-left corner) at which the window s...
Similar to MutexHolder, but for a light mutex.
An object to create GraphicsOutputs that share a particular 3-D API.
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
clear_mouse_mode
Removes the mouse_mode specification from the properties.
X11_Cursor get_hidden_cursor()
Returns an invisible Cursor suitable for assigning to windows that have the cursor_hidden property se...
X11_Display * get_display() const
Returns a pointer to the X display associated with the pipe: the display on which to create the windo...
clear_cursor_hidden
Removes the cursor_hidden specification from the properties.
get_real_time
Returns the actual number of seconds elapsed since the ClockObject was created, or since it was last ...
virtual MouseData get_pointer(int device) const
Returns the MouseData associated with the nth input device's pointer.
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
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.
get_minimized
Returns true if the window is minimized.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_size
Returns size in pixels of the useful part of the window, not including decorations.
virtual void set_properties_now(WindowProperties &properties)
Applies the requested set of properties to the window, if possible, for instance to request a change ...
clear_fullscreen
Removes the fullscreen specification from the properties.
This class maintains the set of all known PNMFileTypes in the universe.
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,...
has_cursor_hidden
Returns true if set_cursor_hidden() has been specified.
A thread; that is, a lightweight process.
get_display_width
Returns the width of the entire display, if it is known.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Encapsulates all the communication with a particular instance of a given rendering backend.
has_title
Returns true if the window title has been specified, false otherwise.
bool try_lock()
Alias for try_acquire() to match C++11 semantics.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
This class is the main interface to controlling the render process.
int get_y_size() const
Returns size in pixels in the y dimension of the useful part of the window, not including decorations...
void set_maxval(xelval maxval)
Rescales the image to the indicated maxval.
int get_x_size() const
Returns size in pixels in the x dimension of the useful part of the window, not including decorations...
get_fullscreen
Returns true if the window is in fullscreen mode.
TypeHandle is the identifier used to differentiate C++ class types.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
clear_foreground
Removes the foreground specification from the properties.
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.
clear_title
Removes the title specification from the properties.
bool is_fullscreen() const
Returns true if the window has been opened as a fullscreen window, false otherwise.
bool supports_relative_mouse() const
Returns true if relative mouse mode is supported on this display.
set_open
Specifies whether the window should be open.
int get_x_origin() const
Returns the x coordinate of the window's top-left corner, not including decorations.
get_z_order
Returns the window's z_order.
clear_size
Removes the size specification from the properties.
bool enable_relative_mouse()
Enables relative mouse mode for this display.
has_size
Returns true if the window size has been specified, false otherwise.
has_foreground
Returns true if set_foreground() has been specified.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual void process_events()
Do whatever processing is necessary to ensure that the window responds to user events.
Interfaces to the X11 window system.
set_foreground
Specifies whether the window should be opened in the foreground (true), or left in the background (fa...
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.
const FrameBufferProperties & get_fb_properties() const
Returns the framebuffer properties of the window.