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);
514 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
515 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
517 for (; win_it != win_end; ++win_it) {
518 GHOST_WindowX11 *window =
static_cast<GHOST_WindowX11 *
>(*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) {
558 data->timestamp =
event->xbutton.time;
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)
586 init_timestamp_data
data;
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)
794 GHOST_WindowX11 *window = findGhostWindow(xe->xany.window);
795 GHOST_Event *g_event =
nullptr;
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();
873 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
874 vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
876 for (; win_it != win_end; ++win_it) {
877 GHOST_WindowX11 *window_xinput =
static_cast<GHOST_WindowX11 *
>(*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;
1001 g_event =
new GHOST_EventCursor(event_ms,
1004 xme.x_root + x_accum,
1005 xme.y_root + y_accum,
1010 g_event =
new GHOST_EventCursor(event_ms,
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) {
1272 g_event =
new GHOST_EventWheel(event_ms, window, 1);
1276 if (xbe.button == Button5) {
1277 if (xbe.type == ButtonPress) {
1278 g_event =
new GHOST_EventWheel(event_ms, window, -1);
1284 if (xbe.button == Button1) {
1287 else if (xbe.button == Button2) {
1290 else if (xbe.button == Button3) {
1296 else if (xbe.button == 6) {
1299 else if (xbe.button == 7) {
1302 else if (xbe.button == 8) {
1305 else if (xbe.button == 9) {
1312 g_event =
new GHOST_EventButton(event_ms, type, window, gbmask, window->
GetTabletData());
1317 case ConfigureNotify: {
1328 const XFocusChangeEvent &xfe = xe->xfocus;
1332 printf(
"X: focus %s for window %d\n", xfe.type == FocusIn ?
"in" :
"out",
int(xfe.window));
1340#if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING)
1341 XIC xic = window->getX11_XIC();
1343 if (xe->type == FocusIn) {
1355 case ClientMessage: {
1356 XClientMessageEvent &xcme = xe->xclient;
1358 if (((Atom)xcme.data.l[0]) ==
m_atom.WM_DELETE_WINDOW) {
1361 else if (((Atom)xcme.data.l[0]) ==
m_atom.WM_TAKE_FOCUS) {
1362 XWindowAttributes attr;
1376 if (XGetWindowAttributes(m_display, xcme.window, &attr) == True) {
1377 if (XGetInputFocus(m_display, &fwin, &revert_to) == True) {
1378 if (attr.map_state == IsViewable) {
1379 if (fwin != xcme.window) {
1380 XSetInputFocus(m_display, xcme.window, RevertToParent, xcme.data.l[1]);
1390 if (window->getDropTarget()->GHOST_HandleClientMessage(xe) ==
false) {
1405 case GraphicsExpose:
1417 const XCrossingEvent &xce = xe->xcrossing;
1419 if (xce.mode == NotifyNormal) {
1420 g_event =
new GHOST_EventCursor(event_ms,
1430 "X: %s window %d\n", xce.type == EnterNotify ?
"entering" :
"leaving",
int(xce.window));
1433 if (xce.type == EnterNotify) {
1464 case ReparentNotify:
1466 case SelectionRequest: {
1468 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;
2044 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
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;
2251 sseln =
m_atom.CLIPBOARD;
2254 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
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) {
2351 const vector<GHOST_IWindow *> &win_vec =
m_windowManager->getWindows();
2352 vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
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
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a vector
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
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
struct GHOST_SystemX11::@225015373161073305164235171050213346146315276316 m_atom
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override
void addDirtyWindow(GHOST_WindowX11 *bad_wind)
Atom _NET_WM_STATE_MAXIMIZED_VERT
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override
GHOST_IWindow * createWindow(const char *title, int32_t left, int32_t top, uint32_t width, uint32_t height, GHOST_TWindowState state, GHOST_GPUSettings gpuSettings, const bool exclusive=false, const bool is_dialog=false, const GHOST_IWindow *parentWindow=nullptr) override
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override
GHOST_TCapabilityFlag getCapabilities() const override
bool processEvents(bool waitForEvent) override
Atom _NET_WM_STATE_FULLSCREEN
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override
uint8_t getNumDisplays() const override
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
uint64_t ms_from_input_time(const Time timestamp) const
Atom _NET_WM_STATE_MAXIMIZED_HORZ
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override
GHOST_IContext * createOffscreenContext(GHOST_GPUSettings gpuSettings) override
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)
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
input_tx image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img") .compute_source("compositor_compute_preview.glsl") .do_static_compilation(true)
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)
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
unsigned __int64 uint64_t
GHOST_TDrawingContextType context_type
GHOST_GPUDevice preferred_device
void set(GHOST_TModifierKey mask, bool down)