12#include <X11/XKBlib.h>
15#include <X11/keysym.h>
37#ifdef WITH_OPENGL_BACKEND
42#ifdef WITH_VULKAN_BACKEND
47# include <X11/XF86keysym.h>
51# include <X11/extensions/Xfixes.h>
53# define WITH_XWAYLAND_HACK
58# include <X11/extensions/XInput2.h>
74# define USE_XINPUT_HOTPLUG
78#define USE_UNITY_WORKAROUND
82#define USE_NON_LATIN_KB_WORKAROUND
86 return ptr[bit >> 3] & (1 << (bit & 7));
92 const XkbDescPtr xkb_descr,
93 const KeyCode keycode);
99#ifdef WITH_XWAYLAND_HACK
100static bool use_xwayland_hack =
false;
108 m_keyboard_vector{0},
109#ifdef WITH_X11_XINPUT
112 m_keycode_last_repeat_key(
uint(-1))
115 m_display = XOpenDisplay(
nullptr);
118 throw std::runtime_error(
"unable to open a display!");
121#ifdef USE_X11_ERROR_HANDLERS
126#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
133#define GHOST_INTERN_ATOM_IF_EXISTS(atom) \
135 m_atom.atom = XInternAtom(m_display, #atom, True); \
138#define GHOST_INTERN_ATOM(atom) \
140 m_atom.atom = XInternAtom(m_display, #atom, False); \
165#ifdef WITH_X11_XINPUT
166 m_atom.TABLET = XInternAtom(m_display, XI_TABLET, False);
169#undef GHOST_INTERN_ATOM_IF_EXISTS
170#undef GHOST_INTERN_ATOM
174 m_last_release_keycode = 0;
175 m_last_release_time = 0;
179 int xkb_opcode, xkb_event, xkb_error;
180 int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
182 use_xkb = XkbQueryExtension(
183 m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
185 XkbSetDetectableAutoRepeat(m_display,
true,
nullptr);
187 m_xkb_descr = XkbGetMap(m_display, 0, XkbUseCoreKbd);
189 XkbGetNames(m_display, XkbKeyNamesMask, m_xkb_descr);
190 XkbGetControls(m_display, XkbPerKeyRepeatMask | XkbRepeatKeysMask, m_xkb_descr);
194#ifdef WITH_XWAYLAND_HACK
195 use_xwayland_hack = getenv(
"WAYLAND_DISPLAY") !=
nullptr;
198#ifdef WITH_X11_XINPUT
201 memset(&m_xinput_version, 0,
sizeof(m_xinput_version));
202 XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
203 if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
204 if (version->present) {
205 m_xinput_version = *version;
211# ifdef USE_XINPUT_HOTPLUG
212 if (m_xinput_version.present) {
213 XEventClass class_presence;
215 DevicePresence(m_display, xi_presence, class_presence);
216 XSelectExtensionEvent(
217 m_display, RootWindow(m_display, DefaultScreen(m_display)), &class_presence, 1);
222 refreshXInputDevices();
228#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
234#ifdef WITH_X11_XINPUT
236 clearXInputDevices();
240 XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask,
true);
243 XCloseDisplay(m_display);
251#ifdef WITH_INPUT_NDOF
262 timespec ts = {0, 0};
263 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
264 GHOST_ASSERT(
false,
"Could not instantiate monotonic timer!");
280 static uint64_t timestamp_offset = 0;
283 static uint32_t timestamp_prev = 0;
285 static uint32_t timestamp_start = 0;
287 static bool is_time_init =
false;
292 const uint32_t timestamp_wrap_ms = 2000;
293 static uint32_t timestamp_offset_fake = 0;
295 timestamp_offset_fake =
UINT32_MAX - (timestamp + timestamp_wrap_ms);
297 timestamp = uint32_t(timestamp + timestamp_offset_fake);
304 timestamp_offset = current_time;
305 timestamp_start = timestamp;
310 timestamp = uint32_t(timestamp) - timestamp_start;
314 timestamp_prev = timestamp;
317 if (
UNLIKELY(timestamp < timestamp_prev)) {
323 timestamp_prev = timestamp;
327 return timestamp_final;
348 width = DisplayWidth(m_display, DefaultScreen(m_display));
349 height = DisplayHeight(m_display, DefaultScreen(m_display));
360 const bool exclusive,
361 const bool is_dialog,
408#ifdef WITH_VULKAN_BACKEND
409 case GHOST_kDrawingContextTypeVulkan: {
411 GHOST_kVulkanPlatformX11,
421 if (context->initializeDrawingContext()) {
429#ifdef WITH_OPENGL_BACKEND
430 case GHOST_kDrawingContextTypeOpenGL: {
431 for (
int minor = 6; minor >= 3; --minor) {
436 (GLXFBConfig)
nullptr,
437 GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
442 if (context->initializeDrawingContext()) {
464#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
465static void destroyIMCallback(XIM , XPointer
ptr, XPointer )
470 *(XIM *)
ptr =
nullptr;
474bool GHOST_SystemX11::openX11_IM()
481 XSetLocaleModifiers(
"");
483 m_xim = XOpenIM(m_display,
nullptr, (
char *)GHOST_X11_RES_NAME, (
char *)GHOST_X11_RES_CLASS);
489 destroy.callback = (XIMProc)destroyIMCallback;
490 destroy.client_data = (XPointer)&m_xim;
491 XSetIMValues(m_xim, XNDestroyCallback, &destroy,
nullptr);
508 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
510 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
511 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
513 for (; win_it != win_end; ++win_it) {
514 GHOST_WindowX11 *window =
static_cast<GHOST_WindowX11 *
>(*win_it);
524 int fd = ConnectionNumber(display);
530 if (maxSleep == -1) {
531 select(fd + 1, &fds,
nullptr,
nullptr,
nullptr);
536 tv.tv_sec = maxSleep / 1000;
537 tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
539 select(fd + 1, &fds,
nullptr,
nullptr, &tv);
551 switch (event->type) {
554 data->timestamp =
event->xbutton.time;
557 data->timestamp =
event->xmotion.time;
561 data->timestamp =
event->xkey.time;
564 data->timestamp =
event->xproperty.time;
568 data->timestamp =
event->xcrossing.time;
571 data->timestamp =
event->xselectionclear.time;
580Time GHOST_SystemX11::lastEventTime(Time default_time)
582 init_timestamp_data
data;
583 data.timestamp = default_time;
587 return data.timestamp;
595 bool anyProcessed =
false;
600 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
619 while (XPending(m_display)) {
621 XNextEvent(m_display, &xevent);
623#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
627 if (
ELEM(xevent.type, FocusIn, KeyPress)) {
628 if (!m_xim && openX11_IM()) {
634 if (window && !window->getX11_XIC() && window->createX11_XIC()) {
636 if (xevent.type == KeyPress) {
640 XSetICFocus(window->getX11_XIC());
647 if (
ELEM(xevent.type, KeyPress, KeyRelease)) {
648 if (xevent.xkey.time != 0) {
649 m_last_key_time = xevent.xkey.time;
654 if (XFilterEvent(&xevent, (
Window)
nullptr) == True) {
661 if (xevent.type == KeyRelease) {
662 m_last_release_keycode = xevent.xkey.keycode;
663 m_last_release_time = xevent.xkey.time;
665 else if (xevent.type == KeyPress) {
666 if ((xevent.xkey.keycode == m_last_release_keycode) &&
667 (xevent.xkey.time <= m_last_release_time))
673 processEvent(&xevent);
676#ifdef USE_UNITY_WORKAROUND
683 if (xevent.type == FocusIn) {
687 if (window && XPending(m_display) >= 2) {
688 XNextEvent(m_display, &xevent);
690 if (xevent.type == KeymapNotify) {
695 XPeekEvent(m_display, &xev_next);
697 if (
ELEM(xev_next.type, KeyPress, KeyRelease)) {
700 const static KeySym modifiers[] = {
714 KeyCode kc = XKeysymToKeycode(m_display, modifiers[
i]);
715 if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
731 if (generateWindowExposeEvents()) {
735#ifdef WITH_INPUT_NDOF
741 }
while (waitForEvent && !anyProcessed);
746#ifdef WITH_X11_XINPUT
747static bool checkTabletProximity(
Display *display, XDevice *device)
754 if (device ==
nullptr) {
761 state = XQueryDeviceState(display, device);
766 XInputClass *cls =
state->data;
768 for (
int loop = 0; loop <
state->num_classes; loop++) {
769 switch (cls->c_class) {
771 XValuatorState *val_state = (XValuatorState *)cls;
776 if ((val_state->mode & 2) == 0) {
777 XFreeDeviceState(
state);
782 cls = (XInputClass *)((
char *)cls + cls->length);
784 XFreeDeviceState(
state);
790void GHOST_SystemX11::processEvent(XEvent *xe)
792 GHOST_WindowX11 *window = findGhostWindow(xe->xany.window);
793 GHOST_Event *g_event =
nullptr;
796 bool is_repeat =
false;
797 if (
ELEM(xe->type, KeyPress, KeyRelease)) {
798 XKeyEvent *xke = &(xe->xkey);
801 bool is_repeat_keycode =
false;
803 if (m_xkb_descr !=
nullptr) {
805 is_repeat_keycode = (
807 (xke->keycode < (XkbPerKeyBitArraySize << 3)) &&
808 bit_is_on(m_xkb_descr->ctrls->per_key_repeat, xke->keycode));
812 switch (XLookupKeysym(xke, 0)) {
829 is_repeat_keycode =
true;
834 if (is_repeat_keycode) {
835 if (xe->type == KeyPress) {
836 if (m_keycode_last_repeat_key == xke->keycode) {
839 m_keycode_last_repeat_key = xke->keycode;
842 if (m_keycode_last_repeat_key == xke->keycode) {
843 m_keycode_last_repeat_key =
uint(-1);
848 else if (xe->type == EnterNotify) {
850 m_keycode_last_repeat_key =
uint(-1);
853#ifdef USE_XINPUT_HOTPLUG
855 if (m_xinput_version.present) {
856 XEventClass class_presence;
859 DevicePresence(m_display, xi_presence, class_presence);
860 (void)class_presence;
862 if (xe->type == xi_presence) {
863 const XDevicePresenceNotifyEvent *notify_event = (
const XDevicePresenceNotifyEvent *)xe;
866 refreshXInputDevices();
870 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
871 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
872 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
874 for (; win_it != win_end; ++win_it) {
875 GHOST_WindowX11 *window_xinput =
static_cast<GHOST_WindowX11 *
>(*win_it);
876 window_xinput->refreshXInputDevices();
888#ifdef WITH_X11_XINPUT
895 bool any_proximity =
false;
897 for (
const GHOST_TabletX11 &xtablet : m_xtablets) {
898 if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
899 any_proximity =
true;
903 if (!any_proximity) {
911 const XExposeEvent &xee = xe->xexpose;
913 if (xee.count == 0) {
925 const XMotionEvent &xme = xe->xmotion;
946 const int32_t subregion_div = 4;
953 bounds.m_l = center[0] - (
size[0] / (subregion_div * 2));
954 bounds.m_r = center[0] + (
size[0] / (subregion_div * 2));
955 bounds.m_t = center[1] - (
size[1] / (subregion_div * 2));
956 bounds.m_b = center[1] + (
size[1] / (subregion_div * 2));
973 bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
978 if (x_new != xme.x_root || y_new != xme.y_root) {
985 if (x_new != xme.x_root && xme.time > m_last_warp_x) {
986 x_accum += (xme.x_root - x_new);
987 m_last_warp_x = lastEventTime(xme.time) + 25;
989 if (y_new != xme.y_root && xme.time > m_last_warp_y) {
990 y_accum += (xme.y_root - y_new);
991 m_last_warp_y = lastEventTime(xme.time) + 25;
999 g_event =
new GHOST_EventCursor(event_ms,
1002 xme.x_root + x_accum,
1003 xme.y_root + y_accum,
1008 g_event =
new GHOST_EventCursor(event_ms,
1020 XKeyEvent *xke = &(xe->xkey);
1021#ifdef WITH_X11_XINPUT
1023 const Time time = xke->time ? xke->time : m_last_key_time;
1025 const Time time = xke->time;
1030 char *utf8_buf =
nullptr;
1033#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1040 char utf8_array[16 * 6 + 5];
1049#ifdef USE_NON_LATIN_KB_WORKAROUND
1075 const uint mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch);
1076 const uint number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask;
1077 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1078 ((xke->state & number_hack_forbidden_kmods_mask) == 0))
1080 key_sym = XLookupKeysym(xke, ShiftMask);
1081 if (!((key_sym >= XK_0) && (key_sym <= XK_9))) {
1082 key_sym = XLookupKeysym(xke, 0);
1086 key_sym = XLookupKeysym(xke, 0);
1089 if (!XLookupString(xke, &ascii, 1, &key_sym_str,
nullptr)) {
1151 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1152 ((key_sym = XLookupKeysym(xke, ShiftMask)) >= XK_0) && (key_sym <= XK_9))
1158 key_sym = XLookupKeysym(xke, 0);
1163 if (!XLookupString(xke, &ascii, 1,
nullptr,
nullptr)) {
1168#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1173 if (xke->type == KeyPress) {
1174 utf8_buf = utf8_array;
1175#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1177 xic = window->getX11_XIC();
1182 if (!(
len = Xutf8LookupString(
1183 xic, xke, utf8_buf,
sizeof(utf8_array) - 5, &key_sym, &status)))
1188 if (status == XBufferOverflow) {
1189 utf8_buf = (
char *)malloc(
len + 5);
1190 len = Xutf8LookupString(xic, xke, utf8_buf,
len, &key_sym, &status);
1193 if (
ELEM(status, XLookupChars, XLookupBoth)) {
1196 if ((utf8_buf[0] < 32 && utf8_buf[0] > 0) || (utf8_buf[0] == 127)) {
1200 else if (status == XLookupKeySym) {
1205 printf(
"Bad keycode lookup. Keysym 0x%x Status: %s\n",
1207 (status == XLookupNone ?
"XLookupNone" :
1208 status == XLookupKeySym ?
"XLookupKeySym" :
1211 printf(
"'%.*s' %p %p\n",
len, utf8_buf, xic, m_xim);
1218 if (!utf8_buf[0] && ascii) {
1219 utf8_buf[0] = ascii;
1224 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, utf8_buf);
1226#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1229 if (xke->type == KeyPress && xic) {
1234 if (
uchar(utf8_buf[
i++]) > 0x7f) {
1235 for (;
i <
len; ++
i) {
1237 if (c < 0x80 || c > 0xbf) {
1249 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, &utf8_buf[
i]);
1253 if (utf8_buf != utf8_array) {
1262 case ButtonRelease: {
1263 const XButtonEvent &xbe = xe->xbutton;
1270 if (xbe.button == Button4) {
1271 if (xbe.type == ButtonPress) {
1276 if (xbe.button == Button5) {
1277 if (xbe.type == ButtonPress) {
1284 if (xbe.button == Button1) {
1287 else if (xbe.button == Button2) {
1290 else if (xbe.button == Button3) {
1296 else if (xbe.button == 6) {
1299 else if (xbe.button == 7) {
1302 else if (xbe.button == 8) {
1305 else if (xbe.button == 9) {
1312 g_event =
new GHOST_EventButton(event_ms, type, window, gbmask, window->
GetTabletData());
1317 case ConfigureNotify: {
1328 const XFocusChangeEvent &xfe = xe->xfocus;
1332 printf(
"X: focus %s for window %d\n", xfe.type == FocusIn ?
"in" :
"out",
int(xfe.window));
1340#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1341 XIC xic = window->getX11_XIC();
1343 if (xe->type == FocusIn) {
1355 case ClientMessage: {
1356 XClientMessageEvent &xcme = xe->xclient;
1358 if (((Atom)xcme.data.l[0]) ==
m_atom.WM_DELETE_WINDOW) {
1361 else if (((Atom)xcme.data.l[0]) ==
m_atom.WM_TAKE_FOCUS) {
1362 XWindowAttributes attr;
1376 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
1377 if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
1378 if (attr.map_state == IsViewable) {
1379 if (fwin != xcme.window) {
1380 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
1390 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) ==
false) {
1405 case GraphicsExpose:
1417 const XCrossingEvent &xce = xe->xcrossing;
1419 if (xce.mode == NotifyNormal) {
1420 g_event =
new GHOST_EventCursor(event_ms,
1430 "X: %s window %d\n", xce.type == EnterNotify ?
"entering" :
"leaving",
int(xce.window));
1433 if (xce.type == EnterNotify) {
1464 case ReparentNotify:
1466 case SelectionRequest: {
1468 XSelectionRequestEvent *xse = &xe->xselectionrequest;
1471 if (xse->property ==
None) {
1472 xse->property = xse->target;
1475 nxe.xselection.type = SelectionNotify;
1476 nxe.xselection.requestor = xse->requestor;
1477 nxe.xselection.property = xse->property;
1478 nxe.xselection.display = xse->display;
1479 nxe.xselection.selection = xse->selection;
1480 nxe.xselection.target = xse->target;
1481 nxe.xselection.time = xse->time;
1484 if (
ELEM(xse->target,
1490 if (xse->selection == XInternAtom(m_display,
"PRIMARY", False)) {
1491 XChangeProperty(m_display,
1500 else if (xse->selection == XInternAtom(m_display,
"CLIPBOARD", False)) {
1501 XChangeProperty(m_display,
1511 else if (xse->target ==
m_atom.TARGETS) {
1512 const Atom atom_list[] = {
m_atom.TARGETS,
1517 XChangeProperty(m_display,
1523 reinterpret_cast<const uchar *
>(atom_list),
1529 nxe.xselection.property =
None;
1533 XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
1539#ifdef WITH_X11_XINPUT
1540 for (GHOST_TabletX11 &xtablet : m_xtablets) {
1541 if (
ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
1542 const XDeviceMotionEvent *
data = (
const XDeviceMotionEvent *)xe;
1543 if (
data->deviceid != xtablet.ID) {
1547 const uchar axis_first =
data->first_axis;
1548 const uchar axes_end = axis_first +
data->axes_count;
1562# define AXIS_VALUE_GET(axis, val) \
1563 ((axis_first <= axis && axes_end > axis) && \
1564 ((void)(val = data->axis_data[axis - axis_first]), true))
1566 if (AXIS_VALUE_GET(2, axis_value)) {
1578 if (AXIS_VALUE_GET(3, axis_value)) {
1580 float(xtablet.XtiltLevels);
1582 if (AXIS_VALUE_GET(4, axis_value)) {
1584 float(xtablet.YtiltLevels);
1587# undef AXIS_VALUE_GET
1589 else if (xe->type == xtablet.ProxInEvent) {
1590 const XProximityNotifyEvent *
data = (
const XProximityNotifyEvent *)xe;
1591 if (
data->deviceid != xtablet.ID) {
1597 else if (xe->type == xtablet.ProxOutEvent) {
1628 XImage *image = XGetImage(m_display,
1629 XRootWindow(m_display, XDefaultScreen(m_display)),
1636 if (image ==
nullptr) {
1639 c.pixel = XGetPixel(image, 0, 0);
1641 XQueryColor(m_display, XDefaultColormap(m_display, XDefaultScreen(m_display)), &c);
1644 r_color[0] = c.red / 65535.0f;
1645 r_color[1] = c.green / 65535.0f;
1646 r_color[2] = c.blue / 65535.0f;
1655 memset((
void *)m_keyboard_vector, 0,
sizeof(m_keyboard_vector));
1657 XQueryKeymap(m_display, (
char *)m_keyboard_vector);
1661 const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L);
1662 const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R);
1663 const static KeyCode control_l = XKeysymToKeycode(m_display, XK_Control_L);
1664 const static KeyCode control_r = XKeysymToKeycode(m_display, XK_Control_R);
1665 const static KeyCode alt_l = XKeysymToKeycode(m_display, XK_Alt_L);
1666 const static KeyCode alt_r = XKeysymToKeycode(m_display, XK_Alt_R);
1667 const static KeyCode super_l = XKeysymToKeycode(m_display, XK_Super_L);
1668 const static KeyCode super_r = XKeysymToKeycode(m_display, XK_Super_R);
1669 const static KeyCode hyper_l = XKeysymToKeycode(m_display, XK_Hyper_L);
1670 const static KeyCode hyper_r = XKeysymToKeycode(m_display, XK_Hyper_R);
1674 ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
1676 ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
1679 ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
1681 ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
1687 ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) != 0);
1689 ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) != 0);
1692 ((m_keyboard_vector[hyper_l >> 3] >> (hyper_l & 7)) & 1) != 0);
1694 ((m_keyboard_vector[hyper_r >> 3] >> (hyper_r & 7)) & 1) != 0);
1701 Window root_return, child_return;
1705 if (XQueryPointer(m_display,
1706 RootWindow(m_display, DefaultScreen(m_display)),
1713 &mask_return) == True)
1737 if (XQueryPointer(display,
1738 RootWindow(display, DefaultScreen(display)),
1745 &mask_return) == False)
1771#ifdef WITH_XWAYLAND_HACK
1785#ifdef WITH_XWAYLAND_HACK
1786 if (use_xwayland_hack) {
1787 if (child_return !=
None) {
1788 XFixesHideCursor(m_display, child_return);
1793#if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1794 if ((m_xinput_version.present) && (m_xinput_version.major_version >= 2)) {
1797 if (XIGetClientPointer(m_display,
None, &device_id) != False) {
1798 XIWarpPointer(m_display, device_id,
None,
None, 0, 0, 0, 0, relx, rely);
1804 XWarpPointer(m_display,
None,
None, 0, 0, 0, 0, relx, rely);
1807#ifdef WITH_XWAYLAND_HACK
1808 if (use_xwayland_hack) {
1809 if (child_return !=
None) {
1810 XFixesShowCursor(m_display, child_return);
1815 XSync(m_display, 0);
1834 GHOST_ASSERT((bad_wind !=
nullptr),
"addDirtyWindow() nullptr ptr trapped (window)");
1836 m_dirty_windows.push_back(bad_wind);
1839bool GHOST_SystemX11::generateWindowExposeEvents()
1843 bool anyProcessed =
false;
1845 for (; w_start != w_end; ++w_start) {
1849 (*w_start)->validate();
1853 anyProcessed =
true;
1857 m_dirty_windows.clear();
1858 return anyProcessed;
1862 XkbDescPtr xkb_descr,
1863 const KeyCode keycode)
1874#define GXMAP(k, x, y) \
1883 if ((key >= XK_A) && (key <= XK_Z)) {
1886 else if ((key >= XK_a) && (key <= XK_z)) {
1889 else if ((key >= XK_0) && (key <= XK_9)) {
1892 else if ((key >= XK_F1) && (key <= XK_F24)) {
1980#ifdef WITH_XF86KEYSYM
1986# ifdef XF86XK_AudioForward
1991#ifdef WITH_GHOST_DEBUG
1992 printf(
"%s: unknown key: %lu / 0x%lx\n", __func__, key, key);
2004#define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
2008 GHOST_ASSERT(XkbKeyNameLength == 4,
"Name length is invalid!");
2009 if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) {
2010 const char *id_str = xkb_descr->names->keys[keycode].name;
2011 const uint32_t
id =
MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]);
2013 case MAKE_ID(
'T',
'L',
'D',
'E'):
2015 case MAKE_ID(
'L',
'S',
'G',
'T'):
2017#ifdef WITH_GHOST_DEBUG
2019 printf(
"%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str);
2024 else if (keycode != 0) {
2034#define XCLIB_XCOUT_NONE 0
2035#define XCLIB_XCOUT_SENTCONVSEL 1
2036#define XCLIB_XCOUT_INCR 2
2037#define XCLIB_XCOUT_FALLBACK 3
2038#define XCLIB_XCOUT_FALLBACK_UTF8 4
2039#define XCLIB_XCOUT_FALLBACK_COMP 5
2040#define XCLIB_XCOUT_FALLBACK_TEXT 6
2044 const XEvent *evt, Atom sel, Atom target,
uchar **txt,
ulong *
len,
uint *context)
const
2049 ulong pty_size, pty_items;
2052 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2053 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2067 XConvertSelection(m_display, sel, target,
m_atom.XCLIP_OUT, win, CurrentTime);
2072 if (evt->type != SelectionNotify) {
2076 if (target ==
m_atom.UTF8_STRING && evt->xselection.property ==
None) {
2080 if (target ==
m_atom.COMPOUND_TEXT && evt->xselection.property ==
None) {
2084 if (target ==
m_atom.TEXT && evt->xselection.property ==
None) {
2090 XGetWindowProperty(m_display,
2104 if (pty_type ==
m_atom.INCR) {
2106 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2115 if (pty_format != 8) {
2121 XGetWindowProperty(m_display,
2135 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2138 ltxt = (
uchar *)malloc(pty_items);
2139 memcpy(ltxt, buffer, pty_items);
2160 if (evt->type != PropertyNotify) {
2165 if (evt->xproperty.state != PropertyNewValue) {
2170 XGetWindowProperty(m_display,
2183 if (pty_format != 8) {
2188 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2192 if (pty_size == 0) {
2195 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2207 XGetWindowProperty(m_display,
2227 ltxt = (
uchar *)realloc(ltxt, *
len);
2231 memcpy(<xt[*
len - pty_items], buffer, pty_items);
2237 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2246 Atom target =
m_atom.UTF8_STRING;
2255 if (selection == True) {
2259 sseln =
m_atom.CLIPBOARD;
2262 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2263 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2268 owner = XGetSelectionOwner(m_display, sseln);
2270 if (sseln ==
m_atom.CLIPBOARD) {
2272 sel_buf = (
char *)malloc(sel_buf_size);
2277 sel_buf = (
char *)malloc(sel_buf_size);
2281 if (owner ==
None) {
2286 vector<XEvent> restore_events;
2290 bool restore_this_event =
false;
2292 XNextEvent(m_display, &evt);
2293 restore_this_event = (evt.type != SelectionNotify);
2299 if (restore_this_event) {
2300 restore_events.push_back(evt);
2312 target =
m_atom.COMPOUND_TEXT;
2332 while (!restore_events.empty()) {
2333 XPutBackEvent(m_display, &restore_events.back());
2334 restore_events.pop_back();
2339 char *tmp_data = (
char *)malloc(sel_len + 1);
2340 memcpy(tmp_data, (
char *)sel_buf, sel_len);
2341 tmp_data[sel_len] =
'\0';
2343 if (sseln ==
m_atom.STRING) {
2359 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2360 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2365 if (selection == False) {
2366 XSetSelectionOwner(m_display,
m_atom.CLIPBOARD, m_window, CurrentTime);
2367 owner = XGetSelectionOwner(m_display,
m_atom.CLIPBOARD);
2372 size_t buffer_size = strlen(buffer) + 1;
2377 XSetSelectionOwner(m_display,
m_atom.PRIMARY, m_window, CurrentTime);
2378 owner = XGetSelectionOwner(m_display,
m_atom.PRIMARY);
2383 size_t buffer_size = strlen(buffer) + 1;
2388 if (owner != m_window) {
2389 fprintf(stderr,
"failed to own primary\n");
2443 XFillRectangle(display,
2451 XFillRectangle(display,
2459 XDrawString(display,
2486 data = strdup(text);
2487 for (tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps)) {
2492 data = strdup(text);
2493 *
str = (
char **)malloc(
size_t(*
count) *
sizeof(
char *));
2494 for (
i = 0, tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps),
i++) {
2495 (*str)[
i] = strdup(tok);
2501 const char *message,
2502 const char *help_label,
2503 const char *continue_label,
2507 char **text_splitted =
nullptr;
2509 split(message,
"\n", &text_splitted, &textLines);
2516 int screen = DefaultScreen(m_display);
2517 window = XCreateSimpleWindow(m_display,
2518 RootWindow(m_display, screen),
2524 BlackPixel(m_display, screen),
2525 WhitePixel(m_display, screen));
2529 hints.flags = PSize | PMinSize | PMaxSize;
2530 hints.min_width = hints.max_width = hints.base_width = dialog_data.
width;
2531 hints.min_height = hints.max_height = hints.base_height = dialog_data.
height;
2532 XSetWMNormalHints(m_display, window, &hints);
2537 Atom wm_Name = XInternAtom(m_display,
"_NET_WM_NAME", False);
2538 Atom utf8Str = XInternAtom(m_display,
"UTF8_STRING", False);
2540 Atom winType = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE", False);
2541 Atom typeDialog = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE_DIALOG", False);
2543 XChangeProperty(m_display,
2549 (
const uchar *)title,
2550 int(strlen(title)));
2553 m_display, window, winType, XA_ATOM, 32, PropModeReplace, (
uchar *)&typeDialog, 1);
2557 XGCValues buttonBorderGCValues;
2558 buttonBorderGCValues.foreground = BlackPixel(m_display, screen);
2559 buttonBorderGCValues.background = WhitePixel(m_display, screen);
2560 XGCValues buttonGCValues;
2561 buttonGCValues.foreground = WhitePixel(m_display, screen);
2562 buttonGCValues.background = BlackPixel(m_display, screen);
2564 GC buttonBorderGC = XCreateGC(m_display, window, GCForeground, &buttonBorderGCValues);
2565 GC buttonGC = XCreateGC(m_display, window, GCForeground, &buttonGCValues);
2567 XSelectInput(m_display, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
2568 XMapWindow(m_display, window);
2570 const bool has_link = link && strlen(link);
2573 XNextEvent(m_display, &
e);
2574 if (
e.type == Expose) {
2575 for (
int i = 0;
i < textLines;
i++) {
2576 XDrawString(m_display,
2578 DefaultGC(m_display, screen),
2582 int(strlen(text_splitted[
i])));
2584 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 1, continue_label);
2586 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 2, help_label);
2589 else if (
e.type == ButtonRelease) {
2595 string cmd =
"xdg-open \"" + string(link) +
"\"";
2596 if (system(cmd.c_str()) != 0) {
2597 GHOST_PRINTF(
"GHOST_SystemX11::showMessageBox: Unable to run system command [%s]",
2606 for (
int i = 0;
i < textLines;
i++) {
2607 free(text_splitted[
i]);
2609 free(text_splitted);
2611 XDestroyWindow(m_display, window);
2612 XFreeGC(m_display, buttonBorderGC);
2613 XFreeGC(m_display, buttonGC);
2633 event_ms, eventType, draggedObjectType, window, mouseX, mouseY,
data));
2650 char error_code_str[512];
2652 XGetErrorText(display, event->error_code, error_code_str,
sizeof(error_code_str));
2655 "Received X11 Error:\n"
2656 "\terror code: %d\n"
2657 "\trequest code: %d\n"
2658 "\tminor code: %d\n"
2659 "\terror text: %s\n",
2661 event->request_code,
2676 fprintf(stderr,
"Ignoring Xlib error: error IO\n");
2682#ifdef WITH_X11_XINPUT
2684static bool is_filler_char(
char c)
2686 return isspace(c) ||
ELEM(c,
'_',
'-',
';',
':');
2690static bool match_token(
const char *haystack,
const char *needle)
2693 for (h = haystack; *h;) {
2694 while (*h && is_filler_char(*h)) {
2701 for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) {
2704 if (!*n && (is_filler_char(*h) || !*h)) {
2708 while (*h && !is_filler_char(*h)) {
2725static GHOST_TTabletMode tablet_mode_from_name(
const char *name,
const char *type)
2728 static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
"pen",
nullptr};
2730 static const char *type_blacklist[] = {
"pad",
"cursor",
"touch",
nullptr};
2733 for (
i = 0; type_blacklist[
i] !=
nullptr;
i++) {
2734 if (type && (strcasecmp(type, type_blacklist[
i]) == 0)) {
2740 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2741 if (type && match_token(type, tablet_stylus_whitelist[
i])) {
2745 if (type && match_token(type,
"eraser")) {
2748 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2749 if (name && match_token(name, tablet_stylus_whitelist[
i])) {
2753 if (name && match_token(name,
"eraser")) {
2762void GHOST_SystemX11::refreshXInputDevices()
2764 if (m_xinput_version.present) {
2766 clearXInputDevices();
2773 XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
2775 for (
int i = 0;
i < device_count; ++
i) {
2776 char *device_type = device_info[
i].type ? XGetAtomName(m_display, device_info[
i].type) :
2778 GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[
i].name, device_type);
2783 XFree((
void *)device_type);
2790 GHOST_TabletX11 xtablet = {tablet_mode};
2791 xtablet.ID = device_info[
i].id;
2792 xtablet.Device = XOpenDevice(m_display, xtablet.ID);
2794 if (xtablet.Device !=
nullptr) {
2796 XAnyClassPtr ici = device_info[
i].inputclassinfo;
2798 if (ici !=
nullptr) {
2799 for (
int j = 0; j < device_info[
i].num_classes; ++j) {
2800 if (ici->c_class == ValuatorClass) {
2801 XValuatorInfo *xvi = (XValuatorInfo *)ici;
2802 if (xvi->axes !=
nullptr) {
2803 xtablet.PressureLevels = xvi->axes[2].max_value;
2805 if (xvi->num_axes > 3) {
2808 xtablet.XtiltLevels = xvi->axes[3].max_value;
2809 xtablet.YtiltLevels = xvi->axes[4].max_value;
2812 xtablet.XtiltLevels = 0;
2813 xtablet.YtiltLevels = 0;
2820 ici = (XAnyClassPtr)(((
char *)ici) + ici->length);
2824 m_xtablets.push_back(xtablet);
2828 XFreeDeviceList(device_info);
2835void GHOST_SystemX11::clearXInputDevices()
2837 for (GHOST_TabletX11 &xtablet : m_xtablets) {
2838 if (xtablet.Device) {
2839 XCloseDevice(m_display, xtablet.Device);
void BLI_kdtree_nd_ free(KDTree *tree)
#define GHOST_OPENGL_GLX_RESET_NOTIFICATION_STRATEGY
#define GHOST_OPENGL_GLX_CONTEXT_FLAGS
#define GHOST_PRINTF(x,...)
#define GHOST_ASSERT(x, info)
static void DeviceAdded(uint32_t)
static void DeviceRemoved(uint32_t)
static char * txt_select_buffer
static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg)
static void split(const char *text, const char *seps, char ***str, int *count)
int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *event)
#define XCLIB_XCOUT_FALLBACK_UTF8
static char * txt_cut_buffer
#define XCLIB_XCOUT_FALLBACK
#define GHOST_INTERN_ATOM(atom)
static void SleepTillEvent(Display *display, int64_t maxSleep)
#define XCLIB_XCOUT_FALLBACK_COMP
#define MAKE_ID(a, b, c, d)
static uchar bit_is_on(const uchar *ptr, int bit)
static GHOST_TKey ghost_key_from_keysym_or_keycode(const KeySym key_sym, const XkbDescPtr xkb_descr, const KeyCode keycode)
#define XCLIB_XCOUT_SENTCONVSEL
#define XCLIB_XCOUT_FALLBACK_TEXT
static GHOST_TKey ghost_key_from_keycode(const XkbDescPtr xkb_descr, const KeyCode keycode)
int GHOST_X11_ApplicationIOErrorHandler(Display *)
static GHOST_TSuccess getCursorPosition_impl(Display *display, int32_t &x, int32_t &y, Window *child_return)
#define GHOST_INTERN_ATOM_IF_EXISTS(atom)
static GHOST_TKey ghost_key_from_keysym(const KeySym key)
int GHOST_X11_ApplicationErrorHandler(Display *display, XErrorEvent *event)
#define GHOST_X11_ERROR_HANDLERS_OVERRIDE(var)
int GHOST_X11_ApplicationIOErrorHandler(Display *display)
#define GHOST_X11_ERROR_HANDLERS_RESTORE(var)
@ GHOST_kEventWheelAxisVertical
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdate
@ GHOST_kEventWindowDeactivate
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kCapabilityInputIME
@ GHOST_kCapabilityClipboardImages
@ GHOST_kCapabilityWindowDecorationStyles
#define GHOST_CAPABILITY_FLAG_ALL
@ GHOST_kKeyNumpadAsterisk
@ GHOST_kModifierKeyRightControl
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyRightHyper
@ GHOST_kModifierKeyRightAlt
@ GHOST_kModifierKeyRightShift
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kModifierKeyLeftOS
@ GHOST_kModifierKeyRightOS
@ GHOST_kModifierKeyLeftHyper
@ GHOST_kButtonMaskButton4
@ GHOST_kButtonMaskButton7
@ GHOST_kButtonMaskButton6
@ GHOST_kButtonMaskButton5
@ GHOST_kButtonMaskMiddle
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
void drawButton(Display *display, Window &window, GC &borderGC, GC &buttonGC, uint button_num, const char *label)
bool isInsideButton(const XEvent &e, uint button_num) const
uint button_text_offset_y
static GHOST_ISystem * getSystem()
virtual bool isDebugEnabled()=0
char * getClipboard(bool selection) const override
void putClipboard(const char *buffer, bool selection) const override
void getClipboard_xcout(const XEvent *evt, Atom sel, Atom target, unsigned char **txt, unsigned long *len, unsigned int *context) const
~GHOST_SystemX11() override
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override
void addDirtyWindow(GHOST_WindowX11 *bad_wind)
Atom _NET_WM_STATE_MAXIMIZED_VERT
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override
GHOST_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_GPUSettings gpuSettings, const bool exclusive=false, const bool is_dialog=false, const GHOST_IWindow *parentWindow=nullptr) override
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override
GHOST_TCapabilityFlag getCapabilities() const override
bool processEvents(bool waitForEvent) override
Atom _NET_WM_STATE_FULLSCREEN
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override
uint8_t getNumDisplays() const override
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
uint64_t ms_from_input_time(const Time timestamp) const
Atom _NET_WM_STATE_MAXIMIZED_HORZ
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override
GHOST_IContext * createOffscreenContext(GHOST_GPUSettings gpuSettings) override
struct GHOST_SystemX11::@215064161053002243253223354161241141306252300203 m_atom
GHOST_TSuccess init() override
GHOST_TSuccess showMessageBox(const char *title, const char *message, const char *help_label, const char *continue_label, const char *link, GHOST_DialogOptions dialog_options) const override
uint64_t getMilliSeconds() const override
GHOST_TSuccess getPixelAtCursor(float r_color[3]) const override
GHOST_TSuccess pushEvent(const GHOST_IEvent *event)
GHOST_TSuccess init() override
GHOST_TimerManager * getTimerManager() const
GHOST_WindowManager * m_windowManager
GHOST_TSuccess exit() override
bool fireTimers(uint64_t time)
bool getValid() const override
GHOST_TabletData & GetTabletData()
GHOST_TWindowState m_post_state
GHOST_TSuccess setState(GHOST_TWindowState state) override
void getClientBounds(GHOST_Rect &bounds) const override
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) const override
void setCursorGrabAccum(int32_t x, int32_t y)
GHOST_TAxisFlag getCursorGrabAxis() const
GHOST_TGrabCursorMode getCursorGrabMode() const
bool getCursorGrabModeIsWarp() const
void getCursorGrabAccum(int32_t &x, int32_t &y) const
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
void set(GHOST_TModifierKey mask, bool down)