wl_init.c 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950
  1. //========================================================================
  2. // GLFW 3.4 Wayland - www.glfw.org
  3. //------------------------------------------------------------------------
  4. // Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would
  17. // be appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not
  20. // be misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. //
  25. //========================================================================
  26. // It is fine to use C99 in this file because it will not be built with VS
  27. //========================================================================
  28. #define _GNU_SOURCE
  29. #include "internal.h"
  30. #include "backend_utils.h"
  31. #include "wl_client_side_decorations.h"
  32. #include "linux_desktop_settings.h"
  33. #include "../kitty/monotonic.h"
  34. #include "wl_text_input.h"
  35. #include "wayland-text-input-unstable-v3-client-protocol.h"
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <sys/mman.h>
  40. #include <unistd.h>
  41. #include <fcntl.h>
  42. #include <sys/socket.h>
  43. #include <wayland-client.h>
  44. #include <stdio.h>
  45. // errno.h needed for BSD code paths
  46. #include <errno.h>
  47. // Needed for the BTN_* defines
  48. #ifdef __has_include
  49. #if __has_include(<linux/input.h>)
  50. #include <linux/input.h>
  51. #elif __has_include(<dev/evdev/input.h>)
  52. #include <dev/evdev/input.h>
  53. #endif
  54. #else
  55. #include <linux/input.h>
  56. #endif
  57. #define debug debug_rendering
  58. #define x window->wl.allCursorPosX
  59. #define y window->wl.allCursorPosY
  60. static _GLFWwindow*
  61. get_window_from_surface(struct wl_surface* surface) {
  62. if (!surface) return NULL;
  63. _GLFWwindow *ans = wl_surface_get_user_data(surface);
  64. if (ans) {
  65. const _GLFWwindow *w = _glfw.windowListHead;
  66. while (w) {
  67. if (w == ans) return ans;
  68. w = w->next;
  69. }
  70. }
  71. return NULL;
  72. }
  73. static void
  74. pointerHandleEnter(
  75. void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface,
  76. wl_fixed_t sx, wl_fixed_t sy
  77. ) {
  78. _GLFWwindow* window = get_window_from_surface(surface);
  79. if (!window) return;
  80. _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial; _glfw.wl.pointer_enter_serial = serial;
  81. _glfw.wl.pointerFocus = window;
  82. window->wl.allCursorPosX = wl_fixed_to_double(sx);
  83. window->wl.allCursorPosY = wl_fixed_to_double(sy);
  84. if (surface != window->wl.surface) {
  85. csd_handle_pointer_event(window, -2, -2, surface);
  86. } else {
  87. window->wl.decorations.focus = CENTRAL_WINDOW;
  88. window->wl.hovered = true;
  89. window->wl.cursorPosX = x;
  90. window->wl.cursorPosY = y;
  91. _glfwPlatformSetCursor(window, window->wl.currentCursor);
  92. _glfwInputCursorEnter(window, true);
  93. }
  94. }
  95. static void
  96. pointerHandleLeave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface) {
  97. _GLFWwindow* window = _glfw.wl.pointerFocus;
  98. if (!window) return;
  99. _glfw.wl.serial = serial;
  100. _glfw.wl.pointerFocus = NULL;
  101. if (window->wl.surface == surface) {
  102. window->wl.hovered = false;
  103. _glfwInputCursorEnter(window, false);
  104. _glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
  105. } else csd_handle_pointer_event(window, -3, -3, surface);
  106. }
  107. static void
  108. pointerHandleMotion(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t time UNUSED, wl_fixed_t sx, wl_fixed_t sy) {
  109. _GLFWwindow* window = _glfw.wl.pointerFocus;
  110. if (!window || window->cursorMode == GLFW_CURSOR_DISABLED) return;
  111. window->wl.allCursorPosX = wl_fixed_to_double(sx);
  112. window->wl.allCursorPosY = wl_fixed_to_double(sy);
  113. if (window->wl.decorations.focus != CENTRAL_WINDOW) {
  114. csd_handle_pointer_event(window, -1, -1, NULL);
  115. } else {
  116. window->wl.cursorPosX = x;
  117. window->wl.cursorPosY = y;
  118. _glfwInputCursorPos(window, x, y);
  119. _glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
  120. }
  121. }
  122. static void pointerHandleButton(void* data UNUSED,
  123. struct wl_pointer* pointer UNUSED,
  124. uint32_t serial,
  125. uint32_t time UNUSED,
  126. uint32_t button,
  127. uint32_t state)
  128. {
  129. _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial;
  130. _GLFWwindow* window = _glfw.wl.pointerFocus;
  131. if (!window) return;
  132. if (window->wl.decorations.focus != CENTRAL_WINDOW) {
  133. csd_handle_pointer_event(window, button, state, NULL);
  134. return;
  135. }
  136. /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
  137. * codes. */
  138. int glfwButton = button - BTN_LEFT;
  139. _glfwInputMouseClick(
  140. window, glfwButton, state == WL_POINTER_BUTTON_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE, _glfw.wl.xkb.states.modifiers);
  141. }
  142. #undef x
  143. #undef y
  144. #define info (window->wl.pointer_curr_axis_info)
  145. static void
  146. pointer_handle_axis_common(enum _GLFWWaylandAxisEvent type, uint32_t axis, wl_fixed_t value) {
  147. _GLFWwindow* window = _glfw.wl.pointerFocus;
  148. if (!window || window->wl.decorations.focus != CENTRAL_WINDOW) return;
  149. float fval = (float) wl_fixed_to_double(value);
  150. #define CASE(type, type_const, axis, fval) \
  151. case type_const: \
  152. if (info.type.axis##_axis_type == AXIS_EVENT_UNKNOWN) { \
  153. info.type.axis##_axis_type = type_const; info.type.axis = 0.f; } \
  154. info.type.axis += fval; break;
  155. if (window) {
  156. switch ((enum wl_pointer_axis)axis) {
  157. case WL_POINTER_AXIS_VERTICAL_SCROLL:
  158. switch (type) {
  159. case AXIS_EVENT_UNKNOWN: break;
  160. CASE(discrete, AXIS_EVENT_DISCRETE, y, -fval); // wheel event
  161. CASE(discrete, AXIS_EVENT_VALUE120, y, -fval); // wheel event higher res than plain discrete
  162. CASE(continuous, AXIS_EVENT_CONTINUOUS, y, -fval); // touchpad, etc. high res
  163. }
  164. break;
  165. case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
  166. switch (type) {
  167. case AXIS_EVENT_UNKNOWN: break;
  168. CASE(discrete, AXIS_EVENT_DISCRETE, x, fval); // wheel event
  169. CASE(discrete, AXIS_EVENT_VALUE120, x, fval); // wheel event higher res than plain discrete
  170. CASE(continuous, AXIS_EVENT_CONTINUOUS, x, fval); // touchpad, etc. high res
  171. }
  172. break;
  173. }
  174. }
  175. #undef CASE
  176. }
  177. static void
  178. pointer_handle_axis(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t time, uint32_t axis, wl_fixed_t value) {
  179. _GLFWwindow* window = _glfw.wl.pointerFocus;
  180. if (!window) return;
  181. if (!info.timestamp_ns) info.timestamp_ns = ms_to_monotonic_t(time);
  182. pointer_handle_axis_common(AXIS_EVENT_CONTINUOUS, axis, value);
  183. }
  184. static void
  185. pointer_handle_frame(void *data UNUSED, struct wl_pointer *pointer UNUSED) {
  186. _GLFWwindow* window = _glfw.wl.pointerFocus;
  187. if (!window) return;
  188. float x = 0, y = 0;
  189. int highres = 0;
  190. if (info.discrete.y_axis_type != AXIS_EVENT_UNKNOWN) {
  191. y = info.discrete.y;
  192. if (info.discrete.y_axis_type == AXIS_EVENT_VALUE120) y /= 120.f;
  193. } else if (info.continuous.y_axis_type != AXIS_EVENT_UNKNOWN) {
  194. highres = 1;
  195. y = info.continuous.y;
  196. }
  197. if (info.discrete.x_axis_type != AXIS_EVENT_UNKNOWN) {
  198. x = info.discrete.x;
  199. if (info.discrete.x_axis_type == AXIS_EVENT_VALUE120) x /= 120.f;
  200. } else if (info.continuous.x_axis_type != AXIS_EVENT_UNKNOWN) {
  201. highres = 1;
  202. x = info.continuous.x;
  203. }
  204. /* clear pointer_curr_axis_info for next frame */
  205. memset(&info, 0, sizeof(info));
  206. if (x != 0.0f || y != 0.0f) {
  207. float scale = (float)_glfwWaylandWindowScale(window);
  208. y *= scale; x *= scale;
  209. _glfwInputScroll(window, -x, y, highres, _glfw.wl.xkb.states.modifiers);
  210. }
  211. }
  212. static void
  213. pointer_handle_axis_source(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t source UNUSED) { }
  214. static void
  215. pointer_handle_axis_stop(void *data UNUSED, struct wl_pointer *wl_pointer UNUSED, uint32_t time UNUSED, uint32_t axis UNUSED) { }
  216. static void
  217. pointer_handle_axis_discrete(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t discrete) {
  218. pointer_handle_axis_common(AXIS_EVENT_DISCRETE, axis, wl_fixed_from_int(discrete));
  219. }
  220. static void
  221. pointer_handle_axis_value120(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis, int32_t value120) {
  222. pointer_handle_axis_common(AXIS_EVENT_VALUE120, axis, wl_fixed_from_int(value120));
  223. }
  224. static void
  225. pointer_handle_axis_relative_direction(void *data UNUSED, struct wl_pointer *pointer UNUSED, uint32_t axis UNUSED, uint32_t axis_relative_direction UNUSED) { }
  226. #undef info
  227. static const struct wl_pointer_listener pointerListener = {
  228. .enter = pointerHandleEnter,
  229. .leave = pointerHandleLeave,
  230. .motion = pointerHandleMotion,
  231. .button = pointerHandleButton,
  232. .axis = pointer_handle_axis,
  233. .frame = pointer_handle_frame,
  234. .axis_source = pointer_handle_axis_source,
  235. .axis_stop = pointer_handle_axis_stop,
  236. .axis_discrete = pointer_handle_axis_discrete,
  237. #ifdef WL_POINTER_AXIS_VALUE120_SINCE_VERSION
  238. .axis_value120 = pointer_handle_axis_value120,
  239. #endif
  240. #ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION
  241. .axis_relative_direction = pointer_handle_axis_relative_direction,
  242. #endif
  243. };
  244. static void keyboardHandleKeymap(void* data UNUSED,
  245. struct wl_keyboard* keyboard UNUSED,
  246. uint32_t format,
  247. int fd,
  248. uint32_t size)
  249. {
  250. char* mapStr;
  251. if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1)
  252. {
  253. _glfwInputError(GLFW_PLATFORM_ERROR, "Unknown keymap format: %u", format);
  254. close(fd);
  255. return;
  256. }
  257. mapStr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
  258. if (mapStr == MAP_FAILED) {
  259. close(fd);
  260. _glfwInputError(GLFW_PLATFORM_ERROR, "Mapping of keymap file descriptor failed: %u", format);
  261. return;
  262. }
  263. glfw_xkb_compile_keymap(&_glfw.wl.xkb, mapStr);
  264. munmap(mapStr, size);
  265. close(fd);
  266. }
  267. static void keyboardHandleEnter(void* data UNUSED,
  268. struct wl_keyboard* keyboard UNUSED,
  269. uint32_t serial,
  270. struct wl_surface* surface,
  271. struct wl_array* keys)
  272. {
  273. _GLFWwindow* window = get_window_from_surface(surface);
  274. if (!window) return;
  275. _glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.keyboard_enter_serial = serial;
  276. _glfw.wl.keyboardFocusId = window->id;
  277. _glfwInputWindowFocus(window, true);
  278. uint32_t* key;
  279. if (keys && _glfw.wl.keyRepeatInfo.key) {
  280. wl_array_for_each(key, keys) {
  281. if (*key == _glfw.wl.keyRepeatInfo.key) {
  282. toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
  283. break;
  284. }
  285. }
  286. }
  287. }
  288. static void keyboardHandleLeave(void* data UNUSED,
  289. struct wl_keyboard* keyboard UNUSED,
  290. uint32_t serial,
  291. struct wl_surface* surface UNUSED)
  292. {
  293. _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
  294. if (!window)
  295. return;
  296. _glfw.wl.serial = serial;
  297. _glfw.wl.keyboardFocusId = 0;
  298. _glfwInputWindowFocus(window, false);
  299. toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
  300. }
  301. static void
  302. dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
  303. if (_glfw.wl.keyRepeatInfo.keyboardFocusId != _glfw.wl.keyboardFocusId || _glfw.wl.keyboardRepeatRate == 0) return;
  304. _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
  305. if (!window) return;
  306. glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
  307. changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate));
  308. toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
  309. }
  310. static void keyboardHandleKey(void* data UNUSED,
  311. struct wl_keyboard* keyboard UNUSED,
  312. uint32_t serial,
  313. uint32_t time UNUSED,
  314. uint32_t key,
  315. uint32_t state)
  316. {
  317. _GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
  318. if (!window)
  319. return;
  320. int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
  321. _glfw.wl.serial = serial; _glfw.wl.input_serial = serial;
  322. glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
  323. if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
  324. {
  325. _glfw.wl.keyRepeatInfo.key = key;
  326. _glfw.wl.keyRepeatInfo.keyboardFocusId = window->id;
  327. changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, _glfw.wl.keyboardRepeatDelay);
  328. toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
  329. } else if (action == GLFW_RELEASE && key == _glfw.wl.keyRepeatInfo.key) {
  330. _glfw.wl.keyRepeatInfo.key = 0;
  331. toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
  332. }
  333. }
  334. static void keyboardHandleModifiers(void* data UNUSED,
  335. struct wl_keyboard* keyboard UNUSED,
  336. uint32_t serial,
  337. uint32_t modsDepressed,
  338. uint32_t modsLatched,
  339. uint32_t modsLocked,
  340. uint32_t group)
  341. {
  342. _glfw.wl.serial = serial; _glfw.wl.input_serial = serial;
  343. glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
  344. }
  345. static void keyboardHandleRepeatInfo(void* data UNUSED,
  346. struct wl_keyboard* keyboard,
  347. int32_t rate,
  348. int32_t delay)
  349. {
  350. if (keyboard != _glfw.wl.keyboard)
  351. return;
  352. _glfw.wl.keyboardRepeatRate = rate;
  353. _glfw.wl.keyboardRepeatDelay = ms_to_monotonic_t(delay);
  354. }
  355. static const struct wl_keyboard_listener keyboardListener = {
  356. keyboardHandleKeymap,
  357. keyboardHandleEnter,
  358. keyboardHandleLeave,
  359. keyboardHandleKey,
  360. keyboardHandleModifiers,
  361. keyboardHandleRepeatInfo,
  362. };
  363. static void seatHandleCapabilities(void* data UNUSED,
  364. struct wl_seat* seat,
  365. enum wl_seat_capability caps)
  366. {
  367. if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer)
  368. {
  369. _glfw.wl.pointer = wl_seat_get_pointer(seat);
  370. wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL);
  371. if (_glfw.wl.wp_cursor_shape_manager_v1) {
  372. if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);
  373. _glfw.wl.wp_cursor_shape_device_v1 = NULL;
  374. _glfw.wl.wp_cursor_shape_device_v1 = wp_cursor_shape_manager_v1_get_pointer(_glfw.wl.wp_cursor_shape_manager_v1, _glfw.wl.pointer);
  375. }
  376. }
  377. else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer)
  378. {
  379. if (_glfw.wl.wp_cursor_shape_device_v1) wp_cursor_shape_device_v1_destroy(_glfw.wl.wp_cursor_shape_device_v1);
  380. _glfw.wl.wp_cursor_shape_device_v1 = NULL;
  381. wl_pointer_destroy(_glfw.wl.pointer);
  382. _glfw.wl.pointer = NULL;
  383. if (_glfw.wl.cursorAnimationTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
  384. }
  385. if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard)
  386. {
  387. _glfw.wl.keyboard = wl_seat_get_keyboard(seat);
  388. wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL);
  389. }
  390. else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard)
  391. {
  392. wl_keyboard_destroy(_glfw.wl.keyboard);
  393. _glfw.wl.keyboard = NULL;
  394. _glfw.wl.keyboardFocusId = 0;
  395. if (_glfw.wl.keyRepeatInfo.keyRepeatTimer) toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
  396. }
  397. }
  398. static void seatHandleName(void* data UNUSED,
  399. struct wl_seat* seat UNUSED,
  400. const char* name UNUSED)
  401. {
  402. }
  403. static const struct wl_seat_listener seatListener = {
  404. seatHandleCapabilities,
  405. seatHandleName,
  406. };
  407. static void wmBaseHandlePing(void* data UNUSED,
  408. struct xdg_wm_base* wmBase,
  409. uint32_t serial)
  410. {
  411. xdg_wm_base_pong(wmBase, serial);
  412. }
  413. static const struct xdg_wm_base_listener wmBaseListener = {
  414. wmBaseHandlePing
  415. };
  416. static void registryHandleGlobal(void* data UNUSED,
  417. struct wl_registry* registry,
  418. uint32_t name,
  419. const char* interface,
  420. uint32_t version)
  421. {
  422. #define is(x) strcmp(interface, x##_interface.name) == 0
  423. if (is(wl_compositor))
  424. {
  425. #ifdef WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION
  426. _glfw.wl.compositorVersion = MIN(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION, (int)version);
  427. _glfw.wl.has_preferred_buffer_scale = _glfw.wl.compositorVersion >= WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION;
  428. #else
  429. _glfw.wl.compositorVersion = MIN(3, (int)version);
  430. #endif
  431. _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion);
  432. }
  433. else if (is(wl_subcompositor))
  434. {
  435. _glfw.wl.subcompositor =
  436. wl_registry_bind(registry, name, &wl_subcompositor_interface, 1);
  437. }
  438. else if (is(wl_shm))
  439. {
  440. _glfw.wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
  441. }
  442. else if (is(wl_output))
  443. {
  444. _glfwAddOutputWayland(name, version);
  445. }
  446. else if (is(wl_seat))
  447. {
  448. if (!_glfw.wl.seat)
  449. {
  450. #ifdef WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION
  451. _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_RELATIVE_DIRECTION_SINCE_VERSION, (int)version);
  452. #elif defined(WL_POINTER_AXIS_VALUE120_SINCE_VERSION)
  453. _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_VALUE120_SINCE_VERSION, (int)version);
  454. #else
  455. _glfw.wl.seatVersion = MIN(WL_POINTER_AXIS_DISCRETE_SINCE_VERSION, version);
  456. #endif
  457. _glfw.wl.seat =
  458. wl_registry_bind(registry, name, &wl_seat_interface,
  459. _glfw.wl.seatVersion);
  460. wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL);
  461. }
  462. if (_glfw.wl.seat) {
  463. if (_glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) _glfwSetupWaylandDataDevice();
  464. if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
  465. _glfwSetupWaylandPrimarySelectionDevice();
  466. }
  467. }
  468. }
  469. else if (is(xdg_wm_base))
  470. {
  471. _glfw.wl.xdg_wm_base_version = 1;
  472. #ifdef XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION
  473. _glfw.wl.xdg_wm_base_version = MIN(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION, (int)version);
  474. #elif defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
  475. _glfw.wl.xdg_wm_base_version = MIN(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION, (int)version);
  476. #endif
  477. _glfw.wl.wmBase = wl_registry_bind(registry, name, &xdg_wm_base_interface, _glfw.wl.xdg_wm_base_version);
  478. xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL);
  479. }
  480. else if (is(zxdg_decoration_manager_v1))
  481. {
  482. _glfw.wl.decorationManager =
  483. wl_registry_bind(registry, name,
  484. &zxdg_decoration_manager_v1_interface, 1);
  485. }
  486. else if (is(zwp_relative_pointer_manager_v1))
  487. {
  488. _glfw.wl.relativePointerManager =
  489. wl_registry_bind(registry, name,
  490. &zwp_relative_pointer_manager_v1_interface,
  491. 1);
  492. }
  493. else if (is(zwp_pointer_constraints_v1))
  494. {
  495. _glfw.wl.pointerConstraints =
  496. wl_registry_bind(registry, name,
  497. &zwp_pointer_constraints_v1_interface,
  498. 1);
  499. }
  500. else if (is(zwp_text_input_manager_v3))
  501. {
  502. _glfwWaylandBindTextInput(registry, name);
  503. }
  504. else if (is(wl_data_device_manager))
  505. {
  506. _glfw.wl.dataDeviceManager =
  507. wl_registry_bind(registry, name,
  508. &wl_data_device_manager_interface,
  509. 1);
  510. if (_glfw.wl.seat && _glfw.wl.dataDeviceManager && !_glfw.wl.dataDevice) {
  511. _glfwSetupWaylandDataDevice();
  512. }
  513. }
  514. else if (is(zwp_primary_selection_device_manager_v1))
  515. {
  516. _glfw.wl.primarySelectionDeviceManager =
  517. wl_registry_bind(registry, name,
  518. &zwp_primary_selection_device_manager_v1_interface,
  519. 1);
  520. if (_glfw.wl.seat && _glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
  521. _glfwSetupWaylandPrimarySelectionDevice();
  522. }
  523. }
  524. else if (is(wp_single_pixel_buffer_manager_v1)) {
  525. _glfw.wl.wp_single_pixel_buffer_manager_v1 = wl_registry_bind(registry, name, &wp_single_pixel_buffer_manager_v1_interface, 1);
  526. }
  527. else if (is(xdg_activation_v1)) {
  528. _glfw.wl.xdg_activation_v1 = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1);
  529. }
  530. else if (is(wp_cursor_shape_manager_v1)) {
  531. _glfw.wl.wp_cursor_shape_manager_v1 = wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, 1);
  532. }
  533. else if (is(wp_fractional_scale_manager_v1)) {
  534. _glfw.wl.wp_fractional_scale_manager_v1 = wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1);
  535. }
  536. else if (is(wp_viewporter)) {
  537. _glfw.wl.wp_viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
  538. }
  539. else if (is(org_kde_kwin_blur_manager)) {
  540. _glfw.wl.org_kde_kwin_blur_manager = wl_registry_bind(registry, name, &org_kde_kwin_blur_manager_interface, 1);
  541. }
  542. else if (is(zwlr_layer_shell_v1)) {
  543. if (version >= 4) {
  544. _glfw.wl.zwlr_layer_shell_v1_version = version;
  545. _glfw.wl.zwlr_layer_shell_v1 = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, version);
  546. }
  547. }
  548. else if (is(zwp_idle_inhibit_manager_v1)) {
  549. _glfw.wl.idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
  550. }
  551. else if (is(xdg_toplevel_icon_manager_v1)) {
  552. _glfw.wl.xdg_toplevel_icon_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
  553. }
  554. else if (is(xdg_system_bell_v1)) {
  555. _glfw.wl.xdg_system_bell_v1 = wl_registry_bind(registry, name, &xdg_system_bell_v1_interface, 1);
  556. } else if (is(xdg_toplevel_tag_manager_v1)) {
  557. _glfw.wl.xdg_toplevel_tag_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_tag_manager_v1_interface, 1);
  558. }
  559. #undef is
  560. }
  561. static void registryHandleGlobalRemove(void *data UNUSED,
  562. struct wl_registry *registry UNUSED,
  563. uint32_t name)
  564. {
  565. _GLFWmonitor* monitor;
  566. for (int i = 0; i < _glfw.monitorCount; ++i)
  567. {
  568. monitor = _glfw.monitors[i];
  569. if (monitor->wl.name == name)
  570. {
  571. for (_GLFWwindow *window = _glfw.windowListHead; window; window = window->next) {
  572. for (int m = window->wl.monitorsCount - 1; m >= 0; m--) {
  573. if (window->wl.monitors[m] == monitor) {
  574. remove_i_from_array(window->wl.monitors, m, window->wl.monitorsCount);
  575. }
  576. }
  577. }
  578. _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0);
  579. return;
  580. }
  581. }
  582. }
  583. static const struct wl_registry_listener registryListener = {
  584. registryHandleGlobal,
  585. registryHandleGlobalRemove
  586. };
  587. GLFWAPI GLFWColorScheme glfwGetCurrentSystemColorTheme(bool query_if_unintialized) {
  588. return glfw_current_system_color_theme(query_if_unintialized);
  589. }
  590. static pid_t
  591. get_socket_peer_pid(int fd) {
  592. (void)fd;
  593. #ifdef __linux__
  594. struct ucred ucred;
  595. socklen_t len = sizeof(struct ucred);
  596. return (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) ? -1 : ucred.pid;
  597. #elif defined(LOCAL_PEERCRED) && defined(XUCRED_VERSION)
  598. struct xucred peercred;
  599. socklen_t peercredlen = sizeof(peercred);
  600. return (getsockopt(c->fd, LOCAL_PEERCRED, 1, (void *)&peercred, &peercredlen) == 0 && peercred.cr_version == XUCRED_VERSION) ? peercred.cr_pid : -1;
  601. #elif defined(LOCAL_PEERPID)
  602. pid_t pid;
  603. socklen_t pid_size = sizeof(pid);
  604. return getsockopt(client, SOL_LOCAL, LOCAL_PEERPID, &pid, &pid_size) == -1 ? -1 : pid;
  605. #else
  606. errno = ENOSYS;
  607. return -1;
  608. #endif
  609. }
  610. GLFWAPI pid_t glfwWaylandCompositorPID(void) {
  611. if (!_glfw.wl.display) return -1;
  612. int fd = wl_display_get_fd(_glfw.wl.display);
  613. if (fd < 0) return -1;
  614. return get_socket_peer_pid(fd);
  615. }
  616. const char*
  617. _glfwWaylandCompositorName(void) {
  618. static bool probed = false;
  619. if (!probed) {
  620. probed = true;
  621. static const size_t sz = 1024;
  622. _glfw.wl.compositor_name = malloc(sz);
  623. if (!_glfw.wl.compositor_name) return "";
  624. char *ans = _glfw.wl.compositor_name; ans[0] = 0;
  625. pid_t cpid = glfwWaylandCompositorPID();
  626. if (cpid < 0) return ans;
  627. snprintf(ans, sz, "/proc/%d/cmdline", cpid);
  628. int fd = open(ans, O_RDONLY | O_CLOEXEC);
  629. if (fd < 0) {
  630. ans[0] = 0;
  631. } else {
  632. ssize_t n;
  633. while (true) {
  634. n = read(fd, ans, sz-1);
  635. if (n < 0 && errno == EINTR) continue;
  636. close(fd); break;
  637. }
  638. ans[n < 0 ? 0 : n] = 0;
  639. }
  640. }
  641. return _glfw.wl.compositor_name ? _glfw.wl.compositor_name : "";
  642. }
  643. //////////////////////////////////////////////////////////////////////////
  644. ////// GLFW platform API //////
  645. //////////////////////////////////////////////////////////////////////////
  646. static const char*
  647. get_compositor_missing_capabilities(void) {
  648. #define C(title, x) if (!_glfw.wl.x) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", #title);
  649. static char buf[512];
  650. char *p = buf;
  651. *p = 0;
  652. C(viewporter, wp_viewporter); C(fractional_scale, wp_fractional_scale_manager_v1);
  653. C(blur, org_kde_kwin_blur_manager); C(server_side_decorations, decorationManager);
  654. C(cursor_shape, wp_cursor_shape_manager_v1); C(layer_shell, zwlr_layer_shell_v1);
  655. C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);
  656. C(idle_inhibit, idle_inhibit_manager); C(icon, xdg_toplevel_icon_manager_v1); C(bell, xdg_system_bell_v1);
  657. C(window-tag, xdg_toplevel_tag_manager_v1);
  658. if (_glfw.wl.xdg_wm_base_version < 6) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", "window-state-suspended");
  659. if (_glfw.wl.xdg_wm_base_version < 5) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", "window-capabilities");
  660. #undef C
  661. while (p > buf && (p - 1)[0] == ' ') { p--; *p = 0; }
  662. return buf;
  663. }
  664. GLFWAPI const char* glfwWaylandMissingCapabilities(void) { return get_compositor_missing_capabilities(); }
  665. int _glfwPlatformInit(bool *supports_window_occlusion)
  666. {
  667. int i;
  668. _GLFWmonitor* monitor;
  669. _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
  670. if (!_glfw.wl.cursor.handle)
  671. {
  672. _glfwInputError(GLFW_PLATFORM_ERROR,
  673. "Wayland: Failed to open libwayland-cursor");
  674. return false;
  675. }
  676. glfw_dlsym(_glfw.wl.cursor.theme_load, _glfw.wl.cursor.handle, "wl_cursor_theme_load");
  677. glfw_dlsym(_glfw.wl.cursor.theme_destroy, _glfw.wl.cursor.handle, "wl_cursor_theme_destroy");
  678. glfw_dlsym(_glfw.wl.cursor.theme_get_cursor, _glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor");
  679. glfw_dlsym(_glfw.wl.cursor.image_get_buffer, _glfw.wl.cursor.handle, "wl_cursor_image_get_buffer");
  680. _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1");
  681. if (!_glfw.wl.egl.handle)
  682. {
  683. _glfwInputError(GLFW_PLATFORM_ERROR,
  684. "Wayland: Failed to open libwayland-egl");
  685. return false;
  686. }
  687. glfw_dlsym(_glfw.wl.egl.window_create, _glfw.wl.egl.handle, "wl_egl_window_create");
  688. glfw_dlsym(_glfw.wl.egl.window_destroy, _glfw.wl.egl.handle, "wl_egl_window_destroy");
  689. glfw_dlsym(_glfw.wl.egl.window_resize, _glfw.wl.egl.handle, "wl_egl_window_resize");
  690. _glfw.wl.display = wl_display_connect(NULL);
  691. if (!_glfw.wl.display)
  692. {
  693. _glfwInputError(GLFW_PLATFORM_ERROR,
  694. "Wayland: Failed to connect to display");
  695. return false;
  696. }
  697. if (!initPollData(&_glfw.wl.eventLoopData, wl_display_get_fd(_glfw.wl.display))) {
  698. _glfwInputError(GLFW_PLATFORM_ERROR,
  699. "Wayland: Failed to initialize event loop data");
  700. }
  701. glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData);
  702. glfw_initialize_desktop_settings();
  703. _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", ms_to_monotonic_t(500ll), 0, true, dispatchPendingKeyRepeats, NULL, NULL);
  704. _glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", ms_to_monotonic_t(500ll), 0, true, animateCursorImage, NULL, NULL);
  705. _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
  706. wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);
  707. if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return false;
  708. // Sync so we got all registry objects
  709. wl_display_roundtrip(_glfw.wl.display);
  710. _glfwWaylandInitTextInput();
  711. // Sync so we got all initial output events
  712. wl_display_roundtrip(_glfw.wl.display);
  713. for (i = 0; i < _glfw.monitorCount; ++i)
  714. {
  715. monitor = _glfw.monitors[i];
  716. if (monitor->widthMM <= 0 || monitor->heightMM <= 0)
  717. {
  718. // If Wayland does not provide a physical size, assume the default 96 DPI
  719. monitor->widthMM = (int) (monitor->modes[monitor->wl.currentMode].width * 25.4f / 96.f);
  720. monitor->heightMM = (int) (monitor->modes[monitor->wl.currentMode].height * 25.4f / 96.f);
  721. }
  722. }
  723. if (!_glfw.wl.wmBase)
  724. {
  725. _glfwInputError(GLFW_PLATFORM_ERROR,
  726. "Wayland: Failed to find xdg-shell in your compositor");
  727. return false;
  728. }
  729. if (_glfw.wl.shm)
  730. {
  731. _glfw.wl.cursorSurface =
  732. wl_compositor_create_surface(_glfw.wl.compositor);
  733. }
  734. else
  735. {
  736. _glfwInputError(GLFW_PLATFORM_ERROR,
  737. "Wayland: Failed to find Wayland SHM");
  738. return false;
  739. }
  740. if (_glfw.hints.init.debugRendering) {
  741. const char *mc = get_compositor_missing_capabilities();
  742. if (mc && mc[0]) debug("Compositor missing capabilities: %s\n", mc);
  743. }
  744. *supports_window_occlusion = _glfw.wl.xdg_wm_base_version > 5;
  745. return true;
  746. }
  747. void _glfwPlatformTerminate(void)
  748. {
  749. if (_glfw.wl.activation_requests.array) {
  750. for (size_t i=0; i < _glfw.wl.activation_requests.sz; i++) {
  751. glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i;
  752. if (r->callback) r->callback(NULL, NULL, r->callback_data);
  753. xdg_activation_token_v1_destroy(r->token);
  754. }
  755. free(_glfw.wl.activation_requests.array);
  756. }
  757. _glfwTerminateEGL();
  758. if (_glfw.wl.egl.handle)
  759. {
  760. _glfw_dlclose(_glfw.wl.egl.handle);
  761. _glfw.wl.egl.handle = NULL;
  762. }
  763. glfw_xkb_release(&_glfw.wl.xkb);
  764. glfw_dbus_terminate(&_glfw.wl.dbus);
  765. glfw_wlc_destroy();
  766. if (_glfw.wl.cursor.handle)
  767. {
  768. _glfw_dlclose(_glfw.wl.cursor.handle);
  769. _glfw.wl.cursor.handle = NULL;
  770. }
  771. if (_glfw.wl.cursorSurface)
  772. wl_surface_destroy(_glfw.wl.cursorSurface);
  773. if (_glfw.wl.subcompositor)
  774. wl_subcompositor_destroy(_glfw.wl.subcompositor);
  775. if (_glfw.wl.compositor)
  776. wl_compositor_destroy(_glfw.wl.compositor);
  777. if (_glfw.wl.shm)
  778. wl_shm_destroy(_glfw.wl.shm);
  779. if (_glfw.wl.decorationManager)
  780. zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
  781. if (_glfw.wl.wmBase)
  782. xdg_wm_base_destroy(_glfw.wl.wmBase);
  783. if (_glfw.wl.pointer)
  784. wl_pointer_destroy(_glfw.wl.pointer);
  785. if (_glfw.wl.keyboard)
  786. wl_keyboard_destroy(_glfw.wl.keyboard);
  787. if (_glfw.wl.seat)
  788. wl_seat_destroy(_glfw.wl.seat);
  789. if (_glfw.wl.relativePointerManager)
  790. zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
  791. if (_glfw.wl.pointerConstraints)
  792. zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
  793. _glfwWaylandDestroyTextInput();
  794. if (_glfw.wl.dataSourceForClipboard)
  795. wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
  796. if (_glfw.wl.dataSourceForPrimarySelection)
  797. zwp_primary_selection_source_v1_destroy(_glfw.wl.dataSourceForPrimarySelection);
  798. for (size_t doi=0; doi < arraysz(_glfw.wl.dataOffers); doi++) {
  799. if (_glfw.wl.dataOffers[doi].id) {
  800. destroy_data_offer(&_glfw.wl.dataOffers[doi]);
  801. }
  802. }
  803. if (_glfw.wl.dataDevice)
  804. wl_data_device_destroy(_glfw.wl.dataDevice);
  805. if (_glfw.wl.dataDeviceManager)
  806. wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager);
  807. if (_glfw.wl.primarySelectionDevice)
  808. zwp_primary_selection_device_v1_destroy(_glfw.wl.primarySelectionDevice);
  809. if (_glfw.wl.primarySelectionDeviceManager)
  810. zwp_primary_selection_device_manager_v1_destroy(_glfw.wl.primarySelectionDeviceManager);
  811. if (_glfw.wl.xdg_activation_v1)
  812. xdg_activation_v1_destroy(_glfw.wl.xdg_activation_v1);
  813. if (_glfw.wl.xdg_toplevel_icon_manager_v1)
  814. xdg_toplevel_icon_manager_v1_destroy(_glfw.wl.xdg_toplevel_icon_manager_v1);
  815. if (_glfw.wl.xdg_system_bell_v1)
  816. xdg_system_bell_v1_destroy(_glfw.wl.xdg_system_bell_v1);
  817. if (_glfw.wl.xdg_toplevel_tag_manager_v1)
  818. xdg_toplevel_tag_manager_v1_destroy(_glfw.wl.xdg_toplevel_tag_manager_v1);
  819. if (_glfw.wl.wp_single_pixel_buffer_manager_v1)
  820. wp_single_pixel_buffer_manager_v1_destroy(_glfw.wl.wp_single_pixel_buffer_manager_v1);
  821. if (_glfw.wl.wp_cursor_shape_manager_v1)
  822. wp_cursor_shape_manager_v1_destroy(_glfw.wl.wp_cursor_shape_manager_v1);
  823. if (_glfw.wl.wp_viewporter)
  824. wp_viewporter_destroy(_glfw.wl.wp_viewporter);
  825. if (_glfw.wl.wp_fractional_scale_manager_v1)
  826. wp_fractional_scale_manager_v1_destroy(_glfw.wl.wp_fractional_scale_manager_v1);
  827. if (_glfw.wl.org_kde_kwin_blur_manager)
  828. org_kde_kwin_blur_manager_destroy(_glfw.wl.org_kde_kwin_blur_manager);
  829. if (_glfw.wl.zwlr_layer_shell_v1)
  830. zwlr_layer_shell_v1_destroy(_glfw.wl.zwlr_layer_shell_v1);
  831. if (_glfw.wl.idle_inhibit_manager)
  832. zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idle_inhibit_manager);
  833. if (_glfw.wl.registry)
  834. wl_registry_destroy(_glfw.wl.registry);
  835. if (_glfw.wl.display)
  836. {
  837. wl_display_flush(_glfw.wl.display);
  838. wl_display_disconnect(_glfw.wl.display);
  839. _glfw.wl.display = NULL;
  840. }
  841. finalizePollData(&_glfw.wl.eventLoopData);
  842. if (_glfw.wl.compositor_name) {
  843. free(_glfw.wl.compositor_name);
  844. _glfw.wl.compositor_name = NULL;
  845. }
  846. }
  847. #define GLFW_LOOP_BACKEND wl
  848. #include "main_loop.h"
  849. const char* _glfwPlatformGetVersionString(void)
  850. {
  851. (void)keep_going;
  852. return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa"
  853. #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK)
  854. " clock_gettime"
  855. #else
  856. " gettimeofday"
  857. #endif
  858. " evdev"
  859. #if defined(_GLFW_BUILD_DLL)
  860. " shared"
  861. #endif
  862. ;
  863. }