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.info()
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);
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,...
RRCrtc find_fullscreen_crtc(const LPoint2i &point, int &x, int &y, int &width, int &height)
Finds a CRTC for going fullscreen to, at the given origin.
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.