12#include <X11/XKBlib.h>
15#include <X11/keysym.h>
38#ifdef WITH_OPENGL_BACKEND
43#ifdef WITH_VULKAN_BACKEND
48# include <X11/XF86keysym.h>
52# include <X11/extensions/Xfixes.h>
54# define WITH_XWAYLAND_HACK
59# include <X11/extensions/XInput2.h>
75# define USE_XINPUT_HOTPLUG
79#define USE_UNITY_WORKAROUND
83#define USE_NON_LATIN_KB_WORKAROUND
87 return ptr[bit >> 3] & (1 << (bit & 7));
93 const XkbDescPtr xkb_descr,
94 const KeyCode keycode);
100#ifdef WITH_XWAYLAND_HACK
101static bool use_xwayland_hack =
false;
108 m_xkb_descr(nullptr),
109 m_keyboard_vector{0},
110#ifdef WITH_X11_XINPUT
113 m_keycode_last_repeat_key(
uint(-1))
116 m_display = XOpenDisplay(
nullptr);
119 throw std::runtime_error(
"unable to open a display!");
122#ifdef USE_X11_ERROR_HANDLERS
127#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
134#define GHOST_INTERN_ATOM_IF_EXISTS(atom) \
136 m_atom.atom = XInternAtom(m_display, #atom, True); \
139#define GHOST_INTERN_ATOM(atom) \
141 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
266 timespec ts = {0, 0};
267 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
268 GHOST_ASSERT(
false,
"Could not instantiate monotonic timer!");
284 static uint64_t timestamp_offset = 0;
289 static uint32_t timestamp_start = 0;
291 static bool is_time_init =
false;
296 const uint32_t timestamp_wrap_ms = 2000;
297 static uint32_t timestamp_offset_fake = 0;
299 timestamp_offset_fake =
UINT32_MAX - (timestamp + timestamp_wrap_ms);
301 timestamp =
uint32_t(timestamp + timestamp_offset_fake);
308 timestamp_offset = current_time;
309 timestamp_start = timestamp;
314 timestamp =
uint32_t(timestamp) - timestamp_start;
318 timestamp_prev = timestamp;
321 if (
UNLIKELY(timestamp < timestamp_prev)) {
327 timestamp_prev = timestamp;
331 return timestamp_final;
352 width = DisplayWidth(m_display, DefaultScreen(m_display));
353 height = DisplayHeight(m_display, DefaultScreen(m_display));
364 const bool exclusive,
365 const bool is_dialog,
412#ifdef WITH_VULKAN_BACKEND
413 case GHOST_kDrawingContextTypeVulkan: {
415 GHOST_kVulkanPlatformX11,
425 if (context->initializeDrawingContext()) {
433#ifdef WITH_OPENGL_BACKEND
434 case GHOST_kDrawingContextTypeOpenGL: {
435 for (
int minor = 6; minor >= 3; --minor) {
440 (GLXFBConfig)
nullptr,
441 GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
446 if (context->initializeDrawingContext()) {
468#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
469static void destroyIMCallback(XIM , XPointer
ptr, XPointer )
474 *(XIM *)
ptr =
nullptr;
478bool GHOST_SystemX11::openX11_IM()
485 XSetLocaleModifiers(
"");
487 m_xim = XOpenIM(m_display,
nullptr, (
char *)GHOST_X11_RES_NAME, (
char *)GHOST_X11_RES_CLASS);
493 destroy.callback = (XIMProc)destroyIMCallback;
494 destroy.client_data = (XPointer)&m_xim;
495 XSetIMValues(m_xim, XNDestroyCallback, &destroy,
nullptr);
517 for (; win_it != win_end; ++win_it) {
528 int fd = ConnectionNumber(display);
534 if (maxSleep == -1) {
535 select(fd + 1, &fds,
nullptr,
nullptr,
nullptr);
540 tv.tv_sec = maxSleep / 1000;
541 tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
543 select(fd + 1, &fds,
nullptr,
nullptr, &tv);
555 switch (event->type) {
561 data->timestamp =
event->xmotion.time;
565 data->timestamp =
event->xkey.time;
568 data->timestamp =
event->xproperty.time;
572 data->timestamp =
event->xcrossing.time;
575 data->timestamp =
event->xselectionclear.time;
584Time GHOST_SystemX11::lastEventTime(Time default_time)
591 return data.timestamp;
599 bool anyProcessed =
false;
604 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
623 while (XPending(m_display)) {
625 XNextEvent(m_display, &xevent);
627#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
631 if (
ELEM(xevent.type, FocusIn, KeyPress)) {
632 if (!m_xim && openX11_IM()) {
638 if (window && !window->getX11_XIC() && window->createX11_XIC()) {
640 if (xevent.type == KeyPress) {
644 XSetICFocus(window->getX11_XIC());
651 if (
ELEM(xevent.type, KeyPress, KeyRelease)) {
652 if (xevent.xkey.time != 0) {
653 m_last_key_time = xevent.xkey.time;
658 if (XFilterEvent(&xevent, (
Window)
nullptr) == True) {
665 if (xevent.type == KeyRelease) {
666 m_last_release_keycode = xevent.xkey.keycode;
667 m_last_release_time = xevent.xkey.time;
669 else if (xevent.type == KeyPress) {
670 if ((xevent.xkey.keycode == m_last_release_keycode) &&
671 (xevent.xkey.time <= m_last_release_time))
677 processEvent(&xevent);
680#ifdef USE_UNITY_WORKAROUND
687 if (xevent.type == FocusIn) {
691 if (window && XPending(m_display) >= 2) {
692 XNextEvent(m_display, &xevent);
694 if (xevent.type == KeymapNotify) {
699 XPeekEvent(m_display, &xev_next);
701 if (
ELEM(xev_next.type, KeyPress, KeyRelease)) {
704 const static KeySym modifiers[] = {
716 KeyCode kc = XKeysymToKeycode(m_display, modifiers[i]);
717 if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
733 if (generateWindowExposeEvents()) {
737#ifdef WITH_INPUT_NDOF
743 }
while (waitForEvent && !anyProcessed);
748#ifdef WITH_X11_XINPUT
749static bool checkTabletProximity(Display *display, XDevice *device)
756 if (device ==
nullptr) {
763 state = XQueryDeviceState(display, device);
768 XInputClass *cls =
state->data;
770 for (
int loop = 0; loop <
state->num_classes; loop++) {
771 switch (cls->c_class) {
773 XValuatorState *val_state = (XValuatorState *)cls;
778 if ((val_state->mode & 2) == 0) {
779 XFreeDeviceState(
state);
784 cls = (XInputClass *)((
char *)cls + cls->length);
786 XFreeDeviceState(
state);
792void GHOST_SystemX11::processEvent(XEvent *xe)
798 bool is_repeat =
false;
799 if (
ELEM(xe->type, KeyPress, KeyRelease)) {
800 XKeyEvent *xke = &(xe->xkey);
803 bool is_repeat_keycode =
false;
805 if (m_xkb_descr !=
nullptr) {
807 is_repeat_keycode = (
809 (xke->keycode < (XkbPerKeyBitArraySize << 3)) &&
810 bit_is_on(m_xkb_descr->ctrls->per_key_repeat, xke->keycode));
814 switch (XLookupKeysym(xke, 0)) {
831 is_repeat_keycode =
true;
836 if (is_repeat_keycode) {
837 if (xe->type == KeyPress) {
838 if (m_keycode_last_repeat_key == xke->keycode) {
841 m_keycode_last_repeat_key = xke->keycode;
844 if (m_keycode_last_repeat_key == xke->keycode) {
845 m_keycode_last_repeat_key =
uint(-1);
850 else if (xe->type == EnterNotify) {
852 m_keycode_last_repeat_key =
uint(-1);
855#ifdef USE_XINPUT_HOTPLUG
857 if (m_xinput_version.present) {
858 XEventClass class_presence;
861 DevicePresence(m_display, xi_presence, class_presence);
862 (void)class_presence;
864 if (xe->type == xi_presence) {
865 const XDevicePresenceNotifyEvent *notify_event = (
const XDevicePresenceNotifyEvent *)xe;
868 refreshXInputDevices();
876 for (; win_it != win_end; ++win_it) {
878 window_xinput->refreshXInputDevices();
890#ifdef WITH_X11_XINPUT
897 bool any_proximity =
false;
899 for (
const GHOST_TabletX11 &xtablet : m_xtablets) {
900 if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
901 any_proximity =
true;
905 if (!any_proximity) {
913 const XExposeEvent &xee = xe->xexpose;
915 if (xee.count == 0) {
927 const XMotionEvent &xme = xe->xmotion;
948 const int32_t subregion_div = 4;
955 bounds.m_l = center[0] - (size[0] / (subregion_div * 2));
956 bounds.m_r = center[0] + (size[0] / (subregion_div * 2));
957 bounds.m_t = center[1] - (size[1] / (subregion_div * 2));
958 bounds.m_b = center[1] + (size[1] / (subregion_div * 2));
975 bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
980 if (x_new != xme.x_root || y_new != xme.y_root) {
987 if (x_new != xme.x_root && xme.time > m_last_warp_x) {
988 x_accum += (xme.x_root - x_new);
989 m_last_warp_x = lastEventTime(xme.time) + 25;
991 if (y_new != xme.y_root && xme.time > m_last_warp_y) {
992 y_accum += (xme.y_root - y_new);
993 m_last_warp_y = lastEventTime(xme.time) + 25;
1004 xme.x_root + x_accum,
1005 xme.y_root + y_accum,
1022 XKeyEvent *xke = &(xe->xkey);
1023#ifdef WITH_X11_XINPUT
1025 const Time time = xke->time ? xke->time : m_last_key_time;
1027 const Time time = xke->time;
1032 char *utf8_buf =
nullptr;
1035#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1042 char utf8_array[16 * 6 + 5];
1051#ifdef USE_NON_LATIN_KB_WORKAROUND
1077 const uint mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch);
1078 const uint number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask;
1079 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1080 ((xke->state & number_hack_forbidden_kmods_mask) == 0))
1082 key_sym = XLookupKeysym(xke, ShiftMask);
1083 if (!((key_sym >= XK_0) && (key_sym <= XK_9))) {
1084 key_sym = XLookupKeysym(xke, 0);
1088 key_sym = XLookupKeysym(xke, 0);
1091 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) {
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) {
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 Atom target, utf8_string, string, compound_text, c_string;
1469 XSelectionRequestEvent *xse = &xe->xselectionrequest;
1471 target = XInternAtom(m_display,
"TARGETS", False);
1472 utf8_string = XInternAtom(m_display,
"UTF8_STRING", False);
1473 string = XInternAtom(m_display,
"STRING", False);
1474 compound_text = XInternAtom(m_display,
"COMPOUND_TEXT", False);
1475 c_string = XInternAtom(m_display,
"C_STRING", False);
1478 if (xse->property ==
None) {
1479 xse->property = xse->target;
1482 nxe.xselection.type = SelectionNotify;
1483 nxe.xselection.requestor = xse->requestor;
1484 nxe.xselection.property = xse->property;
1485 nxe.xselection.display = xse->display;
1486 nxe.xselection.selection = xse->selection;
1487 nxe.xselection.target = xse->target;
1488 nxe.xselection.time = xse->time;
1491 if (
ELEM(xse->target, utf8_string,
string, compound_text, c_string)) {
1492 if (xse->selection == XInternAtom(m_display,
"PRIMARY", False)) {
1493 XChangeProperty(m_display,
1502 else if (xse->selection == XInternAtom(m_display,
"CLIPBOARD", False)) {
1503 XChangeProperty(m_display,
1513 else if (xse->target == target) {
1516 alist[1] = utf8_string;
1518 alist[3] = compound_text;
1519 alist[4] = c_string;
1520 XChangeProperty(m_display,
1532 nxe.xselection.property =
None;
1536 XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
1542#ifdef WITH_X11_XINPUT
1543 for (GHOST_TabletX11 &xtablet : m_xtablets) {
1544 if (
ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
1545 const XDeviceMotionEvent *data = (
const XDeviceMotionEvent *)xe;
1546 if (data->deviceid != xtablet.ID) {
1550 const uchar axis_first = data->first_axis;
1551 const uchar axes_end = axis_first + data->axes_count;
1565# define AXIS_VALUE_GET(axis, val) \
1566 ((axis_first <= axis && axes_end > axis) && \
1567 ((void)(val = data->axis_data[axis - axis_first]), true))
1569 if (AXIS_VALUE_GET(2, axis_value)) {
1581 if (AXIS_VALUE_GET(3, axis_value)) {
1583 float(xtablet.XtiltLevels);
1585 if (AXIS_VALUE_GET(4, axis_value)) {
1587 float(xtablet.YtiltLevels);
1590# undef AXIS_VALUE_GET
1592 else if (xe->type == xtablet.ProxInEvent) {
1593 const XProximityNotifyEvent *data = (
const XProximityNotifyEvent *)xe;
1594 if (data->deviceid != xtablet.ID) {
1600 else if (xe->type == xtablet.ProxOutEvent) {
1631 XImage *image = XGetImage(m_display,
1632 XRootWindow(m_display, XDefaultScreen(m_display)),
1639 if (image ==
nullptr) {
1642 c.pixel = XGetPixel(image, 0, 0);
1644 XQueryColor(m_display, XDefaultColormap(m_display, XDefaultScreen(m_display)), &c);
1647 r_color[0] = c.red / 65535.0f;
1648 r_color[1] = c.green / 65535.0f;
1649 r_color[2] = c.blue / 65535.0f;
1658 memset((
void *)m_keyboard_vector, 0,
sizeof(m_keyboard_vector));
1660 XQueryKeymap(m_display, (
char *)m_keyboard_vector);
1664 const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L);
1665 const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R);
1666 const static KeyCode control_l = XKeysymToKeycode(m_display, XK_Control_L);
1667 const static KeyCode control_r = XKeysymToKeycode(m_display, XK_Control_R);
1668 const static KeyCode alt_l = XKeysymToKeycode(m_display, XK_Alt_L);
1669 const static KeyCode alt_r = XKeysymToKeycode(m_display, XK_Alt_R);
1670 const static KeyCode super_l = XKeysymToKeycode(m_display, XK_Super_L);
1671 const static KeyCode super_r = XKeysymToKeycode(m_display, XK_Super_R);
1675 ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
1677 ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
1680 ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
1682 ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
1688 ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) != 0);
1690 ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) != 0);
1697 Window root_return, child_return;
1701 if (XQueryPointer(m_display,
1702 RootWindow(m_display, DefaultScreen(m_display)),
1709 &mask_return) == True)
1733 if (XQueryPointer(display,
1734 RootWindow(display, DefaultScreen(display)),
1741 &mask_return) == False)
1767#ifdef WITH_XWAYLAND_HACK
1781#ifdef WITH_XWAYLAND_HACK
1782 if (use_xwayland_hack) {
1783 if (child_return !=
None) {
1784 XFixesHideCursor(m_display, child_return);
1789#if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1790 if ((m_xinput_version.present) && (m_xinput_version.major_version >= 2)) {
1793 if (XIGetClientPointer(m_display,
None, &device_id) != False) {
1794 XIWarpPointer(m_display, device_id,
None,
None, 0, 0, 0, 0, relx, rely);
1800 XWarpPointer(m_display,
None,
None, 0, 0, 0, 0, relx, rely);
1803#ifdef WITH_XWAYLAND_HACK
1804 if (use_xwayland_hack) {
1805 if (child_return !=
None) {
1806 XFixesShowCursor(m_display, child_return);
1811 XSync(m_display, 0);
1828 GHOST_ASSERT((bad_wind !=
nullptr),
"addDirtyWindow() nullptr ptr trapped (window)");
1830 m_dirty_windows.push_back(bad_wind);
1833bool GHOST_SystemX11::generateWindowExposeEvents()
1837 bool anyProcessed =
false;
1839 for (; w_start != w_end; ++w_start) {
1843 (*w_start)->validate();
1847 anyProcessed =
true;
1851 m_dirty_windows.clear();
1852 return anyProcessed;
1856 XkbDescPtr xkb_descr,
1857 const KeyCode keycode)
1868#define GXMAP(k, x, y) \
1877 if ((key >= XK_A) && (key <= XK_Z)) {
1880 else if ((key >= XK_a) && (key <= XK_z)) {
1883 else if ((key >= XK_0) && (key <= XK_9)) {
1886 else if ((key >= XK_F1) && (key <= XK_F24)) {
1972#ifdef WITH_XF86KEYSYM
1978# ifdef XF86XK_AudioForward
1983#ifdef WITH_GHOST_DEBUG
1984 printf(
"%s: unknown key: %lu / 0x%lx\n", __func__, key, key);
1996#define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
2000 GHOST_ASSERT(XkbKeyNameLength == 4,
"Name length is invalid!");
2001 if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) {
2002 const char *id_str = xkb_descr->names->keys[keycode].name;
2003 const uint32_t id =
MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]);
2005 case MAKE_ID(
'T',
'L',
'D',
'E'):
2007 case MAKE_ID(
'L',
'S',
'G',
'T'):
2009#ifdef WITH_GHOST_DEBUG
2011 printf(
"%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str);
2016 else if (keycode != 0) {
2026#define XCLIB_XCOUT_NONE 0
2027#define XCLIB_XCOUT_SENTCONVSEL 1
2028#define XCLIB_XCOUT_INCR 2
2029#define XCLIB_XCOUT_FALLBACK 3
2030#define XCLIB_XCOUT_FALLBACK_UTF8 4
2031#define XCLIB_XCOUT_FALLBACK_COMP 5
2032#define XCLIB_XCOUT_FALLBACK_TEXT 6
2036 const XEvent *evt, Atom sel, Atom target,
uchar **txt,
ulong *
len,
uint *context)
const
2041 ulong pty_size, pty_items;
2045 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2059 XConvertSelection(m_display, sel, target,
m_atom.XCLIP_OUT, win, CurrentTime);
2064 if (evt->type != SelectionNotify) {
2068 if (target ==
m_atom.UTF8_STRING && evt->xselection.property ==
None) {
2072 if (target ==
m_atom.COMPOUND_TEXT && evt->xselection.property ==
None) {
2076 if (target ==
m_atom.TEXT && evt->xselection.property ==
None) {
2082 XGetWindowProperty(m_display,
2096 if (pty_type ==
m_atom.INCR) {
2098 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2107 if (pty_format != 8) {
2113 XGetWindowProperty(m_display,
2127 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2130 ltxt = (
uchar *)malloc(pty_items);
2131 memcpy(ltxt, buffer, pty_items);
2152 if (evt->type != PropertyNotify) {
2157 if (evt->xproperty.state != PropertyNewValue) {
2162 XGetWindowProperty(m_display,
2175 if (pty_format != 8) {
2180 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2184 if (pty_size == 0) {
2187 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2199 XGetWindowProperty(m_display,
2219 ltxt = (
uchar *)realloc(ltxt, *
len);
2223 memcpy(<xt[*
len - pty_items], buffer, pty_items);
2229 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2238 Atom target =
m_atom.UTF8_STRING;
2247 if (selection == True) {
2251 sseln =
m_atom.CLIPBOARD;
2255 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2260 owner = XGetSelectionOwner(m_display, sseln);
2262 if (sseln ==
m_atom.CLIPBOARD) {
2264 sel_buf = (
char *)malloc(sel_buf_size);
2269 sel_buf = (
char *)malloc(sel_buf_size);
2273 if (owner ==
None) {
2278 vector<XEvent> restore_events;
2282 bool restore_this_event =
false;
2284 XNextEvent(m_display, &evt);
2285 restore_this_event = (evt.type != SelectionNotify);
2291 if (restore_this_event) {
2292 restore_events.push_back(evt);
2304 target =
m_atom.COMPOUND_TEXT;
2324 while (!restore_events.empty()) {
2325 XPutBackEvent(m_display, &restore_events.back());
2326 restore_events.pop_back();
2331 char *tmp_data = (
char *)malloc(sel_len + 1);
2332 memcpy(tmp_data, (
char *)sel_buf, sel_len);
2333 tmp_data[sel_len] =
'\0';
2335 if (sseln ==
m_atom.STRING) {
2352 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2357 if (selection == False) {
2358 XSetSelectionOwner(m_display,
m_atom.CLIPBOARD, m_window, CurrentTime);
2359 owner = XGetSelectionOwner(m_display,
m_atom.CLIPBOARD);
2364 size_t buffer_size = strlen(buffer) + 1;
2369 XSetSelectionOwner(m_display,
m_atom.PRIMARY, m_window, CurrentTime);
2370 owner = XGetSelectionOwner(m_display,
m_atom.PRIMARY);
2375 size_t buffer_size = strlen(buffer) + 1;
2380 if (owner != m_window) {
2381 fprintf(stderr,
"failed to own primary\n");
2435 XFillRectangle(display,
2443 XFillRectangle(display,
2451 XDrawString(display,
2478 data = strdup(text);
2479 for (tok = strtok(data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps)) {
2484 data = strdup(text);
2485 *
str = (
char **)malloc(
size_t(*
count) *
sizeof(
char *));
2486 for (i = 0, tok = strtok(data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps), i++) {
2487 (*str)[i] = strdup(tok);
2493 const char *message,
2494 const char *help_label,
2495 const char *continue_label,
2499 char **text_splitted =
nullptr;
2501 split(message,
"\n", &text_splitted, &textLines);
2508 int screen = DefaultScreen(m_display);
2509 window = XCreateSimpleWindow(m_display,
2510 RootWindow(m_display, screen),
2516 BlackPixel(m_display, screen),
2517 WhitePixel(m_display, screen));
2521 hints.flags = PSize | PMinSize | PMaxSize;
2522 hints.min_width = hints.max_width = hints.base_width = dialog_data.
width;
2523 hints.min_height = hints.max_height = hints.base_height = dialog_data.
height;
2524 XSetWMNormalHints(m_display, window, &hints);
2529 Atom wm_Name = XInternAtom(m_display,
"_NET_WM_NAME", False);
2530 Atom utf8Str = XInternAtom(m_display,
"UTF8_STRING", False);
2532 Atom winType = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE", False);
2533 Atom typeDialog = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE_DIALOG", False);
2535 XChangeProperty(m_display,
2541 (
const uchar *)title,
2542 int(strlen(title)));
2545 m_display, window, winType, XA_ATOM, 32, PropModeReplace, (
uchar *)&typeDialog, 1);
2549 XGCValues buttonBorderGCValues;
2550 buttonBorderGCValues.foreground = BlackPixel(m_display, screen);
2551 buttonBorderGCValues.background = WhitePixel(m_display, screen);
2552 XGCValues buttonGCValues;
2553 buttonGCValues.foreground = WhitePixel(m_display, screen);
2554 buttonGCValues.background = BlackPixel(m_display, screen);
2556 GC buttonBorderGC = XCreateGC(m_display, window, GCForeground, &buttonBorderGCValues);
2557 GC buttonGC = XCreateGC(m_display, window, GCForeground, &buttonGCValues);
2559 XSelectInput(m_display, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
2560 XMapWindow(m_display, window);
2562 const bool has_link = link && strlen(link);
2565 XNextEvent(m_display, &
e);
2566 if (
e.type == Expose) {
2567 for (
int i = 0; i < textLines; i++) {
2568 XDrawString(m_display,
2570 DefaultGC(m_display, screen),
2574 int(strlen(text_splitted[i])));
2576 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 1, continue_label);
2578 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 2, help_label);
2581 else if (
e.type == ButtonRelease) {
2587 string cmd =
"xdg-open \"" + string(link) +
"\"";
2588 if (system(cmd.c_str()) != 0) {
2589 GHOST_PRINTF(
"GHOST_SystemX11::showMessageBox: Unable to run system command [%s]",
2598 for (
int i = 0; i < textLines; i++) {
2599 free(text_splitted[i]);
2601 free(text_splitted);
2603 XDestroyWindow(m_display, window);
2604 XFreeGC(m_display, buttonBorderGC);
2605 XFreeGC(m_display, buttonGC);
2625 event_ms, eventType, draggedObjectType, window, mouseX, mouseY, data));
2642 char error_code_str[512];
2644 XGetErrorText(display, event->error_code, error_code_str,
sizeof(error_code_str));
2647 "Received X11 Error:\n"
2648 "\terror code: %d\n"
2649 "\trequest code: %d\n"
2650 "\tminor code: %d\n"
2651 "\terror text: %s\n",
2653 event->request_code,
2668 fprintf(stderr,
"Ignoring Xlib error: error IO\n");
2674#ifdef WITH_X11_XINPUT
2676static bool is_filler_char(
char c)
2678 return isspace(c) ||
ELEM(c,
'_',
'-',
';',
':');
2682static bool match_token(
const char *haystack,
const char *needle)
2685 for (h = haystack; *h;) {
2686 while (*h && is_filler_char(*h)) {
2693 for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) {
2696 if (!*n && (is_filler_char(*h) || !*h)) {
2700 while (*h && !is_filler_char(*h)) {
2717static GHOST_TTabletMode tablet_mode_from_name(
const char *name,
const char *type)
2720 static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
"pen",
nullptr};
2722 static const char *type_blacklist[] = {
"pad",
"cursor",
"touch",
nullptr};
2725 for (i = 0; type_blacklist[i] !=
nullptr; i++) {
2726 if (type && (strcasecmp(type, type_blacklist[i]) == 0)) {
2732 for (i = 0; tablet_stylus_whitelist[i] !=
nullptr; i++) {
2733 if (type && match_token(type, tablet_stylus_whitelist[i])) {
2737 if (type && match_token(type,
"eraser")) {
2740 for (i = 0; tablet_stylus_whitelist[i] !=
nullptr; i++) {
2741 if (name && match_token(name, tablet_stylus_whitelist[i])) {
2745 if (name && match_token(name,
"eraser")) {
2754void GHOST_SystemX11::refreshXInputDevices()
2756 if (m_xinput_version.present) {
2758 clearXInputDevices();
2765 XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
2767 for (
int i = 0; i < device_count; ++i) {
2768 char *device_type = device_info[i].type ? XGetAtomName(m_display, device_info[i].type) :
2770 GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[i].name, device_type);
2775 XFree((
void *)device_type);
2782 GHOST_TabletX11 xtablet = {tablet_mode};
2783 xtablet.ID = device_info[i].id;
2784 xtablet.Device = XOpenDevice(m_display, xtablet.ID);
2786 if (xtablet.Device !=
nullptr) {
2788 XAnyClassPtr ici = device_info[i].inputclassinfo;
2790 if (ici !=
nullptr) {
2791 for (
int j = 0; j < device_info[i].num_classes; ++j) {
2792 if (ici->c_class == ValuatorClass) {
2793 XValuatorInfo *xvi = (XValuatorInfo *)ici;
2794 if (xvi->axes !=
nullptr) {
2795 xtablet.PressureLevels = xvi->axes[2].max_value;
2797 if (xvi->num_axes > 3) {
2800 xtablet.XtiltLevels = xvi->axes[3].max_value;
2801 xtablet.YtiltLevels = xvi->axes[4].max_value;
2804 xtablet.XtiltLevels = 0;
2805 xtablet.YtiltLevels = 0;
2812 ici = (XAnyClassPtr)(((
char *)ici) + ici->length);
2816 m_xtablets.push_back(xtablet);
2820 XFreeDeviceList(device_info);
2827void GHOST_SystemX11::clearXInputDevices()
2829 for (GHOST_TabletX11 &xtablet : m_xtablets) {
2830 if (xtablet.Device) {
2831 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_kEventWindowClose
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdate
@ GHOST_kEventWindowDeactivate
@ GHOST_kTabletModeEraser
@ GHOST_kTabletModeStylus
@ GHOST_kCapabilityInputIME
@ GHOST_kCapabilityClipboardImages
#define GHOST_CAPABILITY_FLAG_ALL
@ GHOST_kKeyNumpadAsterisk
@ GHOST_kModifierKeyRightControl
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyRightAlt
@ GHOST_kModifierKeyRightShift
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kModifierKeyLeftOS
@ GHOST_kModifierKeyRightOS
@ GHOST_kButtonMaskButton4
@ GHOST_kButtonMaskButton7
@ GHOST_kButtonMaskButton6
@ GHOST_kButtonMaskButton5
@ GHOST_kButtonMaskMiddle
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
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)
struct GHOST_SystemX11::@1504 m_atom
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
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
virtual GHOST_TSuccess exit()
GHOST_TSuccess pushEvent(const GHOST_IEvent *event)
virtual GHOST_TSuccess init()
GHOST_TimerManager * getTimerManager() const
GHOST_WindowManager * m_windowManager
GHOST_DisplayManager * m_displayManager
bool fireTimers(uint64_t time)
GHOST_TSuccess addWindow(GHOST_IWindow *window)
const std::vector< GHOST_IWindow * > & getWindows() const
GHOST_TSuccess setActiveWindow(GHOST_IWindow *window)
void setWindowInactive(const GHOST_IWindow *window)
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
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
unsigned __int64 uint64_t
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
void set(GHOST_TModifierKey mask, bool down)