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;
109#ifdef WITH_X11_XINPUT
112 keycode_last_repeat_key_(
uint(-1))
115 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 atom_.atom = XInternAtom(display_, #atom, True); \
138#define GHOST_INTERN_ATOM(atom) \
140 atom_.atom = XInternAtom(display_, #atom, False); \
164#ifdef WITH_X11_XINPUT
165 atom_.TABLET = XInternAtom(display_, XI_TABLET, False);
168#undef GHOST_INTERN_ATOM_IF_EXISTS
169#undef GHOST_INTERN_ATOM
173 last_release_keycode_ = 0;
174 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 display_, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
184 XkbSetDetectableAutoRepeat(display_,
true,
nullptr);
186 xkb_descr_ = XkbGetMap(display_, 0, XkbUseCoreKbd);
188 XkbGetNames(display_, XkbKeyNamesMask, xkb_descr_);
189 XkbGetControls(display_, XkbPerKeyRepeatMask | XkbRepeatKeysMask, xkb_descr_);
193#ifdef WITH_XWAYLAND_HACK
194 use_xwayland_hack = getenv(
"WAYLAND_DISPLAY") !=
nullptr;
197#ifdef WITH_X11_XINPUT
200 memset(&xinput_version_, 0,
sizeof(xinput_version_));
201 XExtensionVersion *version = XGetExtensionVersion(display_, INAME);
202 if (version && (version != (XExtensionVersion *)NoSuchExtension)) {
203 if (version->present) {
204 xinput_version_ = *version;
210# ifdef USE_XINPUT_HOTPLUG
211 if (xinput_version_.present) {
212 XEventClass class_presence;
214 DevicePresence(display_, xi_presence, class_presence);
215 XSelectExtensionEvent(
216 display_, RootWindow(display_, DefaultScreen(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(xkb_descr_, XkbAllComponentsMask,
true);
242 XCloseDisplay(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(display_, DefaultScreen(display_));
348 height = DisplayHeight(display_, DefaultScreen(display_));
359 const bool exclusive,
360 const bool is_dialog,
409#ifdef WITH_VULKAN_BACKEND
410 case GHOST_kDrawingContextTypeVulkan: {
412 GHOST_kVulkanPlatformX11,
421 if (context->initializeDrawingContext()) {
429#ifdef WITH_OPENGL_BACKEND
430 case GHOST_kDrawingContextTypeOpenGL: {
431 for (
int minor = 6; minor >= 3; --minor) {
433 context_params_offscreen,
436 (GLXFBConfig)
nullptr,
437 GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
441 (context_params_offscreen.
is_debug ? GLX_CONTEXT_DEBUG_BIT_ARB : 0),
443 if (context->initializeDrawingContext()) {
465#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
466static void destroyIMCallback(XIM , XPointer
ptr, XPointer )
471 *(XIM *)
ptr =
nullptr;
475bool GHOST_SystemX11::openX11_IM()
482 XSetLocaleModifiers(
"");
484 xim_ = XOpenIM(display_,
nullptr, (
char *)GHOST_X11_RES_NAME, (
char *)GHOST_X11_RES_CLASS);
490 destroy.callback = (XIMProc)destroyIMCallback;
491 destroy.client_data = (XPointer)&xim_;
492 XSetIMValues(xim_, XNDestroyCallback, &destroy,
nullptr);
509 const vector<GHOST_IWindow *> &win_vec =
window_manager_->getWindows();
511 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
512 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
514 for (; win_it != win_end; ++win_it) {
515 GHOST_WindowX11 *window =
static_cast<GHOST_WindowX11 *
>(*win_it);
525 int fd = ConnectionNumber(display);
531 if (maxSleep == -1) {
532 select(fd + 1, &fds,
nullptr,
nullptr,
nullptr);
537 tv.tv_sec = maxSleep / 1000;
538 tv.tv_usec = (maxSleep - tv.tv_sec * 1000) * 1000;
540 select(fd + 1, &fds,
nullptr,
nullptr, &tv);
552 switch (event->type) {
555 data->timestamp =
event->xbutton.time;
558 data->timestamp =
event->xmotion.time;
562 data->timestamp =
event->xkey.time;
565 data->timestamp =
event->xproperty.time;
569 data->timestamp =
event->xcrossing.time;
572 data->timestamp =
event->xselectionclear.time;
581Time GHOST_SystemX11::lastEventTime(Time default_time)
583 init_timestamp_data
data;
584 data.timestamp = default_time;
588 return data.timestamp;
596 bool anyProcessed =
false;
601 if (waitForEvent && dirty_windows_.empty() && !XPending(display_)) {
620 while (XPending(display_)) {
622 XNextEvent(display_, &xevent);
624#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
628 if (
ELEM(xevent.type, FocusIn, KeyPress)) {
629 if (!xim_ && openX11_IM()) {
635 if (window && !window->getX11_XIC() && window->createX11_XIC()) {
637 if (xevent.type == KeyPress) {
641 XSetICFocus(window->getX11_XIC());
648 if (
ELEM(xevent.type, KeyPress, KeyRelease)) {
649 if (xevent.xkey.time != 0) {
650 last_key_time_ = xevent.xkey.time;
655 if (XFilterEvent(&xevent, (
Window)
nullptr) == True) {
662 if (xevent.type == KeyRelease) {
663 last_release_keycode_ = xevent.xkey.keycode;
664 last_release_time_ = xevent.xkey.time;
666 else if (xevent.type == KeyPress) {
667 if ((xevent.xkey.keycode == last_release_keycode_) &&
668 (xevent.xkey.time <= last_release_time_))
674 processEvent(&xevent);
677#ifdef USE_UNITY_WORKAROUND
684 if (xevent.type == FocusIn) {
688 if (window && XPending(display_) >= 2) {
689 XNextEvent(display_, &xevent);
691 if (xevent.type == KeymapNotify) {
696 XPeekEvent(display_, &xev_next);
698 if (
ELEM(xev_next.type, KeyPress, KeyRelease)) {
701 const static KeySym modifiers[] = {
715 KeyCode kc = XKeysymToKeycode(display_, modifiers[
i]);
716 if (kc != 0 && ((xevent.xkeymap.key_vector[kc >> 3] >> (kc & 7)) & 1) != 0) {
732 if (generateWindowExposeEvents()) {
736#ifdef WITH_INPUT_NDOF
742 }
while (waitForEvent && !anyProcessed);
747#ifdef WITH_X11_XINPUT
748static bool checkTabletProximity(
Display *display, XDevice *device)
755 if (device ==
nullptr) {
762 state = XQueryDeviceState(display, device);
767 XInputClass *cls =
state->data;
769 for (
int loop = 0; loop <
state->num_classes; loop++) {
770 switch (cls->c_class) {
772 XValuatorState *val_state = (XValuatorState *)cls;
777 if ((val_state->mode & 2) == 0) {
778 XFreeDeviceState(
state);
783 cls = (XInputClass *)((
char *)cls + cls->length);
785 XFreeDeviceState(
state);
791void GHOST_SystemX11::processEvent(XEvent *xe)
793 GHOST_WindowX11 *window = findGhostWindow(xe->xany.window);
794 GHOST_Event *g_event =
nullptr;
797 bool is_repeat =
false;
798 if (
ELEM(xe->type, KeyPress, KeyRelease)) {
799 XKeyEvent *xke = &(xe->xkey);
802 bool is_repeat_keycode =
false;
804 if (xkb_descr_ !=
nullptr) {
806 is_repeat_keycode = (
808 (xke->keycode < (XkbPerKeyBitArraySize << 3)) &&
809 bit_is_on(xkb_descr_->ctrls->per_key_repeat, xke->keycode));
813 switch (XLookupKeysym(xke, 0)) {
830 is_repeat_keycode =
true;
835 if (is_repeat_keycode) {
836 if (xe->type == KeyPress) {
837 if (keycode_last_repeat_key_ == xke->keycode) {
840 keycode_last_repeat_key_ = xke->keycode;
843 if (keycode_last_repeat_key_ == xke->keycode) {
844 keycode_last_repeat_key_ =
uint(-1);
849 else if (xe->type == EnterNotify) {
851 keycode_last_repeat_key_ =
uint(-1);
854#ifdef USE_XINPUT_HOTPLUG
856 if (xinput_version_.present) {
857 XEventClass class_presence;
860 DevicePresence(display_, xi_presence, class_presence);
861 (void)class_presence;
863 if (xe->type == xi_presence) {
864 const XDevicePresenceNotifyEvent *notify_event = (
const XDevicePresenceNotifyEvent *)xe;
867 refreshXInputDevices();
871 const vector<GHOST_IWindow *> &win_vec =
window_manager_->getWindows();
872 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
873 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
875 for (; win_it != win_end; ++win_it) {
876 GHOST_WindowX11 *window_xinput =
static_cast<GHOST_WindowX11 *
>(*win_it);
877 window_xinput->refreshXInputDevices();
889#ifdef WITH_X11_XINPUT
896 bool any_proximity =
false;
898 for (
const GHOST_TabletX11 &xtablet : xtablets_) {
899 if (checkTabletProximity(xe->xany.display, xtablet.Device)) {
900 any_proximity =
true;
904 if (!any_proximity) {
912 const XExposeEvent &xee = xe->xexpose;
914 if (xee.count == 0) {
926 const XMotionEvent &xme = xe->xmotion;
947 const int32_t subregion_div = 4;
954 bounds.l_ = center[0] - (
size[0] / (subregion_div * 2));
955 bounds.r_ = center[0] + (
size[0] / (subregion_div * 2));
956 bounds.t_ = center[1] - (
size[1] / (subregion_div * 2));
957 bounds.b_ = center[1] + (
size[1] / (subregion_div * 2));
974 bounds.wrapPoint(x_new, y_new, bounds_margin, bounds_axis);
979 if (x_new != xme.x_root || y_new != xme.y_root) {
986 if (x_new != xme.x_root && xme.time > last_warp_x_) {
987 x_accum += (xme.x_root - x_new);
988 last_warp_x_ = lastEventTime(xme.time) + 25;
990 if (y_new != xme.y_root && xme.time > last_warp_y_) {
991 y_accum += (xme.y_root - y_new);
992 last_warp_y_ = lastEventTime(xme.time) + 25;
1000 g_event =
new GHOST_EventCursor(event_ms,
1003 xme.x_root + x_accum,
1004 xme.y_root + y_accum,
1009 g_event =
new GHOST_EventCursor(event_ms,
1021 XKeyEvent *xke = &(xe->xkey);
1022#ifdef WITH_X11_XINPUT
1024 const Time time = xke->time ? xke->time : last_key_time_;
1026 const Time time = xke->time;
1031 char *utf8_buf =
nullptr;
1034#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1041 char utf8_array[16 * 6 + 5];
1050#ifdef USE_NON_LATIN_KB_WORKAROUND
1076 const uint mode_switch_mask = XkbKeysymToModifiers(xke->display, XK_Mode_switch);
1077 const uint number_hack_forbidden_kmods_mask = mode_switch_mask | ShiftMask;
1078 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1079 ((xke->state & number_hack_forbidden_kmods_mask) == 0))
1081 key_sym = XLookupKeysym(xke, ShiftMask);
1082 if (!((key_sym >= XK_0) && (key_sym <= XK_9))) {
1083 key_sym = XLookupKeysym(xke, 0);
1087 key_sym = XLookupKeysym(xke, 0);
1090 if (!XLookupString(xke, &ascii, 1, &key_sym_str,
nullptr)) {
1152 if ((xke->keycode >= 10 && xke->keycode < 20) &&
1153 ((key_sym = XLookupKeysym(xke, ShiftMask)) >= XK_0) && (key_sym <= XK_9))
1159 key_sym = XLookupKeysym(xke, 0);
1164 if (!XLookupString(xke, &ascii, 1,
nullptr,
nullptr)) {
1169#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1174 if (xke->type == KeyPress) {
1175 utf8_buf = utf8_array;
1176#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1178 xic = window->getX11_XIC();
1183 if (!(
len = Xutf8LookupString(
1184 xic, xke, utf8_buf,
sizeof(utf8_array) - 5, &key_sym, &
status)))
1189 if (
status == XBufferOverflow) {
1190 utf8_buf = (
char *)malloc(
len + 5);
1191 len = Xutf8LookupString(xic, xke, utf8_buf,
len, &key_sym, &
status);
1194 if (
ELEM(
status, XLookupChars, XLookupBoth)) {
1197 if ((utf8_buf[0] < 32 && utf8_buf[0] > 0) || (utf8_buf[0] == 127)) {
1201 else if (
status == XLookupKeySym) {
1206 printf(
"Bad keycode lookup. Keysym 0x%x Status: %s\n",
1208 (
status == XLookupNone ?
"XLookupNone" :
1209 status == XLookupKeySym ?
"XLookupKeySym" :
1212 printf(
"'%.*s' %p %p\n",
len, utf8_buf, xic, xim_);
1219 if (!utf8_buf[0] && ascii) {
1220 utf8_buf[0] = ascii;
1225 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, utf8_buf);
1227#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1230 if (xke->type == KeyPress && xic) {
1235 if (
uchar(utf8_buf[
i++]) > 0x7f) {
1236 for (;
i <
len; ++
i) {
1238 if (c < 0x80 || c > 0xbf) {
1250 g_event =
new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, &utf8_buf[
i]);
1254 if (utf8_buf != utf8_array) {
1263 case ButtonRelease: {
1264 const XButtonEvent &xbe = xe->xbutton;
1271 if (xbe.button == Button4) {
1272 if (xbe.type == ButtonPress) {
1277 if (xbe.button == Button5) {
1278 if (xbe.type == ButtonPress) {
1285 if (xbe.button == Button1) {
1288 else if (xbe.button == Button2) {
1291 else if (xbe.button == Button3) {
1297 else if (xbe.button == 6) {
1300 else if (xbe.button == 7) {
1303 else if (xbe.button == 8) {
1306 else if (xbe.button == 9) {
1313 g_event =
new GHOST_EventButton(event_ms, type, window, gbmask, window->
GetTabletData());
1318 case ConfigureNotify: {
1329 const XFocusChangeEvent &xfe = xe->xfocus;
1333 printf(
"X: focus %s for window %d\n", xfe.type == FocusIn ?
"in" :
"out",
int(xfe.window));
1341#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1342 XIC xic = window->getX11_XIC();
1344 if (xe->type == FocusIn) {
1356 case ClientMessage: {
1357 XClientMessageEvent &xcme = xe->xclient;
1359 if (((Atom)xcme.data.l[0]) ==
atom_.WM_DELETE_WINDOW) {
1362 else if (((Atom)xcme.data.l[0]) ==
atom_.WM_TAKE_FOCUS) {
1363 XWindowAttributes attr;
1377 if (XGetWindowAttributes(display_, xcme.window, &attr) == True) {
1378 if (XGetInputFocus(display_, &fwin, &revert_to) == True) {
1379 if (attr.map_state == IsViewable) {
1380 if (fwin != xcme.window) {
1381 XSetInputFocus(display_, xcme.window, RevertToParent, xcme.data.l[1]);
1391 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) ==
false) {
1406 case GraphicsExpose:
1418 const XCrossingEvent &xce = xe->xcrossing;
1420 if (xce.mode == NotifyNormal) {
1421 g_event =
new GHOST_EventCursor(event_ms,
1431 "X: %s window %d\n", xce.type == EnterNotify ?
"entering" :
"leaving",
int(xce.window));
1434 if (xce.type == EnterNotify) {
1465 case ReparentNotify:
1467 case SelectionRequest: {
1469 XSelectionRequestEvent *xse = &xe->xselectionrequest;
1472 if (xse->property ==
None) {
1473 xse->property = xse->target;
1476 nxe.xselection.type = SelectionNotify;
1477 nxe.xselection.requestor = xse->requestor;
1478 nxe.xselection.property = xse->property;
1479 nxe.xselection.display = xse->display;
1480 nxe.xselection.selection = xse->selection;
1481 nxe.xselection.target = xse->target;
1482 nxe.xselection.time = xse->time;
1487 if (xse->selection == XInternAtom(display_,
"PRIMARY", False)) {
1488 XChangeProperty(display_,
1497 else if (xse->selection == XInternAtom(display_,
"CLIPBOARD", False)) {
1498 XChangeProperty(display_,
1508 else if (xse->target ==
atom_.TARGETS) {
1509 const Atom atom_list[] = {
1511 XChangeProperty(display_,
1517 reinterpret_cast<const uchar *
>(atom_list),
1523 nxe.xselection.property =
None;
1527 XSendEvent(display_, xse->requestor, 0, 0, &nxe);
1533#ifdef WITH_X11_XINPUT
1534 for (GHOST_TabletX11 &xtablet : xtablets_) {
1535 if (
ELEM(xe->type, xtablet.MotionEvent, xtablet.PressEvent)) {
1536 const XDeviceMotionEvent *
data = (
const XDeviceMotionEvent *)xe;
1537 if (
data->deviceid != xtablet.ID) {
1541 const uchar axis_first =
data->first_axis;
1542 const uchar axes_end = axis_first +
data->axes_count;
1556# define AXIS_VALUE_GET(axis, val) \
1557 ((axis_first <= axis && axes_end > axis) && \
1558 ((void)(val = data->axis_data[axis - axis_first]), true))
1560 if (AXIS_VALUE_GET(2, axis_value)) {
1572 if (AXIS_VALUE_GET(3, axis_value)) {
1574 float(xtablet.XtiltLevels);
1576 if (AXIS_VALUE_GET(4, axis_value)) {
1578 float(xtablet.YtiltLevels);
1581# undef AXIS_VALUE_GET
1583 else if (xe->type == xtablet.ProxInEvent) {
1584 const XProximityNotifyEvent *
data = (
const XProximityNotifyEvent *)xe;
1585 if (
data->deviceid != xtablet.ID) {
1591 else if (xe->type == xtablet.ProxOutEvent) {
1622 XImage *image = XGetImage(
1623 display_, XRootWindow(display_, XDefaultScreen(display_)),
x,
y, 1, 1, AllPlanes, XYPixmap);
1624 if (image ==
nullptr) {
1627 c.pixel = XGetPixel(image, 0, 0);
1629 XQueryColor(display_, XDefaultColormap(display_, XDefaultScreen(display_)), &c);
1632 r_color[0] = c.red / 65535.0f;
1633 r_color[1] = c.green / 65535.0f;
1634 r_color[2] = c.blue / 65535.0f;
1643 memset((
void *)keyboard_vector_, 0,
sizeof(keyboard_vector_));
1645 XQueryKeymap(display_, (
char *)keyboard_vector_);
1649 const static KeyCode shift_l = XKeysymToKeycode(display_, XK_Shift_L);
1650 const static KeyCode shift_r = XKeysymToKeycode(display_, XK_Shift_R);
1651 const static KeyCode control_l = XKeysymToKeycode(display_, XK_Control_L);
1652 const static KeyCode control_r = XKeysymToKeycode(display_, XK_Control_R);
1653 const static KeyCode alt_l = XKeysymToKeycode(display_, XK_Alt_L);
1654 const static KeyCode alt_r = XKeysymToKeycode(display_, XK_Alt_R);
1655 const static KeyCode super_l = XKeysymToKeycode(display_, XK_Super_L);
1656 const static KeyCode super_r = XKeysymToKeycode(display_, XK_Super_R);
1657 const static KeyCode hyper_l = XKeysymToKeycode(display_, XK_Hyper_L);
1658 const static KeyCode hyper_r = XKeysymToKeycode(display_, XK_Hyper_R);
1662 ((keyboard_vector_[shift_l >> 3] >> (shift_l & 7)) & 1) != 0);
1664 ((keyboard_vector_[shift_r >> 3] >> (shift_r & 7)) & 1) != 0);
1667 ((keyboard_vector_[control_l >> 3] >> (control_l & 7)) & 1) != 0);
1669 ((keyboard_vector_[control_r >> 3] >> (control_r & 7)) & 1) != 0);
1676 ((keyboard_vector_[super_r >> 3] >> (super_r & 7)) & 1) != 0);
1679 ((keyboard_vector_[hyper_l >> 3] >> (hyper_l & 7)) & 1) != 0);
1681 ((keyboard_vector_[hyper_r >> 3] >> (hyper_r & 7)) & 1) != 0);
1688 Window root_return, child_return;
1692 if (XQueryPointer(display_,
1693 RootWindow(display_, DefaultScreen(display_)),
1700 &mask_return) == True)
1724 if (XQueryPointer(display,
1725 RootWindow(display, DefaultScreen(display)),
1732 &mask_return) == False)
1758#ifdef WITH_XWAYLAND_HACK
1772#ifdef WITH_XWAYLAND_HACK
1773 if (use_xwayland_hack) {
1774 if (child_return !=
None) {
1775 XFixesHideCursor(display_, child_return);
1780#if defined(WITH_X11_XINPUT) && defined(USE_X11_XINPUT_WARP)
1781 if ((xinput_version_.present) && (xinput_version_.major_version >= 2)) {
1784 if (XIGetClientPointer(display_,
None, &device_id) != False) {
1785 XIWarpPointer(display_, device_id,
None,
None, 0, 0, 0, 0, relx, rely);
1791 XWarpPointer(display_,
None,
None, 0, 0, 0, 0, relx, rely);
1794#ifdef WITH_XWAYLAND_HACK
1795 if (use_xwayland_hack) {
1796 if (child_return !=
None) {
1797 XFixesShowCursor(display_, child_return);
1827 GHOST_ASSERT((bad_wind !=
nullptr),
"addDirtyWindow() nullptr ptr trapped (window)");
1829 dirty_windows_.push_back(bad_wind);
1832bool GHOST_SystemX11::generateWindowExposeEvents()
1836 bool anyProcessed =
false;
1838 for (; w_start != w_end; ++w_start) {
1842 (*w_start)->validate();
1846 anyProcessed =
true;
1850 dirty_windows_.clear();
1851 return anyProcessed;
1855 XkbDescPtr xkb_descr,
1856 const KeyCode keycode)
1867#define GXMAP(k, x, y) \
1876 if ((key >= XK_A) && (key <= XK_Z)) {
1879 else if ((key >= XK_a) && (key <= XK_z)) {
1882 else if ((key >= XK_0) && (key <= XK_9)) {
1885 else if ((key >= XK_F1) && (key <= XK_F24)) {
1973#ifdef WITH_XF86KEYSYM
1979# ifdef XF86XK_AudioForward
1984#ifdef WITH_GHOST_DEBUG
1985 printf(
"%s: unknown key: %lu / 0x%lx\n", __func__, key, key);
1997#define MAKE_ID(a, b, c, d) (int(d) << 24 | int(c) << 16 | (b) << 8 | (a))
2001 GHOST_ASSERT(XkbKeyNameLength == 4,
"Name length is invalid!");
2002 if (keycode >= xkb_descr->min_key_code && keycode <= xkb_descr->max_key_code) {
2003 const char *id_str = xkb_descr->names->keys[keycode].name;
2004 const uint32_t
id =
MAKE_ID(id_str[0], id_str[1], id_str[2], id_str[3]);
2006 case MAKE_ID(
'T',
'L',
'D',
'E'):
2008 case MAKE_ID(
'L',
'S',
'G',
'T'):
2010#ifdef WITH_GHOST_DEBUG
2012 printf(
"%s unhandled keycode: %.*s\n", __func__, XkbKeyNameLength, id_str);
2017 else if (keycode != 0) {
2027#define XCLIB_XCOUT_NONE 0
2028#define XCLIB_XCOUT_SENTCONVSEL 1
2029#define XCLIB_XCOUT_INCR 2
2030#define XCLIB_XCOUT_FALLBACK 3
2031#define XCLIB_XCOUT_FALLBACK_UTF8 4
2032#define XCLIB_XCOUT_FALLBACK_COMP 5
2033#define XCLIB_XCOUT_FALLBACK_TEXT 6
2037 const XEvent *evt, Atom sel, Atom target,
uchar **txt,
ulong *
len,
uint *context)
const
2042 ulong pty_size, pty_items;
2045 const vector<GHOST_IWindow *> &win_vec =
window_manager_->getWindows();
2046 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2060 XConvertSelection(display_, sel, target,
atom_.XCLIP_OUT, win, CurrentTime);
2065 if (evt->type != SelectionNotify) {
2069 if (target ==
atom_.UTF8_STRING && evt->xselection.property ==
None) {
2073 if (target ==
atom_.COMPOUND_TEXT && evt->xselection.property ==
None) {
2077 if (target ==
atom_.TEXT && evt->xselection.property ==
None) {
2083 XGetWindowProperty(display_,
2097 if (pty_type ==
atom_.INCR) {
2099 XDeleteProperty(display_, win,
atom_.XCLIP_OUT);
2108 if (pty_format != 8) {
2114 XGetWindowProperty(display_,
2128 XDeleteProperty(display_, win,
atom_.XCLIP_OUT);
2131 ltxt = (
uchar *)malloc(pty_items);
2132 memcpy(ltxt, buffer, pty_items);
2153 if (evt->type != PropertyNotify) {
2158 if (evt->xproperty.state != PropertyNewValue) {
2163 XGetWindowProperty(display_,
2176 if (pty_format != 8) {
2181 XDeleteProperty(display_, win,
atom_.XCLIP_OUT);
2185 if (pty_size == 0) {
2188 XDeleteProperty(display_, win,
atom_.XCLIP_OUT);
2200 XGetWindowProperty(display_,
2220 ltxt = (
uchar *)realloc(ltxt, *
len);
2224 memcpy(<xt[*
len - pty_items], buffer, pty_items);
2230 XDeleteProperty(display_, win,
atom_.XCLIP_OUT);
2239 Atom target =
atom_.UTF8_STRING;
2248 if (selection == True) {
2249 sseln =
atom_.PRIMARY;
2252 sseln =
atom_.CLIPBOARD;
2255 const vector<GHOST_IWindow *> &win_vec =
window_manager_->getWindows();
2256 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2261 owner = XGetSelectionOwner(display_, sseln);
2263 if (sseln ==
atom_.CLIPBOARD) {
2265 sel_buf = (
char *)malloc(sel_buf_size);
2270 sel_buf = (
char *)malloc(sel_buf_size);
2274 if (owner ==
None) {
2279 vector<XEvent> restore_events;
2283 bool restore_this_event =
false;
2285 XNextEvent(display_, &evt);
2286 restore_this_event = (evt.type != SelectionNotify);
2292 if (restore_this_event) {
2293 restore_events.push_back(evt);
2299 target =
atom_.STRING;
2305 target =
atom_.COMPOUND_TEXT;
2311 target =
atom_.TEXT;
2325 while (!restore_events.empty()) {
2326 XPutBackEvent(display_, &restore_events.back());
2327 restore_events.pop_back();
2332 char *tmp_data = (
char *)malloc(sel_len + 1);
2333 memcpy(tmp_data, (
char *)sel_buf, sel_len);
2334 tmp_data[sel_len] =
'\0';
2336 if (sseln ==
atom_.STRING) {
2352 const vector<GHOST_IWindow *> &win_vec =
window_manager_->getWindows();
2353 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
2358 if (selection == False) {
2359 XSetSelectionOwner(display_,
atom_.CLIPBOARD, window_, CurrentTime);
2360 owner = XGetSelectionOwner(display_,
atom_.CLIPBOARD);
2365 size_t buffer_size = strlen(buffer) + 1;
2370 XSetSelectionOwner(display_,
atom_.PRIMARY, window_, CurrentTime);
2371 owner = XGetSelectionOwner(display_,
atom_.PRIMARY);
2376 size_t buffer_size = strlen(buffer) + 1;
2381 if (owner != window_) {
2382 fprintf(stderr,
"failed to own primary\n");
2436 XFillRectangle(display,
2444 XFillRectangle(display,
2452 XDrawString(display,
2479 data = strdup(text);
2480 for (tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps)) {
2485 data = strdup(text);
2486 *
str = (
char **)malloc(
size_t(*
count) *
sizeof(
char *));
2487 for (
i = 0, tok = strtok(
data, seps); tok !=
nullptr; tok = strtok(
nullptr, seps),
i++) {
2488 (*str)[
i] = strdup(tok);
2494 const char *message,
2495 const char *help_label,
2496 const char *continue_label,
2500 char **text_splitted =
nullptr;
2502 split(message,
"\n", &text_splitted, &textLines);
2509 int screen = DefaultScreen(display_);
2510 window = XCreateSimpleWindow(display_,
2511 RootWindow(display_, screen),
2517 BlackPixel(display_, screen),
2518 WhitePixel(display_, screen));
2522 hints.flags = PSize | PMinSize | PMaxSize;
2523 hints.min_width = hints.max_width = hints.base_width = dialog_data.
width;
2524 hints.min_height = hints.max_height = hints.base_height = dialog_data.
height;
2525 XSetWMNormalHints(display_, window, &hints);
2530 Atom wm_Name = XInternAtom(display_,
"_NET_WM_NAME", False);
2531 Atom utf8Str = XInternAtom(display_,
"UTF8_STRING", False);
2533 Atom winType = XInternAtom(display_,
"_NET_WM_WINDOW_TYPE", False);
2534 Atom typeDialog = XInternAtom(display_,
"_NET_WM_WINDOW_TYPE_DIALOG", False);
2536 XChangeProperty(display_,
2542 (
const uchar *)title,
2543 int(strlen(title)));
2546 display_, window, winType, XA_ATOM, 32, PropModeReplace, (
uchar *)&typeDialog, 1);
2550 XGCValues buttonBorderGCValues;
2551 buttonBorderGCValues.foreground = BlackPixel(display_, screen);
2552 buttonBorderGCValues.background = WhitePixel(display_, screen);
2553 XGCValues buttonGCValues;
2554 buttonGCValues.foreground = WhitePixel(display_, screen);
2555 buttonGCValues.background = BlackPixel(display_, screen);
2557 GC buttonBorderGC = XCreateGC(display_, window, GCForeground, &buttonBorderGCValues);
2558 GC buttonGC = XCreateGC(display_, window, GCForeground, &buttonGCValues);
2560 XSelectInput(display_, window, ExposureMask | ButtonPressMask | ButtonReleaseMask);
2561 XMapWindow(display_, window);
2563 const bool has_link = link && strlen(link);
2566 XNextEvent(display_, &
e);
2567 if (
e.type == Expose) {
2568 for (
int i = 0;
i < textLines;
i++) {
2569 XDrawString(display_,
2571 DefaultGC(display_, screen),
2575 int(strlen(text_splitted[
i])));
2577 dialog_data.
drawButton(display_, window, buttonBorderGC, buttonGC, 1, continue_label);
2579 dialog_data.
drawButton(display_, window, buttonBorderGC, buttonGC, 2, help_label);
2582 else if (
e.type == ButtonRelease) {
2588 string cmd =
"xdg-open \"" + string(link) +
"\"";
2589 if (system(cmd.c_str()) != 0) {
2590 GHOST_PRINTF(
"GHOST_SystemX11::showMessageBox: Unable to run system command [%s]",
2599 for (
int i = 0;
i < textLines;
i++) {
2600 free(text_splitted[
i]);
2602 free(text_splitted);
2604 XDestroyWindow(display_, window);
2605 XFreeGC(display_, buttonBorderGC);
2606 XFreeGC(display_, buttonGC);
2626 event_ms, eventType, draggedObjectType, window, mouseX, mouseY,
data));
2643 char error_code_str[512];
2645 XGetErrorText(display, event->error_code, error_code_str,
sizeof(error_code_str));
2648 "Received X11 Error:\n"
2649 "\terror code: %d\n"
2650 "\trequest code: %d\n"
2651 "\tminor code: %d\n"
2652 "\terror text: %s\n",
2654 event->request_code,
2669 fprintf(stderr,
"Ignoring Xlib error: error IO\n");
2675#ifdef WITH_X11_XINPUT
2677static bool is_filler_char(
char c)
2679 return isspace(c) ||
ELEM(c,
'_',
'-',
';',
':');
2683static bool match_token(
const char *haystack,
const char *needle)
2686 for (h = haystack; *h;) {
2687 while (*h && is_filler_char(*h)) {
2694 for (n = needle; *n && *h && tolower(*h) == tolower(*n); n++) {
2697 if (!*n && (is_filler_char(*h) || !*h)) {
2701 while (*h && !is_filler_char(*h)) {
2721 static const char *tablet_stylus_whitelist[] = {
"stylus",
"wizardpen",
"acecad",
"pen",
nullptr};
2723 static const char *type_blacklist[] = {
"pad",
"cursor",
"touch",
nullptr};
2726 for (
i = 0; type_blacklist[
i] !=
nullptr;
i++) {
2727 if (type && (strcasecmp(type, type_blacklist[
i]) == 0)) {
2733 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2734 if (type && match_token(type, tablet_stylus_whitelist[
i])) {
2738 if (type && match_token(type,
"eraser")) {
2741 for (
i = 0; tablet_stylus_whitelist[
i] !=
nullptr;
i++) {
2742 if (
name && match_token(
name, tablet_stylus_whitelist[
i])) {
2746 if (
name && match_token(
name,
"eraser")) {
2755void GHOST_SystemX11::refreshXInputDevices()
2757 if (xinput_version_.present) {
2759 clearXInputDevices();
2766 XDeviceInfo *device_info = XListInputDevices(display_, &device_count);
2768 for (
int i = 0;
i < device_count; ++
i) {
2769 char *device_type = device_info[
i].type ? XGetAtomName(display_, device_info[
i].type) :
2776 XFree((
void *)device_type);
2783 GHOST_TabletX11 xtablet = {tablet_mode};
2784 xtablet.ID = device_info[
i].id;
2785 xtablet.Device = XOpenDevice(display_, xtablet.ID);
2787 if (xtablet.Device !=
nullptr) {
2789 XAnyClassPtr ici = device_info[
i].inputclassinfo;
2791 if (ici !=
nullptr) {
2792 for (
int j = 0; j < device_info[
i].num_classes; ++j) {
2793 if (ici->c_class == ValuatorClass) {
2794 XValuatorInfo *xvi = (XValuatorInfo *)ici;
2795 if (xvi->axes !=
nullptr) {
2796 xtablet.PressureLevels = xvi->axes[2].max_value;
2798 if (xvi->num_axes > 3) {
2801 xtablet.XtiltLevels = xvi->axes[3].max_value;
2802 xtablet.YtiltLevels = xvi->axes[4].max_value;
2805 xtablet.XtiltLevels = 0;
2806 xtablet.YtiltLevels = 0;
2813 ici = (XAnyClassPtr)(((
char *)ici) + ici->length);
2817 xtablets_.push_back(xtablet);
2821 XFreeDeviceList(device_info);
2828void GHOST_SystemX11::clearXInputDevices()
2830 for (GHOST_TabletX11 &xtablet : xtablets_) {
2831 if (xtablet.Device) {
2832 XCloseDevice(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_kCapabilityCursorRGBA
@ GHOST_kCapabilityInputIME
@ GHOST_kCapabilityCursorGenerator
@ GHOST_kCapabilityClipboardImage
@ GHOST_kCapabilityWindowDecorationStyles
#define GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS(gpu_settings)
#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
#define GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS_OFFSCREEN(gpu_settings)
@ 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_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_GPUSettings gpu_settings, const bool exclusive=false, const bool is_dialog=false, const GHOST_IWindow *parent_window=nullptr) 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_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
GHOST_IContext * createOffscreenContext(GHOST_GPUSettings gpu_settings) 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
struct GHOST_SystemX11::@164270272260354005033176304166017055252265157316 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_WindowManager * window_manager_
GHOST_TSuccess pushEvent(const GHOST_IEvent *event)
GHOST_TSuccess init() override
GHOST_TimerManager * getTimerManager() const
GHOST_TSuccess exit() override
bool fireTimers(uint64_t time)
bool getValid() const override
GHOST_TabletData & GetTabletData()
GHOST_TSuccess setState(GHOST_TWindowState state) override
void getClientBounds(GHOST_Rect &bounds) const override
GHOST_TWindowState post_state_
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)