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();
582 if (want_fullscreen && properties.
has_origin()) {
588 if (want_fullscreen) {
592 LPoint2i center(0, 0);
593 if (_properties.has_origin()) {
594 center = _properties.get_origin();
595 if (_properties.has_size()) {
596 center += _properties.get_size() / 2;
599 int x, y, width, height;
603 int reqsizex, reqsizey;
607 }
else if (_properties.has_size()) {
608 reqsizex = _properties.get_x_size();
609 reqsizey = _properties.get_y_size();
621 || (width == reqsizex && height == reqsizey)
622 || !x11_pipe->_have_xrandr) {
628 if (x11display_cat.is_debug()) {
629 x11display_cat.debug()
630 <<
"Setting window to fullscreen on CRTC "
631 << width <<
"x" << height <<
"+" << x <<
"+" << y <<
"\n";
637 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, _xwindow ? _xwindow : x11_pipe->
get_root());
638 SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation);
639 SizeID new_size_id = (SizeID) -1;
643 xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes);
644 for (
int i = 0; i < num_sizes; ++i) {
645 if (xrrs[i].width == reqsizex &&
646 xrrs[i].height == reqsizey) {
650 if (new_size_id == (SizeID) -1) {
651 x11display_cat.error()
652 <<
"Videocard has no supported display resolutions at specified res ("
653 << reqsizex <<
" x " << reqsizey <<
")\n";
659 if (x11display_cat.is_debug()) {
660 x11display_cat.debug()
661 <<
"Switching to fullscreen with resolution "
662 << reqsizex <<
"x" << reqsizey <<
"\n";
665 if (new_size_id != old_size_id) {
666 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), new_size_id, _orig_rotation, CurrentTime);
667 if (_orig_size_id == (SizeID) -1) {
669 _orig_size_id = old_size_id;
681 if (_orig_size_id != (SizeID) -1) {
682 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->
get_root());
683 _XRRSetScreenConfig(_display, conf, x11_pipe->
get_root(), _orig_size_id, _orig_rotation, CurrentTime);
684 _orig_size_id = (SizeID) -1;
687 if (!properties.
has_origin() && _properties.has_origin()) {
688 properties.
set_origin(_properties.get_x_origin(), _properties.get_y_origin());
699 if (x_origin == -2) {
702 if (y_origin == -2) {
706 if (x_origin == -2) {
709 if (y_origin == -2) {
727 set_wm_properties(properties,
true);
732 _properties.set_title(properties.
get_title());
746 XWindowChanges changes;
749 if (_properties.get_fullscreen()) {
752 value_mask |= CWX | CWY;
758 if (changes.x != -1) value_mask |= CWX;
759 if (changes.y != -1) value_mask |= CWY;
767 _fixed_size = _properties.get_size();
773 value_mask |= (CWWidth | CWHeight);
775 if (_properties.get_fixed_size()) {
776 _fixed_size = properties.
get_size();
787 case WindowProperties::Z_bottom:
788 changes.stack_mode = Below;
791 case WindowProperties::Z_normal:
792 changes.stack_mode = TopIf;
795 case WindowProperties::Z_top:
796 changes.stack_mode = Above;
800 value_mask |= (CWStackMode);
814 _properties.set_cursor_filename(cursor_filename);
818 _properties.set_cursor_filename(filename);
820 if (_properties.get_cursor_hidden()) {
823 }
else if (!cursor_filename.empty()) {
825 X11_Cursor cursor = get_cursor(cursor_filename);
826 XDefineCursor(_display, _xwindow, cursor);
829 XDefineCursor(_display, _xwindow, None);
833 if (!properties.has_mouse_mode() &&
834 _properties.get_mouse_mode() != WindowProperties::M_absolute) {
841 XSetInputFocus(_display, _xwindow, RevertToPointerRoot, CurrentTime);
843 XSetInputFocus(_display, PointerRoot, RevertToPointerRoot, CurrentTime);
848 if (properties.has_mouse_mode()) {
850 case WindowProperties::M_absolute:
851 XUngrabPointer(_display, CurrentTime);
852 if (_dga_mouse_enabled) {
854 _dga_mouse_enabled =
false;
856 _properties.set_mouse_mode(WindowProperties::M_absolute);
860 case WindowProperties::M_relative:
861 if (!_dga_mouse_enabled) {
863 X11_Cursor cursor = None;
864 if (_properties.get_cursor_hidden()) {
866 DCAST_INTO_V(x11_pipe, _pipe);
870 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
871 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
872 x11display_cat.error() <<
"Failed to grab pointer!\n";
876 _properties.set_mouse_mode(WindowProperties::M_relative);
878 _dga_mouse_enabled =
true;
883 XQueryPointer(_display, _xwindow, &event.xbutton.root,
884 &event.xbutton.window, &event.xbutton.x_root, &event.xbutton.y_root,
885 &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
889 x11display_cat.warning()
890 <<
"XF86DGA extension not available, cannot enable relative mouse mode\n";
891 _dga_mouse_enabled =
false;
896 case WindowProperties::M_confined:
899 DCAST_INTO_V(x11_pipe, _pipe);
901 if (_dga_mouse_enabled) {
903 _dga_mouse_enabled =
false;
905 X11_Cursor cursor = None;
906 if (_properties.get_cursor_hidden()) {
910 if (XGrabPointer(_display, _xwindow, True, 0, GrabModeAsync,
911 GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) {
912 x11display_cat.error() <<
"Failed to grab pointer!\n";
914 _properties.set_mouse_mode(WindowProperties::M_confined);
922 if (value_mask != 0) {
925 XReconfigureWMWindow(_display, _xwindow, _screen, value_mask, &changes);
928 _awaiting_configure =
true;
935 void x11GraphicsWindow::
936 mouse_mode_absolute() {
943 void x11GraphicsWindow::
944 mouse_mode_relative() {
951 void x11GraphicsWindow::
953 if (_gsg !=
nullptr) {
958 if (_ic != (XIC)
nullptr) {
963 if (_xwindow != (X11_Window)
nullptr) {
964 XDestroyWindow(_display, _xwindow);
965 _xwindow = (X11_Window)
nullptr;
974 if (_orig_size_id != (SizeID) -1) {
976 if (_pipe !=
nullptr) {
978 DCAST_INTO_V(x11_pipe, _pipe);
983 root = RootWindow(_display, _screen);
985 XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root);
986 _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime);
990 GraphicsWindow::close_window();
997 bool x11GraphicsWindow::
999 if (_visual_info ==
nullptr) {
1001 x11display_cat.error()
1002 <<
"No X visual: cannot open window.\n";
1007 DCAST_INTO_R(x11_pipe, _pipe,
false);
1009 if (!_properties.has_origin()) {
1010 _properties.set_origin(0, 0);
1012 if (!_properties.has_size()) {
1013 _properties.set_size(100, 100);
1019 X11_Window parent_window = x11_pipe->
get_root();
1020 WindowHandle *window_handle = _properties.get_parent_window();
1021 if (window_handle !=
nullptr) {
1022 x11display_cat.info()
1023 <<
"Got parent_window " << *window_handle <<
"\n";
1025 if (os_handle !=
nullptr) {
1026 x11display_cat.info()
1027 <<
"os_handle type " << os_handle->get_type() <<
"\n";
1029 if (os_handle->
is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
1030 NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
1031 parent_window = x11_handle->get_handle();
1032 }
else if (os_handle->
is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
1034 parent_window = (X11_Window)int_handle->get_handle();
1038 _parent_window_handle = window_handle;
1041 ButtonPressMask | ButtonReleaseMask |
1042 KeyPressMask | KeyReleaseMask |
1043 EnterWindowMask | LeaveWindowMask |
1045 FocusChangeMask | StructureNotifyMask;
1048 XSetWindowAttributes wa;
1049 wa.background_pixel = XBlackPixel(_display, _screen);
1050 wa.border_pixel = 0;
1051 wa.colormap = _colormap;
1052 wa.event_mask = _event_mask;
1053 wa.override_redirect = _override_redirect;
1055 unsigned long attrib_mask =
1056 CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
1058 _xwindow = XCreateWindow
1059 (_display, parent_window,
1060 _properties.get_x_origin(), _properties.get_y_origin(),
1061 _properties.get_x_size(), _properties.get_y_size(),
1062 0, _visual_info->depth, InputOutput,
1063 _visual_info->visual, attrib_mask, &wa);
1065 if (_xwindow == (X11_Window)0) {
1066 x11display_cat.error()
1067 <<
"failed to create X window.\n";
1071 if (_properties.get_fixed_size()) {
1072 _fixed_size = _properties.get_size();
1075 set_wm_properties(_properties,
false);
1081 XIM im = x11_pipe->
get_im();
1086 XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
1088 if (_ic == (XIC)
nullptr) {
1089 x11display_cat.warning()
1090 <<
"Couldn't create input context.\n";
1094 if (_properties.get_cursor_hidden()) {
1097 }
else if (_properties.has_cursor_filename() && !_properties.get_cursor_filename().empty()) {
1099 X11_Cursor cursor = get_cursor(_properties.get_cursor_filename());
1100 XDefineCursor(_display, _xwindow, cursor);
1103 XMapWindow(_display, _xwindow);
1105 if (_properties.get_raw_mice()) {
1108 if (x11display_cat.is_debug()) {
1109 x11display_cat.debug()
1110 <<
"Raw mice not requested.\n";
1115 _window_handle = NativeWindowHandle::make_x11(_xwindow);
1118 if (_parent_window_handle !=
nullptr) {
1119 _parent_window_handle->attach_child(_window_handle);
1137 void x11GraphicsWindow::
1138 set_wm_properties(
const WindowProperties &properties,
bool already_mapped) {
1140 DCAST_INTO_V(x11_pipe, _pipe);
1143 XTextProperty window_name;
1144 XTextProperty *window_name_p =
nullptr;
1146 const char *name = properties.
get_title().c_str();
1147 if (XStringListToTextProperty((
char **)&name, 1, &window_name) != 0) {
1148 window_name_p = &window_name;
1154 XSizeHints *size_hints_p =
nullptr;
1156 size_hints_p = XAllocSizeHints();
1157 if (size_hints_p !=
nullptr) {
1161 size_hints_p->flags |= USPosition;
1163 LVecBase2i size = _properties.get_size();
1166 size_hints_p->width = size.get_x();
1167 size_hints_p->height = size.get_y();
1168 size_hints_p->flags |= USSize;
1171 size_hints_p->min_width = size.get_x();
1172 size_hints_p->min_height = size.get_y();
1173 size_hints_p->max_width = size.get_x();
1174 size_hints_p->max_height = size.get_y();
1175 size_hints_p->flags |= (PMinSize | PMaxSize);
1182 XWMHints *wm_hints_p =
nullptr;
1183 wm_hints_p = XAllocWMHints();
1184 if (wm_hints_p !=
nullptr) {
1186 wm_hints_p->initial_state = IconicState;
1188 wm_hints_p->initial_state = NormalState;
1190 wm_hints_p->flags = StateHint;
1196 static const int max_type_data = 32;
1197 int32_t type_data[max_type_data];
1198 int next_type_data = 0;
1200 static const int max_state_data = 32;
1201 int32_t state_data[max_state_data];
1202 int next_state_data = 0;
1204 static const int max_set_data = 32;
1207 inline SetAction() { }
1208 inline SetAction(Atom state, Atom action) : _state(state), _action(action) { }
1212 SetAction set_data[max_set_data];
1213 int next_set_data = 0;
1219 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_fullscreen;
1222 state_data[next_state_data++] = x11_pipe->_net_wm_state_fullscreen;
1225 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 1);
1228 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_fullscreen, 0);
1241 XClassHint *class_hints_p =
nullptr;
1242 if (!x_wm_class.empty()) {
1244 class_hints_p = XAllocClassHint();
1245 class_hints_p->res_class = (
char*) x_wm_class.c_str();
1246 if (!x_wm_class_name.empty()) {
1247 class_hints_p->res_name = (
char*) x_wm_class_name.c_str();
1251 class_hints_p = XAllocClassHint();
1252 class_hints_p->res_class = (
char*)
"Undecorated";
1256 type_data[next_type_data++] = x11_pipe->_net_wm_window_type_splash;
1261 case WindowProperties::Z_bottom:
1262 state_data[next_state_data++] = x11_pipe->_net_wm_state_below;
1263 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1264 x11_pipe->_net_wm_state_add);
1265 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1266 x11_pipe->_net_wm_state_remove);
1269 case WindowProperties::Z_normal:
1270 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1271 x11_pipe->_net_wm_state_remove);
1272 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1273 x11_pipe->_net_wm_state_remove);
1276 case WindowProperties::Z_top:
1277 state_data[next_state_data++] = x11_pipe->_net_wm_state_above;
1278 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_below,
1279 x11_pipe->_net_wm_state_remove);
1280 set_data[next_set_data++] = SetAction(x11_pipe->_net_wm_state_above,
1281 x11_pipe->_net_wm_state_add);
1286 nassertv(next_type_data < max_type_data);
1287 nassertv(next_state_data < max_state_data);
1288 nassertv(next_set_data < max_set_data);
1291 int32_t pid = getpid();
1292 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_pid,
1293 XA_CARDINAL, 32, PropModeReplace,
1294 (
unsigned char *)&pid, 1);
1299 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_bypass_compositor,
1300 XA_CARDINAL, 32, PropModeReplace,
1301 (
unsigned char *)&compositor, 1);
1304 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_window_type,
1305 XA_ATOM, 32, PropModeReplace,
1306 (
unsigned char *)type_data, next_type_data);
1309 XChangeProperty(_display, _xwindow, x11_pipe->_net_wm_state,
1310 XA_ATOM, 32, PropModeReplace,
1311 (
unsigned char *)state_data, next_state_data);
1313 if (already_mapped) {
1319 DCAST_INTO_V(x11_pipe, _pipe);
1321 for (
int i = 0; i < next_set_data; ++i) {
1322 XClientMessageEvent event;
1323 memset(&event, 0,
sizeof(event));
1324 event.type = ClientMessage;
1325 event.send_event = True;
1326 event.display = _display;
1327 event.window = _xwindow;
1328 event.message_type = x11_pipe->_net_wm_state;
1330 event.data.l[0] = set_data[i]._action;
1331 event.data.l[1] = set_data[i]._state;
1332 event.data.l[2] = 0;
1333 event.data.l[3] = 1;
1335 XSendEvent(_display, x11_pipe->
get_root(), True, SubstructureNotifyMask | SubstructureRedirectMask, (XEvent *)&event);
1339 XSetWMProperties(_display, _xwindow, window_name_p, window_name_p,
1340 nullptr, 0, size_hints_p, wm_hints_p, class_hints_p);
1342 if (size_hints_p !=
nullptr) {
1343 XFree(size_hints_p);
1345 if (wm_hints_p !=
nullptr) {
1348 if (class_hints_p !=
nullptr) {
1349 XFree(class_hints_p);
1355 Atom protocols[] = {
1359 XSetWMProtocols(_display, _xwindow, protocols,
1360 sizeof(protocols) /
sizeof(Atom));
1367 void x11GraphicsWindow::
1368 setup_colormap(XVisualInfo *visual) {
1370 DCAST_INTO_V(x11_pipe, _pipe);
1371 X11_Window root_window = x11_pipe->
get_root();
1373 _colormap = XCreateColormap(_display, root_window,
1374 visual->visual, AllocNone);
1381 void x11GraphicsWindow::
1383 #ifdef PHAVE_LINUX_INPUT_H
1384 bool any_present =
false;
1385 bool any_mice =
false;
1387 for (
int i=0; i<64; i++) {
1389 fnb <<
"/dev/input/event" << i;
1390 string fn = fnb.str();
1391 int fd = open(fn.c_str(), O_RDONLY | O_NONBLOCK, 0);
1393 EvdevInputDevice *device =
new EvdevInputDevice(
nullptr, fd);
1394 nassertd(device != NULL)
continue;
1396 if (device->has_pointer()) {
1397 add_input_device(device);
1399 x11display_cat.info()
1400 <<
"Raw mouse " << _input_devices.size()
1401 <<
" detected: " << device->get_name() <<
"\n";
1407 if (errno == ENOENT || errno == ENOTDIR) {
1411 x11display_cat.error()
1412 <<
"Opening raw mice: " << strerror(errno) <<
" " << fn <<
"\n";
1418 _properties.set_raw_mice(
true);
1420 }
else if (!any_present) {
1421 x11display_cat.error() <<
1422 "Opening raw mice: files not found: /dev/input/event*\n";
1425 x11display_cat.error() <<
1426 "Opening raw mice: no mouse devices detected in /dev/input/event*\n";
1429 x11display_cat.error() <<
1430 "Opening raw mice: panda not compiled with raw mouse support.\n";
1437 void x11GraphicsWindow::
1438 handle_keystroke(XKeyEvent &event) {
1439 if (!_dga_mouse_enabled) {
1445 static const int buffer_size = 256;
1446 wchar_t buffer[buffer_size];
1448 int len = XwcLookupString(_ic, &event, buffer, buffer_size,
nullptr,
1450 if (status == XBufferOverflow) {
1451 x11display_cat.error()
1452 <<
"Overflowed input buffer.\n";
1456 for (
int i = 0; i < len; i++) {
1472 void x11GraphicsWindow::
1473 handle_keypress(XKeyEvent &event) {
1474 if (!_dga_mouse_enabled) {
1480 if (button != ButtonHandle::none()) {
1481 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1484 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1487 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1490 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1496 if (event.keycode >= 9 && event.keycode <= 135) {
1497 ButtonHandle raw_button = map_raw_button(event.keycode);
1498 if (raw_button != ButtonHandle::none()) {
1507 void x11GraphicsWindow::
1508 handle_keyrelease(XKeyEvent &event) {
1509 if (!_dga_mouse_enabled) {
1515 if (button != ButtonHandle::none()) {
1516 if (button == KeyboardButton::lcontrol() || button == KeyboardButton::rcontrol()) {
1517 _input->
button_up(KeyboardButton::control());
1519 if (button == KeyboardButton::lshift() || button == KeyboardButton::rshift()) {
1520 _input->
button_up(KeyboardButton::shift());
1522 if (button == KeyboardButton::lalt() || button == KeyboardButton::ralt()) {
1523 _input->
button_up(KeyboardButton::alt());
1525 if (button == KeyboardButton::lmeta() || button == KeyboardButton::rmeta()) {
1526 _input->
button_up(KeyboardButton::meta());
1531 if (event.keycode >= 9 && event.keycode <= 135) {
1532 ButtonHandle raw_button = map_raw_button(event.keycode);
1533 if (raw_button != ButtonHandle::none()) {
1544 get_button(XKeyEvent &key_event,
bool allow_shift) {
1545 KeySym key = XLookupKeysym(&key_event, 0);
1547 if ((key_event.state & Mod2Mask) != 0) {
1562 case XK_KP_Multiply:
1564 case XK_KP_Separator:
1565 case XK_KP_Subtract:
1588 k2 = XLookupKeysym(&key_event, 1);
1589 button = map_button(k2);
1590 if (button != ButtonHandle::none()) {
1604 if ((key_event.state & ShiftMask) != 0) {
1605 KeySym k2 = XLookupKeysym(&key_event, 1);
1607 if (button != ButtonHandle::none()) {
1615 if ((key_event.state & (ShiftMask | LockMask)) != 0) {
1616 if (key >= XK_a && key <= XK_z) {
1617 key += (XK_A - XK_a);
1622 return map_button(key);
1630 map_button(KeySym key)
const {
1633 return ButtonHandle::none();
1635 return KeyboardButton::backspace();
1638 return KeyboardButton::tab();
1641 return KeyboardButton::enter();
1643 return KeyboardButton::escape();
1646 return KeyboardButton::space();
1667 case XK_KP_Multiply:
1673 case XK_KP_Separator:
1676 case XK_KP_Subtract:
1781 case XK_bracketleft:
1785 case XK_bracketright:
1787 case XK_asciicircum:
1856 return KeyboardButton::f1();
1859 return KeyboardButton::f2();
1862 return KeyboardButton::f3();
1865 return KeyboardButton::f4();
1867 return KeyboardButton::f5();
1869 return KeyboardButton::f6();
1871 return KeyboardButton::f7();
1873 return KeyboardButton::f8();
1875 return KeyboardButton::f9();
1877 return KeyboardButton::f10();
1879 return KeyboardButton::f11();
1881 return KeyboardButton::f12();
1884 return KeyboardButton::left();
1887 return KeyboardButton::up();
1890 return KeyboardButton::right();
1893 return KeyboardButton::down();
1896 return KeyboardButton::page_up();
1899 return KeyboardButton::page_down();
1902 return KeyboardButton::home();
1905 return KeyboardButton::end();
1908 return KeyboardButton::insert();
1911 return KeyboardButton::del();
1913 return KeyboardButton::num_lock();
1914 case XK_Scroll_Lock:
1915 return KeyboardButton::scroll_lock();
1917 return KeyboardButton::print_screen();
1919 return KeyboardButton::pause();
1921 return KeyboardButton::menu();
1923 return KeyboardButton::lshift();
1925 return KeyboardButton::rshift();
1927 return KeyboardButton::lcontrol();
1929 return KeyboardButton::rcontrol();
1931 return KeyboardButton::lalt();
1933 return KeyboardButton::ralt();
1936 return KeyboardButton::lmeta();
1939 return KeyboardButton::rmeta();
1941 return KeyboardButton::caps_lock();
1943 return KeyboardButton::shift_lock();
1945 if (x11display_cat.is_debug()) {
1946 x11display_cat.debug()
1947 <<
"Unrecognized keysym 0x" << std::hex << key << std::dec <<
"\n";
1949 return ButtonHandle::none();
1956 map_raw_button(KeyCode key)
const {
1957 #ifdef PHAVE_LINUX_INPUT_H
1962 int index = key - 8;
1964 return EvdevInputDevice::map_button(index);
1967 return ButtonHandle::none();
1975 get_mouse_button(XButtonEvent &button_event) {
1976 int index = button_event.button;
1977 if (index == x_wheel_up_button) {
1979 }
else if (index == x_wheel_down_button) {
1981 }
else if (index == x_wheel_left_button) {
1983 }
else if (index == x_wheel_right_button) {
1995 get_keyboard_map()
const {
2002 for (
int k = 9; k <= 135; ++k) {
2004 if (raw_button == ButtonHandle::none()) {
2008 KeySym sym = XkbKeycodeToKeysym(_display, k, 0, 0);
2010 if (button == ButtonHandle::none()) {
2024 Bool x11GraphicsWindow::
2025 check_event(X11_Display *display, XEvent *event,
char *arg) {
2029 return (event->xany.window == self->_xwindow);
2036 X11_Cursor x11GraphicsWindow::
2037 get_cursor(
const Filename &filename) {
2039 DCAST_INTO_R(x11_pipe, _pipe, None);
2041 if (x11_pipe->_xcursor_size == -1) {
2042 x11display_cat.info()
2043 <<
"libXcursor.so.1 not available; cannot change mouse cursor.\n";
2049 if (fi != _cursor_filenames.end()) {
2058 x11display_cat.warning()
2059 <<
"Could not find cursor filename " << filename <<
"\n";
2062 fi = _cursor_filenames.find(resolved);
2063 if (fi != _cursor_filenames.end()) {
2069 if (str ==
nullptr) {
2070 x11display_cat.warning()
2071 <<
"Could not open cursor file " << filename <<
"\n";
2077 str->read(magic, 4);
2079 x11display_cat.warning()
2080 <<
"Could not read from cursor file " << filename <<
"\n";
2086 str->putback(magic[3]);
2087 str->putback(magic[2]);
2088 str->putback(magic[1]);
2089 str->putback(magic[0]);
2091 X11_Cursor h = None;
2092 if (memcmp(magic,
"Xcur", 4) == 0) {
2094 x11display_cat.debug()
2095 <<
"Loading X11 cursor " << filename <<
"\n";
2097 xcfile.closure = str;
2098 xcfile.read = &xcursor_read;
2099 xcfile.write = &xcursor_write;
2100 xcfile.seek = &xcursor_seek;
2102 XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size);
2103 if (images !=
nullptr) {
2104 h = x11_pipe->_XcursorImagesLoadCursor(_display, images);
2105 x11_pipe->_XcursorImagesDestroy(images);
2108 }
else if (memcmp(magic,
"\0\0\1\0", 4) == 0
2109 || memcmp(magic,
"\0\0\2\0", 4) == 0) {
2111 x11display_cat.debug()
2112 <<
"Loading Windows cursor " << filename <<
"\n";
2120 x11display_cat.warning()
2121 <<
"X11 cursor filename '" << resolved <<
"' could not be loaded!\n";
2124 _cursor_filenames[resolved] = h;
2132 X11_Cursor x11GraphicsWindow::
2133 read_ico(istream &ico) {
2135 DCAST_INTO_R(x11_pipe, _pipe, None);
2139 uint16_t reserved, type, count;
2143 uint8_t width, height, colorCount, reserved;
2144 uint16_t xhot, yhot;
2145 uint32_t bitmapSize, offset;
2149 uint32_t headerSize, width, height;
2150 uint16_t planes, bitsPerPixel;
2151 uint32_t compression, imageSize, xPixelsPerM, yPixelsPerM, colorsUsed, colorsImportant;
2155 uint8_t blue, green, red, reserved;
2159 unsigned int j, k, mask, shift;
2160 size_t colorCount, bitsPerPixel;
2162 IcoInfoHeader infoHeader;
2163 IcoEntry *entries =
nullptr;
2164 IcoColor color, *palette =
nullptr;
2166 size_t xorBmpSize, andBmpSize;
2167 char *curXor, *curAnd;
2168 char *xorBmp =
nullptr, *andBmp =
nullptr;
2169 XcursorImage *image =
nullptr;
2170 X11_Cursor ret = None;
2172 int def_size = x11_pipe->_xcursor_size;
2175 ico.read(
reinterpret_cast<char *
>(&header),
sizeof(IcoHeader));
2176 if (!ico.good())
goto cleanup;
2177 if (header.type != 1 && header.type != 2)
goto cleanup;
2178 if (header.count < 1)
goto cleanup;
2181 entries =
new IcoEntry[header.count];
2182 ico.read(
reinterpret_cast<char *
>(entries), header.count *
sizeof(IcoEntry));
2183 if (!ico.good())
goto cleanup;
2184 for (i = 1; i < header.count; i++) {
2185 if (entries[i].width == def_size && entries[i].height == def_size) {
2190 if (entries[i].width > entries[entry].width ||
2191 entries[i].height > entries[entry].height)
2196 ico.seekg(entries[entry].offset);
2197 if (!ico.good())
goto cleanup;
2199 if (ico.peek() == 0x89) {
2213 unsigned int *dest = image->pixels;
2215 if (alpha !=
nullptr) {
2216 for (
size_t p = 0; p < num_pixels; ++p) {
2217 *dest++ = (*alpha << 24U) | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2222 for (
size_t p = 0; p < num_pixels; ++p) {
2223 *dest++ = 0xff000000U | (ptr->r << 16U) | (ptr->g << 8U) | (ptr->b);
2229 ico.read(
reinterpret_cast<char *
>(&infoHeader),
sizeof(IcoInfoHeader));
2230 if (!ico.good())
goto cleanup;
2231 bitsPerPixel = infoHeader.bitsPerPixel;
2233 if (infoHeader.compression != 0)
goto cleanup;
2236 if (bitsPerPixel != 24 && bitsPerPixel != 32) {
2237 colorCount = 1 << bitsPerPixel;
2238 palette =
new IcoColor[colorCount];
2239 ico.read(
reinterpret_cast<char *
>(palette), colorCount *
sizeof(IcoColor));
2240 if (!ico.good())
goto cleanup;
2243 int and_stride = ((infoHeader.width >> 3) + 3) & ~0x03;
2246 xorBmpSize = (infoHeader.width * (infoHeader.height / 2) * bitsPerPixel) / 8;
2247 andBmpSize = and_stride * (infoHeader.height / 2);
2248 curXor = xorBmp =
new char[xorBmpSize];
2249 curAnd = andBmp =
new char[andBmpSize];
2250 ico.read(xorBmp, xorBmpSize);
2251 if (!ico.good())
goto cleanup;
2252 ico.read(andBmp, andBmpSize);
2253 if (!ico.good())
goto cleanup;
2255 image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2);
2258 switch (bitsPerPixel) {
2264 mask = ((1 << bitsPerPixel) - 1);
2265 for (i = image->height - 1; i >= 0; i--) {
2266 for (j = 0; j < image->width; j += 8 / bitsPerPixel) {
2267 for (k = 0; k < 8 / bitsPerPixel; k++) {
2268 shift = 8 - ((k + 1) * bitsPerPixel);
2269 color = palette[(*curXor & (mask << shift)) >> shift];
2270 image->pixels[(i * image->width) + j + k] = (color.red << 16) +
2271 (color.green << 8) +
2279 for (j = 0; j < image->width; j += 8) {
2280 for (k = 0; k < 8; k++) {
2282 image->pixels[(i * image->width) + j + k] |=
2283 ((*curAnd & (1 << shift)) >> shift) ? 0x0 : (0xff << 24);
2293 for (i = image->height - 1; i >= 0; i--) {
2294 for (j = 0; j < image->width; j++) {
2295 shift = 7 - (j & 0x7);
2296 uint32_t alpha = (curAnd[j >> 3] & (1 << shift)) ? 0 : 0xff000000U;
2297 image->pixels[(i * image->width) + j] = (uint8_t)curXor[0]
2298 | ((uint8_t)curXor[1] << 8u)
2299 | ((uint8_t)curXor[2] << 16u)
2303 curAnd += and_stride;
2309 for (i = image->height - 1; i >= 0; i--) {
2310 for (j = 0; j < image->width; j++) {
2311 image->pixels[(i * image->width) + j] = (*(curXor + 3) << 24) +
2312 (*(curXor + 2) << 16) +
2313 (*(curXor + 1) << 8) +
2326 if (header.type == 2) {
2327 image->xhot = entries[entry].xhot;
2328 image->yhot = entries[entry].yhot;
2334 ret = x11_pipe->_XcursorImageLoadCursor(_display, image);
2337 x11_pipe->_XcursorImageDestroy(image);