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 (got_keyrelease_event) {
317 got_keyrelease_event =
false;
319 if (event.type == KeyPress &&
320 event.xkey.keycode == keyrelease_event.keycode &&
321 (event.xkey.time - keyrelease_event.time <= 1)) {
322 if (!XFilterEvent(&event, None)) {
325 handle_keystroke(event.xkey);
329 handle_keypress(event.xkey);
336 ButtonHandle raw_button = map_raw_button(keyrelease_event.keycode);
337 if (raw_button != ButtonHandle::none()) {
341 handle_keyrelease(keyrelease_event);
347 if (event.type == KeyPress) {
348 ButtonHandle raw_button = map_raw_button(event.xkey.keycode);
349 if (raw_button != ButtonHandle::none()) {
354 if (XFilterEvent(&event, None)) {
360 switch (event.type) {
364 case ConfigureNotify:
367 configure_event =
event.xconfigure;
368 got_configure_event =
true;
373 button = get_mouse_button(event.xbutton);
374 if (!_dga_mouse_enabled) {
381 button = get_mouse_button(event.xbutton);
382 if (!_dga_mouse_enabled) {
389 if (_dga_mouse_enabled) {
398 handle_keystroke(event.xkey);
399 handle_keypress(event.xkey);
406 keyrelease_event =
event.xkey;
407 got_keyrelease_event =
true;
411 if (_dga_mouse_enabled) {
425 changed_properties =
true;
431 changed_properties =
true;
436 changed_properties =
true;
441 changed_properties =
true;
444 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
448 if ((Atom)(event.xclient.data.l[0]) == _wm_delete_window) {
452 if (!close_request_event.empty()) {
455 throw_event(close_request_event);
464 system_changed_properties(properties);
472 x11display_cat.info()
473 <<
"DestroyNotify\n";
477 x11display_cat.warning()
478 <<
"unhandled X event type " <<
event.type <<
"\n";
482 if (got_configure_event) {
484 _awaiting_configure =
false;
491 properties.
set_origin(configure_event.x, configure_event.y);
492 properties.
set_size(configure_event.width, configure_event.height);
494 if (_properties.get_fixed_size()) {
500 if (configure_event.width != _fixed_size.get_x() ||
501 configure_event.height != _fixed_size.get_y()) {
502 XWindowChanges changes;
503 changes.width = _fixed_size.get_x();
504 changes.height = _fixed_size.get_y();
505 int value_mask = (CWWidth | CWHeight);
506 XConfigureWindow(_display, _xwindow, value_mask, &changes);
512 if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
513 X11_Cursor cursor = None;
514 if (_properties.get_cursor_hidden()) {
516 DCAST_INTO_V(x11_pipe, _pipe);
520 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
521 _xwindow, cursor, CurrentTime);
524 changed_properties =
true;
528 _properties.get_mouse_mode() == WindowProperties::M_confined ||
529 _dga_mouse_enabled)) {
531 DCAST_INTO_V(x11_pipe, _pipe);
536 X11_Cursor cursor = None;
537 if (_properties.get_cursor_hidden()) {
541 XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync, GrabModeAsync,
542 _xwindow, cursor, CurrentTime);
543 if (_dga_mouse_enabled) {
549 if (_dga_mouse_enabled) {
551 }
else if (_properties.get_mouse_mode() == WindowProperties::M_confined) {
552 XUngrabPointer(_display, CurrentTime);
557 if (changed_properties) {
558 system_changed_properties(properties);
561 if (got_keyrelease_event) {
564 ButtonHandle raw_button = map_raw_button(keyrelease_event.keycode);
565 if (raw_button != ButtonHandle::none()) {
569 handle_keyrelease(keyrelease_event);
587 if (_pipe ==
nullptr) {
594 DCAST_INTO_V(x11_pipe, _pipe);
600 bool is_fullscreen = _properties.has_fullscreen() && _properties.get_fullscreen();
603 if (want_fullscreen && properties.
has_origin()) {
609 if (want_fullscreen) {
613 LPoint2i center(0, 0);
614 if (_properties.has_origin()) {
615 center = _properties.get_origin();
616 if (_properties.has_size()) {
617 center += _properties.get_size() / 2;
620 int x, y, width, height;
624 int reqsizex, reqsizey;
628 }
else if (_properties.has_size()) {
629 reqsizex = _properties.get_x_size();
630 reqsizey = _properties.get_y_size();
642 || (width == reqsizex && height == reqsizey)
643 || !x11_pipe->_have_xrandr) {
649 if (x11display_cat.is_debug()) {
650 x11display_cat.debug()
651 <<
"Setting window to fullscreen on CRTC "
652 << width <<
"x" << height <<
"+" << x <<
"+" << y <<
"\n";
658 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->
get_root());
659 SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
660 SizeID new_size_id = (SizeID) -1;
664 xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
665 for (
int i = 0; i < num_sizes; ++i) {
666 if (xrrs[i].width == reqsizex &&
667 xrrs[i].height == reqsizey) {
671 if (new_size_id == (SizeID) -1) {
672 x11display_cat.error()
673 <<
"Videocard has no supported display resolutions at specified res ("
674 << reqsizex <<
" x " << reqsizey <<
")\n";
680 if (x11display_cat.is_debug()) {
681 x11display_cat.debug()
682 <<
"Switching to fullscreen with resolution "
683 << reqsizex <<
"x" << reqsizey <<
"\n";
686 if (new_size_id != old_size_id) {
687 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
688 if (_orig_size_id == (SizeID) -1) {
690 _orig_size_id = old_size_id;
702 if (_orig_size_id != (SizeID) -1) {
703 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->
get_root());
704 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), _orig_size_id, _orig_rotation, CurrentTime);
705 _orig_size_id = (SizeID) -1;
708 if (!properties.
has_origin() && _properties.has_origin()) {
709 properties.
set_origin(_properties.get_x_origin(), _properties.get_y_origin());
720 if (x_origin == -2) {
723 if (y_origin == -2) {
727 if (x_origin == -2) {
730 if (y_origin == -2) {
748 set_wm_properties(properties,
true);
753 _properties.set_title(properties.
get_title());
767 XWindowChanges changes;
770 if (_properties.get_fullscreen()) {
773 value_mask |= CWX | CWY;
779 if (changes.x != -1) value_mask |= CWX;
780 if (changes.y != -1) value_mask |= CWY;
788 _fixed_size = _properties.get_size();
794 value_mask |= (CWWidth | CWHeight);
796 if (_properties.get_fixed_size()) {
797 _fixed_size = properties.
get_size();
808 case WindowProperties::Z_bottom:
809 changes.stack_mode = Below;
812 case WindowProperties::Z_normal:
813 changes.stack_mode = TopIf;
816 case WindowProperties::Z_top:
817 changes.stack_mode = Above;
821 value_mask |= (CWStackMode);
835 _properties.set_cursor_filename(cursor_filename);
839 _properties.set_cursor_filename(filename);
841 if (_properties.get_cursor_hidden()) {
844 }
else if (!cursor_filename.empty()) {
846 X11_Cursor cursor = get_cursor(cursor_filename);
847 XDefineCursor(_display, _xwindow, cursor);
850 XDefineCursor(_display, _xwindow, None);
854 if (!properties.has_mouse_mode() &&
855 _properties.get_mouse_mode() != WindowProperties::M_absolute) {
862 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
864 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
869 if (properties.has_mouse_mode()) {
871 case WindowProperties::M_absolute:
872 XUngrabPointer(_display, CurrentTime);
873 if (_dga_mouse_enabled) {
875 _dga_mouse_enabled =
false;
877 _properties.set_mouse_mode(WindowProperties::M_absolute);
881 case WindowProperties::M_relative:
882 if (!_dga_mouse_enabled) {
884 X11_Cursor cursor = None;
885 if (_properties.get_cursor_hidden()) {
887 DCAST_INTO_V(x11_pipe, _pipe);
891 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
892 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
893 x11display_cat.error() <<
"Failed to grab pointer!\n";
897 _properties.set_mouse_mode(WindowProperties::M_relative);
899 _dga_mouse_enabled =
true;
904 XQueryPointer(_display, _xwindow, &event.xbutton.root,
905 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
906 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
910 x11display_cat.warning()
911 <<
"XF86DGA extension not available, cannot enable relative mouse mode\n";
912 _dga_mouse_enabled =
false;
917 case WindowProperties::M_confined:
920 DCAST_INTO_V(x11_pipe, _pipe);
922 if (_dga_mouse_enabled) {
924 _dga_mouse_enabled =
false;
926 X11_Cursor cursor = None;
927 if (_properties.get_cursor_hidden()) {
931 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
932 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
933 x11display_cat.error() <<
"Failed to grab pointer!\n";
935 _properties.set_mouse_mode(WindowProperties::M_confined);
943 if (value_mask != 0) {
946 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
949 _awaiting_configure =
true;
956 void x11GraphicsWindow::
957 mouse_mode_absolute() {
964 void x11GraphicsWindow::
965 mouse_mode_relative() {
972 void x11GraphicsWindow::
974 if (_gsg !=
nullptr) {
979 if (_ic != (XIC)
nullptr) {
984 if (_xwindow != (X11_Window)
nullptr) {
985 XDestroyWindow(_display, _xwindow);
986 _xwindow = (X11_Window)
nullptr;
995 if (_orig_size_id != (SizeID) -1) {
997 if (_pipe !=
nullptr) {
999 DCAST_INTO_V(x11_pipe, _pipe);
1004 root = RootWindow(_display, _screen);
1006 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
1007 _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
1011 GraphicsWindow::close_window();
1018 bool x11GraphicsWindow::
1020 if (_visual_info ==
nullptr) {
1022 x11display_cat.error()
1023 <<
"No X visual: cannot open window.\n";
1028 DCAST_INTO_R(x11_pipe, _pipe,
false);
1030 if (!_properties.has_origin()) {
1031 _properties.set_origin(0, 0);
1033 if (!_properties.has_size()) {
1034 _properties.set_size(100, 100);
1040 X11_Window parent_window = x11_pipe->
get_root();
1041 WindowHandle *window_handle = _properties.get_parent_window();
1042 if (window_handle !=
nullptr) {
1043 x11display_cat.info()
1044 <<
"Got parent_window " << *window_handle <<
"\n";
1046 if (os_handle !=
nullptr) {
1047 x11display_cat.info()
1048 <<
"os_handle type " << os_handle->get_type() <<
"\n";
1050 if (os_handle->
is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1051 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1052 parent_window = x11_handle->get_handle();
1053 }
else if (os_handle->
is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1055 parent_window = (X11_Window)int_handle->get_handle();
1059 _parent_window_handle = window_handle;
1062 ButtonPressMask | ButtonReleaseMask |
1063 KeyPressMask | KeyReleaseMask |
1064 EnterWindowMask | LeaveWindowMask |
1066 FocusChangeMask | StructureNotifyMask;
1069 XSetWindowAttributes wa;
1070 wa.background_pixel = XBlackPixel(_display, _screen);
1071 wa.border_pixel = 0;
1072 wa.colormap = _colormap;
1073 wa.event_mask = _event_mask;
1074 wa.override_redirect = _override_redirect;
1076 unsigned long attrib_mask =
1077 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1079 _xwindow = XCreateWindow
1080 (_display, parent_window,
1081 _properties.get_x_origin(), _properties.get_y_origin(),
1082 _properties.get_x_size(), _properties.get_y_size(),
1083 0, _visual_info->depth, InputOutput,
1084 _visual_info->visual, attrib_mask, &wa);
1086 if (_xwindow == (X11_Window)0) {
1087 x11display_cat.error()
1088 <<
"failed to create X window.\n";
1092 if (_properties.get_fixed_size()) {
1093 _fixed_size = _properties.get_size();
1096 set_wm_properties(_properties,
false);
1102 XIM im = x11_pipe->
get_im();
1105 _ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1106 XNClientWindow, _xwindow,
nullptr);
1107 if (_ic == (XIC)
nullptr) {
1108 x11display_cat.warning()
1109 <<
"Couldn't create input context.\n";
1113 if (_properties.get_cursor_hidden()) {
1116 }
else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1118 X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1119 XDefineCursor(_display, _xwindow, cursor);
1122 XMapWindow(_display, _xwindow);
1124 if (_properties.get_raw_mice()) {
1127 if (x11display_cat.is_debug()) {
1128 x11display_cat.debug()
1129 <<
"Raw mice not requested.\n";
1134 _window_handle = NativeWindowHandle::make_x11(_xwindow);
1137 if (_parent_window_handle !=
nullptr) {
1138 _parent_window_handle->attach_child(_window_handle);
1156 void x11GraphicsWindow::
1157 set_wm_properties(
const WindowProperties &properties,
bool already_mapped) {
1159 DCAST_INTO_V(x11_pipe, _pipe);
1162 XTextProperty window_name;
1163 XTextProperty *window_name_p =
nullptr;
1165 const char *name = properties.
get_title().c_str();
1166 if (XStringListToTextProperty((
char **)&name, 1, &window_name) != 0) {
1167 window_name_p = &window_name;
1173 XSizeHints *size_hints_p =
nullptr;
1175 size_hints_p = XAllocSizeHints();
1176 if (size_hints_p !=
nullptr) {
1180 size_hints_p->flags |= USPosition;
1182 LVecBase2i size = _properties.get_size();
1185 size_hints_p->width = size.get_x();
1186 size_hints_p->height = size.get_y();
1187 size_hints_p->flags |= USSize;
1190 size_hints_p->min_width = size.get_x();
1191 size_hints_p->min_height = size.get_y();
1192 size_hints_p->max_width = size.get_x();
1193 size_hints_p->max_height = size.get_y();
1194 size_hints_p->flags |= (PMinSize | PMaxSize);
1201 XWMHints *wm_hints_p =
nullptr;
1202 wm_hints_p = XAllocWMHints();
1203 if (wm_hints_p !=
nullptr) {
1205 wm_hints_p->initial_state = IconicState;
1207 wm_hints_p->initial_state = NormalState;
1209 wm_hints_p->flags = StateHint;
1215 static const int max_type_data = 32;
1216 int32_t type_data[max_type_data];
1217 int next_type_data = 0;
1219 static const int max_state_data = 32;
1220 int32_t state_data[max_state_data];
1221 int next_state_data = 0;
1223 static const int max_set_data = 32;
1226 inline SetAction() { }
1227 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1231 SetAction set_data[max_set_data];
1232 int next_set_data = 0;
1238 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1241 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1244 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1247 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1260 XClassHint *class_hints_p =
nullptr;
1261 if (!x_wm_class.empty()) {
1263 class_hints_p = XAllocClassHint();
1264 class_hints_p->res_class = (
char*) x_wm_class.c_str();
1265 if (!x_wm_class_name.empty()) {
1266 class_hints_p->res_name = (
char*) x_wm_class_name.c_str();
1270 class_hints_p = XAllocClassHint();
1271 class_hints_p->res_class = (
char*)
"Undecorated";
1275 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1280 case WindowProperties::Z_bottom:
1281 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1282 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1283 x11_pipe->_net_wm_state_add);
1284 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1285 x11_pipe->_net_wm_state_remove);
1288 case WindowProperties::Z_normal:
1289 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1290 x11_pipe->_net_wm_state_remove);
1291 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1292 x11_pipe->_net_wm_state_remove);
1295 case WindowProperties::Z_top:
1296 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1297 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1298 x11_pipe->_net_wm_state_remove);
1299 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1300 x11_pipe->_net_wm_state_add);
1305 nassertv(next_type_data < max_type_data);
1306 nassertv(next_state_data < max_state_data);
1307 nassertv(next_set_data < max_set_data);
1310 int32_t pid = getpid();
1311 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1312 XA_CARDINAL, 32, PropModeReplace,
1313 (
unsigned char *)&pid, 1);
1318 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1319 XA_CARDINAL, 32, PropModeReplace,
1320 (
unsigned char *)&compositor, 1);
1323 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1324 XA_ATOM, 32, PropModeReplace,
1325 (
unsigned char *)type_data, next_type_data);
1328 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1329 XA_ATOM, 32, PropModeReplace,
1330 (
unsigned char *)state_data, next_state_data);
1332 if (already_mapped) {
1338 DCAST_INTO_V(x11_pipe, _pipe);
1340 for (
int i = 0; i < next_set_data; ++i) {
1341 XClientMessageEvent event;
1342 memset(&event, 0,
sizeof(event));
1343 event.type = ClientMessage;
1344 event.send_event = True;
1345 event.display = _display;
1346 event.window = _xwindow;
1347 event.message_type = x11_pipe->_net_wm_state;
1349 event.data.l[0] = set_data[i]._action;
1350 event.data.l[1] = set_data[i]._state;
1351 event.data.l[2] = 0;
1352 event.data.l[3] = 1;
1354 XSendEvent(_display, x11_pipe->
get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1358 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1359 nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1361 if (size_hints_p !=
nullptr) {
1362 XFree(size_hints_p);
1364 if (wm_hints_p !=
nullptr) {
1367 if (class_hints_p !=
nullptr) {
1368 XFree(class_hints_p);
1374 Atom protocols[] = {
1378 XSetWMProtocols(_display, _xwindow, protocols,
1379 sizeof(protocols) /
sizeof(Atom));
1386 void x11GraphicsWindow::
1387 setup_colormap(XVisualInfo *visual) {
1389 DCAST_INTO_V(x11_pipe, _pipe);
1390 X11_Window root_window = x11_pipe->
get_root();
1392 _colormap = XCreateColormap(_display, root_window,
1393 visual->visual, AllocNone);
1400 void x11GraphicsWindow::
1402 #ifdef PHAVE_LINUX_INPUT_H
1403 bool any_present =
false;
1404 bool any_mice =
false;
1406 for (
int i=0; i<64; i++) {
1408 fnb <<
"/dev/input/event" << i;
1409 string fn = fnb.str();
1410 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1412 EvdevInputDevice *device =
new EvdevInputDevice(
nullptr, fd);
1413 nassertd(device != NULL)
continue;
1415 if (device->has_pointer()) {
1416 add_input_device(device);
1418 x11display_cat.info()
1419 <<
"Raw mouse " << _input_devices.size()
1420 <<
" detected: " << device->get_name() <<
"\n";
1426 if (errno == ENOENT || errno == ENOTDIR) {
1430 x11display_cat.error()
1431 <<
"Opening raw mice: " << strerror(errno) <<
" " << fn <<
"\n";
1437 _properties.set_raw_mice(
true);
1439 }
else if (!any_present) {
1440 x11display_cat.error() <<
1441 "Opening raw mice: files not found: /dev/input/event*\n";
1444 x11display_cat.error() <<
1445 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1448 x11display_cat.error() <<
1449 "Opening raw mice: panda not compiled with raw mouse support.\n";
1456 void x11GraphicsWindow::
1457 handle_keystroke(XKeyEvent &event) {
1458 if (!_dga_mouse_enabled) {
1464 static const int buffer_size = 256;
1465 wchar_t buffer[buffer_size];
1467 int len = XwcLookupString(_ic, &event, buffer, buffer_size,
nullptr,
1469 if (status == XBufferOverflow) {
1470 x11display_cat.error()
1471 <<
"Overflowed input buffer.\n";
1475 for (
int i = 0; i < len; i++) {
1491 void x11GraphicsWindow::
1492 handle_keypress(XKeyEvent &event) {
1493 if (!_dga_mouse_enabled) {
1499 if (button != ButtonHandle::none()) {
1500 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1503 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1506 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1509 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1519 void x11GraphicsWindow::
1520 handle_keyrelease(XKeyEvent &event) {
1521 if (!_dga_mouse_enabled) {
1527 if (button != ButtonHandle::none()) {
1528 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1529 _input->
button_up(KeyboardButton::control());
1531 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1532 _input->
button_up(KeyboardButton::shift());
1534 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1535 _input->
button_up(KeyboardButton::alt());
1537 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1538 _input->
button_up(KeyboardButton::meta());
1549 get_button(XKeyEvent &key_event,
bool allow_shift) {
1550 KeySym key = XLookupKeysym(&key_event, 0);
1552 if ((key_event.state & Mod2Mask) != 0) {
1567 case XK_KP_Multiply:
1569 case XK_KP_Separator:
1570 case XK_KP_Subtract:
1593 k2 = XLookupKeysym(&key_event, 1);
1594 button = map_button(k2);
1595 if (button != ButtonHandle::none()) {
1609 if ((key_event.state & ShiftMask) != 0) {
1610 KeySym k2 = XLookupKeysym(&key_event, 1);
1612 if (button != ButtonHandle::none()) {
1620 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1621 if (key >= XK_a && key <= XK_z) {
1622 key += (XK_A - XK_a);
1627 return map_button(key);
1635 map_button(KeySym key)
const {
1638 return ButtonHandle::none();
1640 return KeyboardButton::backspace();
1643 return KeyboardButton::tab();
1646 return KeyboardButton::enter();
1648 return KeyboardButton::escape();
1651 return KeyboardButton::space();
1672 case XK_KP_Multiply:
1678 case XK_KP_Separator:
1681 case XK_KP_Subtract:
1786 case XK_bracketleft:
1790 case XK_bracketright:
1792 case XK_asciicircum:
1861 return KeyboardButton::f1();
1864 return KeyboardButton::f2();
1867 return KeyboardButton::f3();
1870 return KeyboardButton::f4();
1872 return KeyboardButton::f5();
1874 return KeyboardButton::f6();
1876 return KeyboardButton::f7();
1878 return KeyboardButton::f8();
1880 return KeyboardButton::f9();
1882 return KeyboardButton::f10();
1884 return KeyboardButton::f11();
1886 return KeyboardButton::f12();
1889 return KeyboardButton::left();
1892 return KeyboardButton::up();
1895 return KeyboardButton::right();
1898 return KeyboardButton::down();
1901 return KeyboardButton::page_up();
1904 return KeyboardButton::page_down();
1907 return KeyboardButton::home();
1910 return KeyboardButton::end();
1913 return KeyboardButton::insert();
1916 return KeyboardButton::del();
1918 return KeyboardButton::num_lock();
1919 case XK_Scroll_Lock:
1920 return KeyboardButton::scroll_lock();
1922 return KeyboardButton::print_screen();
1924 return KeyboardButton::pause();
1926 return KeyboardButton::menu();
1928 return KeyboardButton::lshift();
1930 return KeyboardButton::rshift();
1932 return KeyboardButton::lcontrol();
1934 return KeyboardButton::rcontrol();
1936 return KeyboardButton::lalt();
1938 return KeyboardButton::ralt();
1941 return KeyboardButton::lmeta();
1944 return KeyboardButton::rmeta();
1946 return KeyboardButton::caps_lock();
1948 return KeyboardButton::shift_lock();
1950 if (x11display_cat.is_debug()) {
1951 x11display_cat.debug()
1952 <<
"Unrecognized keysym 0x" << std::hex << key << std::dec <<
"\n";
1954 return ButtonHandle::none();
1961 map_raw_button(KeyCode key)
const {
1962 #ifdef PHAVE_LINUX_INPUT_H
1967 int index = key - 8;
1968 if (index > 0 && index < 128) {
1969 return EvdevInputDevice::map_button(index);
1972 return ButtonHandle::none();
1980 get_mouse_button(XButtonEvent &button_event) {
1981 int index = button_event.button;
1982 if (index == x_wheel_up_button) {
1984 }
else if (index == x_wheel_down_button) {
1986 }
else if (index == x_wheel_left_button) {
1988 }
else if (index == x_wheel_right_button) {
2000 get_keyboard_map()
const {
2007 for (
int k = 9; k <= 135; ++k) {
2008 if (k >= 78 && k <= 91) {
2016 if (raw_button == ButtonHandle::none()) {
2020 KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2026 if (sym >= XK_a && sym <= XK_z) {
2027 label = toupper((
char)sym);
2029 else if (sym >= XK_F1 && sym <= XK_F35) {
2030 label =
"F" + format_string(sym - XK_F1 + 1);
2032 else if (sym >= XK_exclamdown && sym <= XK_ydiaeresis) {
2035 int nbytes = XkbTranslateKeySym(_display, &sym, 0, buffer, 255, 0);
2037 label.assign(buffer, nbytes);
2041 if (button == ButtonHandle::none() && label.empty()) {
2056 Bool x11GraphicsWindow::
2057 check_event(X11_Display *display, XEvent *event,
char *arg) {
2061 return (event->xany.window == self->_xwindow);
2068 X11_Cursor x11GraphicsWindow::
2069 get_cursor(
const Filename &filename) {
2071 DCAST_INTO_R(x11_pipe, _pipe, None);
2073 if (x11_pipe->_xcursor_size == -1) {
2074 x11display_cat.info()
2075 <<
"libXcursor.so.1 not available; cannot change mouse cursor.\n";
2081 if (fi != _cursor_filenames.end()) {
2090 x11display_cat.warning()
2091 <<
"Could not find cursor filename " << filename <<
"\n";
2094 fi = _cursor_filenames.find(resolved);
2095 if (fi != _cursor_filenames.end()) {
2101 if (str ==
nullptr) {
2102 x11display_cat.warning()
2103 <<
"Could not open cursor file " << filename <<
"\n";
2109 str->read(magic, 4);
2111 x11display_cat.warning()
2112 <<
"Could not read from cursor file " << filename <<
"\n";
2118 str->putback(magic[3]);
2119 str->putback(magic[2]);
2120 str->putback(magic[1]);
2121 str->putback(magic[0]);
2123 X11_Cursor h = None;
2124 if (memcmp(magic,
"Xcur", 4) == 0) {
2126 x11display_cat.debug()
2127 <<
"Loading X11 cursor " << filename <<
"\n";
2129 xcfile.closure = str;
2130 xcfile.read = &xcursor_read;
2131 xcfile.write = &xcursor_write;
2132 xcfile.seek = &xcursor_seek;
2134 XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2135 if (images !=
nullptr) {
2136 h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2137 x11_pipe->_XcursorImagesDestroy(images);
2140 }
else if (memcmp(magic,
"\0\0\1\0", 4) == 0
2141 || memcmp(magic,
"\0\0\2\0", 4) == 0) {
2143 x11display_cat.debug()
2144 <<
"Loading Windows cursor " << filename <<
"\n";
2152 x11display_cat.warning()
2153 <<
"X11 cursor filename '" << resolved <<
"' could not be loaded!\n";
2156 _cursor_filenames[resolved] = h;
2164 X11_Cursor x11GraphicsWindow::
2165 read_ico(istream &ico) {
2167 DCAST_INTO_R(x11_pipe, _pipe, None);
2171 uint16_t reserved, type, count;
2175 uint8_t width, height, colorCount, reserved;
2176 uint16_t xhot, yhot;
2177 uint32_t bitmapSize, offset;
2181 uint32_t headerSize, width, height;
2182 uint16_t planes, bitsPerPixel;
2183 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2187 uint8_t blue, green, red, reserved;
2191 unsigned int j, k, mask, shift;
2192 size_t colorCount, bitsPerPixel;
2194 IcoInfoHeader infoHeader;
2195 IcoEntry *entries =
nullptr;
2196 IcoColor color, *palette =
nullptr;
2198 size_t xorBmpSize, andBmpSize;
2199 char *curXor, *curAnd;
2200 char *xorBmp =
nullptr, *andBmp =
nullptr;
2201 XcursorImage *image =
nullptr;
2202 X11_Cursor ret = None;
2204 int def_size = x11_pipe->_xcursor_size;
2207 ico.read(
reinterpret_cast<char *
>(&header),
sizeof(IcoHeader));
2208 if (!ico.good())
goto cleanup;
2209 if (header.type != 1 && header.type != 2)
goto cleanup;
2210 if (header.count < 1)
goto cleanup;
2213 entries =
new IcoEntry[header.count];
2214 ico.read(
reinterpret_cast<char *
>(entries), header.count *
sizeof(IcoEntry));
2215 if (!ico.good())
goto cleanup;
2216 for (i = 1; i < header.count; i++) {
2217 if (entries[i].width == def_size && entries[i].height == def_size) {
2222 if (entries[i].width > entries[entry].width ||
2223 entries[i].height > entries[entry].height)
2228 ico.seekg(entries[entry].offset);
2229 if (!ico.good())
goto cleanup;
2231 if (ico.peek() == 0x89) {
2245 unsigned int *dest = image->pixels;
2247 if (alpha !=
nullptr) {
2248 for (
size_t p = 0; p < num_pixels; ++p) {
2249 *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2254 for (
size_t p = 0; p < num_pixels; ++p) {
2255 *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2261 ico.read(
reinterpret_cast<char *
>(&infoHeader),
sizeof(IcoInfoHeader));
2262 if (!ico.good())
goto cleanup;
2263 bitsPerPixel = infoHeader.bitsPerPixel;
2265 if (infoHeader.compression != 0)
goto cleanup;
2268 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2269 colorCount = 1 << bitsPerPixel;
2270 palette =
new IcoColor[colorCount];
2271 ico.read(
reinterpret_cast<char *
>(palette), colorCount *
sizeof(IcoColor));
2272 if (!ico.good())
goto cleanup;
2275 int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2278 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2279 andBmpSize = and_stride * (infoHeader.height / 2);
2280 curXor = xorBmp =
new char[xorBmpSize];
2281 curAnd = andBmp =
new char[andBmpSize];
2282 ico.read(xorBmp, xorBmpSize);
2283 if (!ico.good())
goto cleanup;
2284 ico.read(andBmp, andBmpSize);
2285 if (!ico.good())
goto cleanup;
2287 image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2290 switch (bitsPerPixel) {
2296 mask = ((1 << bitsPerPixel) - 1);
2297 for (i = image->height - 1; i >= 0; i--) {
2298 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2299 for (k = 0; k < 8 / bitsPerPixel; k++) {
2300 shift = 8 - ((k + 1) * bitsPerPixel);
2301 color = palette[(*curXor & (mask << shift)) >> shift];
2302 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2303 (color.green << 8) +
2311 for (j = 0; j < image->width; j += 8) {
2312 for (k = 0; k < 8; k++) {
2314 image->pixels[(i * image->width) + j + k] |=
2315 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2325 for (i = image->height - 1; i >= 0; i--) {
2326 for (j = 0; j < image->width; j++) {
2327 shift = 7 - (j & 0x7);
2328 uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2329 image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2330 | ((uint8_t)curXor[1] << 8u)
2331 | ((uint8_t)curXor[2] << 16u)
2335 curAnd += and_stride;
2341 for (i = image->height - 1; i >= 0; i--) {
2342 for (j = 0; j < image->width; j++) {
2343 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2344 (*(curXor + 2) << 16) +
2345 (*(curXor + 1) << 8) +
2358 if (header.type == 2) {
2359 image->xhot = entries[entry].xhot;
2360 image->yhot = entries[entry].yhot;
2366 ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2369 x11_pipe->_XcursorImageDestroy(image);