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); \
164#ifdef WITH_X11_XINPUT
165 m_atom.TABLET = XInternAtom(m_display, XI_TABLET, False);
168#undef GHOST_INTERN_ATOM_IF_EXISTS
169#undef GHOST_INTERN_ATOM
173 m_last_release_keycode = 0;
174 m_last_release_time = 0;
178 int xkb_opcode, xkb_event, xkb_error;
179 int xkb_major = XkbMajorVersion, xkb_minor = XkbMinorVersion;
181 use_xkb = XkbQueryExtension(
182 m_display, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
184 XkbSetDetectableAutoRepeat(m_display,
true,
nullptr);
186 m_xkb_descr = XkbGetMap(m_display, 0, XkbUseCoreKbd);
188 XkbGetNames(m_display, XkbKeyNamesMask, m_xkb_descr);
189 XkbGetControls(m_display, XkbPerKeyRepeatMask | XkbRepeatKeysMask, m_xkb_descr);
193#ifdef WITH_XWAYLAND_HACK
194 use_xwayland_hack = getenv(
"WAYLAND_DISPLAY") !=
nullptr;
197#ifdef WITH_X11_XINPUT
200 memset(&m_xinput_version, 0,
sizeof(m_xinput_version));
201 XExtensionVersion *version = XGetExtensionVersion(m_display, INAME);
202 if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
203 if (version->present) {
204 m_xinput_version = *version;
210# ifdef USE_XINPUT_HOTPLUG
211 if (m_xinput_version.present) {
212 XEventClass class_presence;
214 DevicePresence(m_display, xi_presence, class_presence);
215 XSelectExtensionEvent(
216 m_display, RootWindow(m_display, DefaultScreen(m_display)), &class_presence, 1);
221 refreshXInputDevices();
227#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
233#ifdef WITH_X11_XINPUT
235 clearXInputDevices();
239 XkbFreeKeyboard(m_xkb_descr, XkbAllComponentsMask,
true);
242 XCloseDisplay(m_display);
250#ifdef WITH_INPUT_NDOF
261 timespec ts = {0, 0};
262 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
263 GHOST_ASSERT(
false,
"Could not instantiate monotonic timer!");
279 static uint64_t timestamp_offset = 0;
282 static uint32_t timestamp_prev = 0;
284 static uint32_t timestamp_start = 0;
286 static bool is_time_init =
false;
291 const uint32_t timestamp_wrap_ms = 2000;
292 static uint32_t timestamp_offset_fake = 0;
294 timestamp_offset_fake =
UINT32_MAX - (timestamp + timestamp_wrap_ms);
296 timestamp = uint32_t(timestamp + timestamp_offset_fake);
303 timestamp_offset = current_time;
304 timestamp_start = timestamp;
309 timestamp = uint32_t(timestamp) - timestamp_start;
313 timestamp_prev = timestamp;
316 if (
UNLIKELY(timestamp < timestamp_prev)) {
322 timestamp_prev = timestamp;
326 return timestamp_final;
347 width = DisplayWidth(m_display, DefaultScreen(m_display));
348 height = DisplayHeight(m_display, DefaultScreen(m_display));
359 const bool exclusive,
360 const bool is_dialog,
407#ifdef WITH_VULKAN_BACKEND
408 case GHOST_kDrawingContextTypeVulkan: {
410 GHOST_kVulkanPlatformX11,
420 if (context->initializeDrawingContext()) {
428#ifdef WITH_OPENGL_BACKEND
429 case GHOST_kDrawingContextTypeOpenGL: {
430 for (
int minor = 6; minor >= 3; --minor) {
435 (GLXFBConfig)
nullptr,
436 GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
441 if (context->initializeDrawingContext()) {
463#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
464static void destroyIMCallback(XIM , XPointer
ptr, XPointer )
469 *(XIM *)
ptr =
nullptr;
473bool GHOST_SystemX11::openX11_IM()
480 XSetLocaleModifiers(
"");
482 m_xim = XOpenIM(m_display,
nullptr, (
char *)GHOST_X11_RES_NAME, (
char *)GHOST_X11_RES_CLASS);
488 destroy.callback = (XIMProc)destroyIMCallback;
489 destroy.client_data = (XPointer)&m_xim;
490 XSetIMValues(m_xim, XNDestroyCallback, &destroy,
nullptr);
507 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
509 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
510 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
512 for (; win_it != win_end; ++win_it) {
513 GHOST_WindowX11 *window =
static_cast<GHOST_WindowX11 *
>(*win_it);
523 int fd = ConnectionNumber(display);
529 if (maxSleep == -1) {
530 select(fd + 1, &fds,
nullptr,
nullptr,
nullptr);
535 tv.tv_sec = maxSleep / 1000;
536 tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
538 select(fd + 1, &fds,
nullptr,
nullptr, &tv);
550 switch (event->type) {
553 data->timestamp =
event->xbutton.time;
556 data->timestamp =
event->xmotion.time;
560 data->timestamp =
event->xkey.time;
563 data->timestamp =
event->xproperty.time;
567 data->timestamp =
event->xcrossing.time;
570 data->timestamp =
event->xselectionclear.time;
579Time GHOST_SystemX11::lastEventTime(Time default_time)
581 init_timestamp_data
data;
582 data.timestamp = default_time;
586 return data.timestamp;
594 bool anyProcessed =
false;
599 if (waitForEvent && m_dirty_windows.empty() && !XPending(m_display)) {
618 while (XPending(m_display)) {
620 XNextEvent(m_display, &xevent);
622#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
626 if (
ELEM(xevent.type, FocusIn, KeyPress)) {
627 if (!m_xim && openX11_IM()) {
633 if (window && !window->getX11_XIC() && window->createX11_XIC()) {
635 if (xevent.type == KeyPress) {
639 XSetICFocus(window->getX11_XIC());
646 if (
ELEM(xevent.type, KeyPress, KeyRelease)) {
647 if (xevent.xkey.time != 0) {
648 m_last_key_time = xevent.xkey.time;
653 if (XFilterEvent(&xevent, (
Window)
nullptr) == True) {
660 if (xevent.type == KeyRelease) {
661 m_last_release_keycode = xevent.xkey.keycode;
662 m_last_release_time = xevent.xkey.time;
664 else if (xevent.type == KeyPress) {
665 if ((xevent.xkey.keycode == m_last_release_keycode) &&
666 (xevent.xkey.time <= m_last_release_time))
672 processEvent(&xevent);
675#ifdef USE_UNITY_WORKAROUND
682 if (xevent.type == FocusIn) {
686 if (window && XPending(m_display) >= 2) {
687 XNextEvent(m_display, &xevent);
689 if (xevent.type == KeymapNotify) {
694 XPeekEvent(m_display, &xev_next);
696 if (
ELEM(xev_next.type, KeyPress, KeyRelease)) {
699 const static KeySym modifiers[] = {
713 KeyCode kc = XKeysymToKeycode(m_display, modifiers[
i]);
714 if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
730 if (generateWindowExposeEvents()) {
734#ifdef WITH_INPUT_NDOF
740 }
while (waitForEvent && !anyProcessed);
745#ifdef WITH_X11_XINPUT
746static bool checkTabletProximity(
Display *display, XDevice *device)
753 if (device ==
nullptr) {
760 state = XQueryDeviceState(display, device);
765 XInputClass *cls =
state->data;
767 for (
int loop = 0; loop <
state->num_classes; loop++) {
768 switch (cls->c_class) {
770 XValuatorState *val_state = (XValuatorState *)cls;
775 if ((val_state->mode & 2) == 0) {
776 XFreeDeviceState(
state);
781 cls = (XInputClass *)((
char *)cls + cls->length);
783 XFreeDeviceState(
state);
789void GHOST_SystemX11::processEvent(XEvent *xe)
791 GHOST_WindowX11 *window = findGhostWindow(xe->xany.window);
792 GHOST_Event *g_event =
nullptr;
795 bool is_repeat =
false;
796 if (
ELEM(xe->type, KeyPress, KeyRelease)) {
797 XKeyEvent *xke = &(xe->xkey);
800 bool is_repeat_keycode =
false;
802 if (m_xkb_descr !=
nullptr) {
804 is_repeat_keycode = (
806 (xke->keycode < (XkbPerKeyBitArraySize << 3)) &&
807 bit_is_on(m_xkb_descr->ctrls->per_key_repeat, xke->keycode));
811 switch (XLookupKeysym(xke, 0)) {
828 is_repeat_keycode =
true;
833 if (is_repeat_keycode) {
834 if (xe->type == KeyPress) {
835 if (m_keycode_last_repeat_key == xke->keycode) {
838 m_keycode_last_repeat_key = xke->keycode;
841 if (m_keycode_last_repeat_key == xke->keycode) {
842 m_keycode_last_repeat_key =
uint(-1);
847 else if (xe->type == EnterNotify) {
849 m_keycode_last_repeat_key =
uint(-1);
852#ifdef USE_XINPUT_HOTPLUG
854 if (m_xinput_version.present) {
855 XEventClass class_presence;
858 DevicePresence(m_display, xi_presence, class_presence);
859 (void)class_presence;
861 if (xe->type == xi_presence) {
862 const XDevicePresenceNotifyEvent *notify_event = (
const XDevicePresenceNotifyEvent *)xe;
865 refreshXInputDevices();
869 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
870 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
871 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
873 for (; win_it != win_end; ++win_it) {
874 GHOST_WindowX11 *window_xinput =
static_cast<GHOST_WindowX11 *
>(*win_it);
875 window_xinput->refreshXInputDevices();
887#ifdef WITH_X11_XINPUT
894 bool any_proximity =
false;
896 for (
const GHOST_TabletX11 &xtablet : m_xtablets) {
897 if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
898 any_proximity =
true;
902 if (!any_proximity) {
910 const XExposeEvent &xee = xe->xexpose;
912 if (xee.count == 0) {
924 const XMotionEvent &xme = xe->xmotion;
945 const int32_t subregion_div = 4;
952 bounds.m_l = center[0] - (
size[0] / (subregion_div * 2));
953 bounds.m_r = center[0] + (
size[0] / (subregion_div * 2));
954 bounds.m_t = center[1] - (
size[1] / (subregion_div * 2));
955 bounds.m_b = center[1] + (
size[1] / (subregion_div * 2));
972 bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
977 if (x_new != xme.x_root || y_new != xme.y_root) {
984 if (x_new != xme.x_root && xme.time > m_last_warp_x) {
985 x_accum += (xme.x_root - x_new);
986 m_last_warp_x = lastEventTime(xme.time) + 25;
988 if (y_new != xme.y_root && xme.time > m_last_warp_y) {
989 y_accum += (xme.y_root - y_new);
990 m_last_warp_y = lastEventTime(xme.time) + 25;
998 g_event =
new GHOST_EventCursor(event_ms,
1001 xme.x_root + x_accum,
1002 xme.y_root + y_accum,
1007 g_event =
new GHOST_EventCursor(event_ms,
1019 XKeyEvent *xke = &(xe->xkey);
1020#ifdef WITH_X11_XINPUT
1022 const Time time = xke->time ? xke->time : m_last_key_time;
1024 const Time time = xke->time;
1029 char *utf8_buf =
nullptr;
1032#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1039 char utf8_array[16 * 6 + 5];
1048#ifdef USE_NON_LATIN_KB_WORKAROUND
1074 const uint mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch);
1075 const uint number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask;
1076 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1077 ((xke->state & number_hack_forbidden_kmods_mask) == 0))
1079 key_sym = XLookupKeysym(xke, ShiftMask);
1080 if (!((key_sym >= XK_0) && (key_sym <= XK_9))) {
1081 key_sym = XLookupKeysym(xke, 0);
1085 key_sym = XLookupKeysym(xke, 0);
1088 if (!XLookupString(xke, &ascii, 1, &key_sym_str,
nullptr)) {
1150 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1151 ((key_sym = XLookupKeysym(xke, ShiftMask)) >= XK_0) && (key_sym <= XK_9))
1157 key_sym = XLookupKeysym(xke, 0);
1162 if (!XLookupString(xke, &ascii, 1,
nullptr,
nullptr)) {
1167#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1172 if (xke->type == KeyPress) {
1173 utf8_buf = utf8_array;
1174#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1176 xic = window->getX11_XIC();
1181 if (!(
len = Xutf8LookupString(
1182 xic, xke, utf8_buf,
sizeof(utf8_array) - 5, &key_sym, &status)))
1187 if (status == XBufferOverflow) {
1188 utf8_buf = (
char *)malloc(
len + 5);
1189 len = Xutf8LookupString(xic, xke, utf8_buf,
len, &key_sym, &status);
1192 if (
ELEM(status, XLookupChars, XLookupBoth)) {
1195 if ((utf8_buf[0] < 32 && utf8_buf[0] > 0) || (utf8_buf[0] == 127)) {
1199 else if (status == XLookupKeySym) {
1204 printf(
"Bad keycode lookup. Keysym 0x%x Status: %s\n",
1206 (status == XLookupNone ?
"XLookupNone" :
1207 status == XLookupKeySym ?
"XLookupKeySym" :
1210 printf(
"'%.*s' %p %p\n",
len, utf8_buf, xic, m_xim);
1217 if (!utf8_buf[0] && ascii) {
1218 utf8_buf[0] = ascii;
1223 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, utf8_buf);
1225#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1228 if (xke->type == KeyPress && xic) {
1233 if (
uchar(utf8_buf[
i++]) > 0x7f) {
1234 for (;
i <
len; ++
i) {
1236 if (c < 0x80 || c > 0xbf) {
1248 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, &utf8_buf[
i]);
1252 if (utf8_buf != utf8_array) {
1261 case ButtonRelease: {
1262 const XButtonEvent &xbe = xe->xbutton;
1269 if (xbe.button == Button4) {
1270 if (xbe.type == ButtonPress) {
1275 if (xbe.button == Button5) {
1276 if (xbe.type == ButtonPress) {
1283 if (xbe.button == Button1) {
1286 else if (xbe.button == Button2) {
1289 else if (xbe.button == Button3) {
1295 else if (xbe.button == 6) {
1298 else if (xbe.button == 7) {
1301 else if (xbe.button == 8) {
1304 else if (xbe.button == 9) {
1311 g_event =
new GHOST_EventButton(event_ms, type, window, gbmask, window->
GetTabletData());
1316 case ConfigureNotify: {
1327 const XFocusChangeEvent &xfe = xe->xfocus;
1331 printf(
"X: focus %s for window %d\n", xfe.type == FocusIn ?
"in" :
"out",
int(xfe.window));
1339#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1340 XIC xic = window->getX11_XIC();
1342 if (xe->type == FocusIn) {
1354 case ClientMessage: {
1355 XClientMessageEvent &xcme = xe->xclient;
1357 if (((Atom)xcme.data.l[0]) ==
m_atom.WM_DELETE_WINDOW) {
1360 else if (((Atom)xcme.data.l[0]) ==
m_atom.WM_TAKE_FOCUS) {
1361 XWindowAttributes attr;
1375 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
1376 if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
1377 if (attr.map_state == IsViewable) {
1378 if (fwin != xcme.window) {
1379 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
1389 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) ==
false) {
1404 case GraphicsExpose:
1416 const XCrossingEvent &xce = xe->xcrossing;
1418 if (xce.mode == NotifyNormal) {
1419 g_event =
new GHOST_EventCursor(event_ms,
1429 "X: %s window %d\n", xce.type == EnterNotify ?
"entering" :
"leaving",
int(xce.window));
1432 if (xce.type == EnterNotify) {
1463 case ReparentNotify:
1465 case SelectionRequest: {
1467 XSelectionRequestEvent *xse = &xe->xselectionrequest;
1470 if (xse->property ==
None) {
1471 xse->property = xse->target;
1474 nxe.xselection.type = SelectionNotify;
1475 nxe.xselection.requestor = xse->requestor;
1476 nxe.xselection.property = xse->property;
1477 nxe.xselection.display = xse->display;
1478 nxe.xselection.selection = xse->selection;
1479 nxe.xselection.target = xse->target;
1480 nxe.xselection.time = xse->time;
1483 if (
ELEM(xse->target,
1489 if (xse->selection == XInternAtom(m_display,
"PRIMARY", False)) {
1490 XChangeProperty(m_display,
1499 else if (xse->selection == XInternAtom(m_display,
"CLIPBOARD", False)) {
1500 XChangeProperty(m_display,
1510 else if (xse->target ==
m_atom.TARGETS) {
1511 const Atom atom_list[] = {
m_atom.TARGETS,
1516 XChangeProperty(m_display,
1522 reinterpret_cast<const uchar *
>(atom_list),
1528 nxe.xselection.property =
None;
1532 XSendEvent(m_display, xse->requestor, 0, 0, &nxe);
1538#ifdef WITH_X11_XINPUT
1539 for (GHOST_TabletX11 &xtablet : m_xtablets) {
1540 if (
ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
1541 const XDeviceMotionEvent *
data = (
const XDeviceMotionEvent *)xe;
1542 if (
data->deviceid != xtablet.ID) {
1546 const uchar axis_first =
data->first_axis;
1547 const uchar axes_end = axis_first +
data->axes_count;
1561# define AXIS_VALUE_GET(axis, val) \
1562 ((axis_first <= axis && axes_end > axis) && \
1563 ((void)(val = data->axis_data[axis - axis_first]), true))
1565 if (AXIS_VALUE_GET(2, axis_value)) {
1577 if (AXIS_VALUE_GET(3, axis_value)) {
1579 float(xtablet.XtiltLevels);
1581 if (AXIS_VALUE_GET(4, axis_value)) {
1583 float(xtablet.YtiltLevels);
1586# undef AXIS_VALUE_GET
1588 else if (xe->type == xtablet.ProxInEvent) {
1589 const XProximityNotifyEvent *
data = (
const XProximityNotifyEvent *)xe;
1590 if (
data->deviceid != xtablet.ID) {
1596 else if (xe->type == xtablet.ProxOutEvent) {
1627 XImage *image = XGetImage(m_display,
1628 XRootWindow(m_display, XDefaultScreen(m_display)),
1635 if (image ==
nullptr) {
1638 c.pixel = XGetPixel(image, 0, 0);
1640 XQueryColor(m_display, XDefaultColormap(m_display, XDefaultScreen(m_display)), &c);
1643 r_color[0] = c.red / 65535.0f;
1644 r_color[1] = c.green / 65535.0f;
1645 r_color[2] = c.blue / 65535.0f;
1654 memset((
void *)m_keyboard_vector, 0,
sizeof(m_keyboard_vector));
1656 XQueryKeymap(m_display, (
char *)m_keyboard_vector);
1660 const static KeyCode shift_l = XKeysymToKeycode(m_display, XK_Shift_L);
1661 const static KeyCode shift_r = XKeysymToKeycode(m_display, XK_Shift_R);
1662 const static KeyCode control_l = XKeysymToKeycode(m_display, XK_Control_L);
1663 const static KeyCode control_r = XKeysymToKeycode(m_display, XK_Control_R);
1664 const static KeyCode alt_l = XKeysymToKeycode(m_display, XK_Alt_L);
1665 const static KeyCode alt_r = XKeysymToKeycode(m_display, XK_Alt_R);
1666 const static KeyCode super_l = XKeysymToKeycode(m_display, XK_Super_L);
1667 const static KeyCode super_r = XKeysymToKeycode(m_display, XK_Super_R);
1668 const static KeyCode hyper_l = XKeysymToKeycode(m_display, XK_Hyper_L);
1669 const static KeyCode hyper_r = XKeysymToKeycode(m_display, XK_Hyper_R);
1673 ((m_keyboard_vector[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
1675 ((m_keyboard_vector[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
1678 ((m_keyboard_vector[control_l >> 3] >> (control_l & 7)) & 1) != 0);
1680 ((m_keyboard_vector[control_r >> 3] >> (control_r & 7)) & 1) != 0);
1686 ((m_keyboard_vector[super_l >> 3] >> (super_l & 7)) & 1) != 0);
1688 ((m_keyboard_vector[super_r >> 3] >> (super_r & 7)) & 1) != 0);
1691 ((m_keyboard_vector[hyper_l >> 3] >> (hyper_l & 7)) & 1) != 0);
1693 ((m_keyboard_vector[hyper_r >> 3] >> (hyper_r & 7)) & 1) != 0);
1700 Window root_return, child_return;
1704 if (XQueryPointer(m_display,
1705 RootWindow(m_display, DefaultScreen(m_display)),
1712 &mask_return) == True)
1736 if (XQueryPointer(display,
1737 RootWindow(display, DefaultScreen(display)),
1744 &mask_return) == False)
1770#ifdef WITH_XWAYLAND_HACK
1784#ifdef WITH_XWAYLAND_HACK
1785 if (use_xwayland_hack) {
1786 if (child_return !=
None) {
1787 XFixesHideCursor(m_display, child_return);
1792#if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1793 if ((m_xinput_version.present) && (m_xinput_version.major_version >= 2)) {
1796 if (XIGetClientPointer(m_display,
None, &device_id) != False) {
1797 XIWarpPointer(m_display, device_id,
None,
None, 0, 0, 0, 0, relx, rely);
1803 XWarpPointer(m_display,
None,
None, 0, 0, 0, 0, relx, rely);
1806#ifdef WITH_XWAYLAND_HACK
1807 if (use_xwayland_hack) {
1808 if (child_return !=
None) {
1809 XFixesShowCursor(m_display, child_return);
1814 XSync(m_display, 0);
1833 GHOST_ASSERT((bad_wind !=
nullptr),
"addDirtyWindow() nullptr ptr trapped (window)");
1835 m_dirty_windows.push_back(bad_wind);
1838bool GHOST_SystemX11::generateWindowExposeEvents()
1842 bool anyProcessed =
false;
1844 for (; w_start != w_end; ++w_start) {
1848 (*w_start)->validate();
1852 anyProcessed =
true;
1856 m_dirty_windows.clear();
1857 return anyProcessed;
1861 XkbDescPtr xkb_descr,
1862 const KeyCode keycode)
1873#define GXMAP(k, x, y) \
1882 if ((key >= XK_A) && (key <= XK_Z)) {
1885 else if ((key >= XK_a) && (key <= XK_z)) {
1888 else if ((key >= XK_0) && (key <= XK_9)) {
1891 else if ((key >= XK_F1) && (key <= XK_F24)) {
1979#ifdef WITH_XF86KEYSYM
1985# ifdef XF86XK_AudioForward
1990#ifdef WITH_GHOST_DEBUG
1991 printf(
"%s: unknown key: %lu / 0x%lx\n", __func__, key, key);
2003#define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
2007 GHOST_ASSERT(XkbKeyNameLength == 4,
"Name length is invalid!");
2008 if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) {
2009 const char *id_str = xkb_descr->names->keys[keycode].name;
2010 const uint32_t
id =
MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]);
2012 case MAKE_ID(
'T',
'L',
'D',
'E'):
2014 case MAKE_ID(
'L',
'S',
'G',
'T'):
2016#ifdef WITH_GHOST_DEBUG
2018 printf(
"%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str);
2023 else if (keycode != 0) {
2033#define XCLIB_XCOUT_NONE 0
2034#define XCLIB_XCOUT_SENTCONVSEL 1
2035#define XCLIB_XCOUT_INCR 2
2036#define XCLIB_XCOUT_FALLBACK 3
2037#define XCLIB_XCOUT_FALLBACK_UTF8 4
2038#define XCLIB_XCOUT_FALLBACK_COMP 5
2039#define XCLIB_XCOUT_FALLBACK_TEXT 6
2043 const XEvent *evt, Atom sel, Atom target,
uchar **txt,
ulong *
len,
uint *context)
const
2048 ulong pty_size, pty_items;
2051 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2052 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2066 XConvertSelection(m_display, sel, target,
m_atom.XCLIP_OUT, win, CurrentTime);
2071 if (evt->type != SelectionNotify) {
2075 if (target ==
m_atom.UTF8_STRING && evt->xselection.property ==
None) {
2079 if (target ==
m_atom.COMPOUND_TEXT && evt->xselection.property ==
None) {
2083 if (target ==
m_atom.TEXT && evt->xselection.property ==
None) {
2089 XGetWindowProperty(m_display,
2103 if (pty_type ==
m_atom.INCR) {
2105 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2114 if (pty_format != 8) {
2120 XGetWindowProperty(m_display,
2134 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2137 ltxt = (
uchar *)malloc(pty_items);
2138 memcpy(ltxt, buffer, pty_items);
2159 if (evt->type != PropertyNotify) {
2164 if (evt->xproperty.state != PropertyNewValue) {
2169 XGetWindowProperty(m_display,
2182 if (pty_format != 8) {
2187 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2191 if (pty_size == 0) {
2194 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2206 XGetWindowProperty(m_display,
2226 ltxt = (
uchar *)realloc(ltxt, *
len);
2230 memcpy(<xt[*
len - pty_items], buffer, pty_items);
2236 XDeleteProperty(m_display, win,
m_atom.XCLIP_OUT);
2245 Atom target =
m_atom.UTF8_STRING;
2254 if (selection == True) {
2258 sseln =
m_atom.CLIPBOARD;
2261 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2262 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2267 owner = XGetSelectionOwner(m_display, sseln);
2269 if (sseln ==
m_atom.CLIPBOARD) {
2271 sel_buf = (
char *)malloc(sel_buf_size);
2276 sel_buf = (
char *)malloc(sel_buf_size);
2280 if (owner ==
None) {
2285 vector<XEvent> restore_events;
2289 bool restore_this_event =
false;
2291 XNextEvent(m_display, &evt);
2292 restore_this_event = (evt.type != SelectionNotify);
2298 if (restore_this_event) {
2299 restore_events.push_back(evt);
2311 target =
m_atom.COMPOUND_TEXT;
2331 while (!restore_events.empty()) {
2332 XPutBackEvent(m_display, &restore_events.back());
2333 restore_events.pop_back();
2338 char *tmp_data = (
char *)malloc(sel_len + 1);
2339 memcpy(tmp_data, (
char *)sel_buf, sel_len);
2340 tmp_data[sel_len] =
'\0';
2342 if (sseln ==
m_atom.STRING) {
2358 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2359 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2364 if (selection == False) {
2365 XSetSelectionOwner(m_display,
m_atom.CLIPBOARD, m_window, CurrentTime);
2366 owner = XGetSelectionOwner(m_display,
m_atom.CLIPBOARD);
2371 size_t buffer_size = strlen(buffer) + 1;
2376 XSetSelectionOwner(m_display,
m_atom.PRIMARY, m_window, CurrentTime);
2377 owner = XGetSelectionOwner(m_display,
m_atom.PRIMARY);
2382 size_t buffer_size = strlen(buffer) + 1;
2387 if (owner != m_window) {
2388 fprintf(stderr,
"failed to own primary\n");
2442 XFillRectangle(display,
2450 XFillRectangle(display,
2458 XDrawString(display,
2485 data = strdup(text);
2486 for (tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps)) {
2491 data = strdup(text);
2492 *
str = (
char **)malloc(
size_t(*
count) *
sizeof(
char *));
2493 for (
i = 0, tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps),
i++) {
2494 (*str)[
i] = strdup(tok);
2500 const char *message,
2501 const char *help_label,
2502 const char *continue_label,
2506 char **text_splitted =
nullptr;
2508 split(message,
"\n", &text_splitted, &textLines);
2515 int screen = DefaultScreen(m_display);
2516 window = XCreateSimpleWindow(m_display,
2517 RootWindow(m_display, screen),
2523 BlackPixel(m_display, screen),
2524 WhitePixel(m_display, screen));
2528 hints.flags = PSize | PMinSize | PMaxSize;
2529 hints.min_width = hints.max_width = hints.base_width = dialog_data.
width;
2530 hints.min_height = hints.max_height = hints.base_height = dialog_data.
height;
2531 XSetWMNormalHints(m_display, window, &hints);
2536 Atom wm_Name = XInternAtom(m_display,
"_NET_WM_NAME", False);
2537 Atom utf8Str = XInternAtom(m_display,
"UTF8_STRING", False);
2539 Atom winType = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE", False);
2540 Atom typeDialog = XInternAtom(m_display,
"_NET_WM_WINDOW_TYPE_DIALOG", False);
2542 XChangeProperty(m_display,
2548 (
const uchar *)title,
2549 int(strlen(title)));
2552 m_display, window, winType, XA_ATOM, 32, PropModeReplace, (
uchar *)&typeDialog, 1);
2556 XGCValues buttonBorderGCValues;
2557 buttonBorderGCValues.foreground = BlackPixel(m_display, screen);
2558 buttonBorderGCValues.background = WhitePixel(m_display, screen);
2559 XGCValues buttonGCValues;
2560 buttonGCValues.foreground = WhitePixel(m_display, screen);
2561 buttonGCValues.background = BlackPixel(m_display, screen);
2563 GC buttonBorderGC = XCreateGC(m_display, window, GCForeground, &buttonBorderGCValues);
2564 GC buttonGC = XCreateGC(m_display, window, GCForeground, &buttonGCValues);
2566 XSelectInput(m_display, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
2567 XMapWindow(m_display, window);
2569 const bool has_link = link && strlen(link);
2572 XNextEvent(m_display, &
e);
2573 if (
e.type == Expose) {
2574 for (
int i = 0;
i < textLines;
i++) {
2575 XDrawString(m_display,
2577 DefaultGC(m_display, screen),
2581 int(strlen(text_splitted[
i])));
2583 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 1, continue_label);
2585 dialog_data.
drawButton(m_display, window, buttonBorderGC, buttonGC, 2, help_label);
2588 else if (
e.type == ButtonRelease) {
2594 string cmd =
"xdg-open \"" + string(link) +
"\"";
2595 if (system(cmd.c_str()) != 0) {
2596 GHOST_PRINTF(
"GHOST_SystemX11::showMessageBox: Unable to run system command [%s]",
2605 for (
int i = 0;
i < textLines;
i++) {
2606 free(text_splitted[
i]);
2608 free(text_splitted);
2610 XDestroyWindow(m_display, window);
2611 XFreeGC(m_display, buttonBorderGC);
2612 XFreeGC(m_display, buttonGC);
2632 event_ms, eventType, draggedObjectType, window, mouseX, mouseY,
data));
2649 char error_code_str[512];
2651 XGetErrorText(display, event->error_code, error_code_str,
sizeof(error_code_str));
2654 "Received X11 Error:\n"
2655 "\terror code: %d\n"
2656 "\trequest code: %d\n"
2657 "\tminor code: %d\n"
2658 "\terror text: %s\n",
2660 event->request_code,
2675 fprintf(stderr,
"Ignoring Xlib error: error IO\n");
2681#ifdef WITH_X11_XINPUT
2683static bool is_filler_char(
char c)
2685 return isspace(c) ||
ELEM(c,
'_',
'-',
';',
':');
2689static bool match_token(
const char *haystack,
const char *needle)
2692 for (h = haystack; *h;) {
2693 while (*h && is_filler_char(*h)) {
2700 for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) {
2703 if (!*n && (is_filler_char(*h) || !*h)) {
2707 while (*h && !is_filler_char(*h)) {
2724static GHOST_TTabletMode tablet_mode_from_name(
const char *name,
const char *type)
2727 static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
"pen",
nullptr};
2729 static const char *type_blacklist[] = {
"pad",
"cursor",
"touch",
nullptr};
2732 for (
i = 0; type_blacklist[
i] !=
nullptr;
i++) {
2733 if (type && (strcasecmp(type, type_blacklist[
i]) == 0)) {
2739 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2740 if (type && match_token(type, tablet_stylus_whitelist[
i])) {
2744 if (type && match_token(type,
"eraser")) {
2747 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2748 if (name && match_token(name, tablet_stylus_whitelist[
i])) {
2752 if (name && match_token(name,
"eraser")) {
2761void GHOST_SystemX11::refreshXInputDevices()
2763 if (m_xinput_version.present) {
2765 clearXInputDevices();
2772 XDeviceInfo *device_info = XListInputDevices(m_display, &device_count);
2774 for (
int i = 0;
i < device_count; ++
i) {
2775 char *device_type = device_info[
i].type ? XGetAtomName(m_display, device_info[
i].type) :
2777 GHOST_TTabletMode tablet_mode = tablet_mode_from_name(device_info[
i].name, device_type);
2782 XFree((
void *)device_type);
2789 GHOST_TabletX11 xtablet = {tablet_mode};
2790 xtablet.ID = device_info[
i].id;
2791 xtablet.Device = XOpenDevice(m_display, xtablet.ID);
2793 if (xtablet.Device !=
nullptr) {
2795 XAnyClassPtr ici = device_info[
i].inputclassinfo;
2797 if (ici !=
nullptr) {
2798 for (
int j = 0; j < device_info[
i].num_classes; ++j) {
2799 if (ici->c_class == ValuatorClass) {
2800 XValuatorInfo *xvi = (XValuatorInfo *)ici;
2801 if (xvi->axes !=
nullptr) {
2802 xtablet.PressureLevels = xvi->axes[2].max_value;
2804 if (xvi->num_axes > 3) {
2807 xtablet.XtiltLevels = xvi->axes[3].max_value;
2808 xtablet.YtiltLevels = xvi->axes[4].max_value;
2811 xtablet.XtiltLevels = 0;
2812 xtablet.YtiltLevels = 0;
2819 ici = (XAnyClassPtr)(((
char *)ici) + ici->length);
2823 m_xtablets.push_back(xtablet);
2827 XFreeDeviceList(device_info);
2834void GHOST_SystemX11::clearXInputDevices()
2836 for (GHOST_TabletX11 &xtablet : m_xtablets) {
2837 if (xtablet.Device) {
2838 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
struct GHOST_SystemX11::@051217216066317346075301236035010123305105337273 m_atom
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
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)