You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2211 lines
71KB

  1. /* ----
  2. * ---- file : lglw_linux.c (**stub**)
  3. * ---- author : bsp
  4. * ---- legal : Distributed under terms of the MIT LICENSE (MIT).
  5. * ----
  6. * ---- Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * ---- of this software and associated documentation files (the "Software"), to deal
  8. * ---- in the Software without restriction, including without limitation the rights
  9. * ---- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * ---- copies of the Software, and to permit persons to whom the Software is
  11. * ---- furnished to do so, subject to the following conditions:
  12. * ----
  13. * ---- The above copyright notice and this permission notice shall be included in
  14. * ---- all copies or substantial portions of the Software.
  15. * ----
  16. * ---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * ---- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * ---- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * ---- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * ---- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * ---- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * ---- THE SOFTWARE.
  23. * ----
  24. * ---- info : This is part of the "lglw" package.
  25. * ----
  26. * ---- created: 04Aug2018
  27. * ---- changed: 05Aug2018, 06Aug2018, 07Aug2018, 08Aug2018, 09Aug2018, 18Aug2018, 10Oct2018, 16Oct2018
  28. * ----
  29. * ----
  30. */
  31. // #define USE_XEVENTPROC defined
  32. #include "lglw.h"
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <stdarg.h>
  37. #include <X11/Xlib.h>
  38. #include <X11/Xutil.h>
  39. #include <X11/Xos.h>
  40. #include <X11/Xatom.h>
  41. #include <GL/gl.h>
  42. #include <GL/glx.h>
  43. #ifdef ARCH_X64
  44. #include <sys/mman.h>
  45. #include <unistd.h>
  46. #endif // ARCH_X64
  47. //
  48. // Regular log entry (low frequency)
  49. //
  50. // #define Dlog_verbose if(1);else printf
  51. #define Dlog if(0);else lglw_log
  52. //
  53. // Verbose log entry
  54. //
  55. #define Dlog_v if(0);else lglw_log
  56. //
  57. // Very-verbose log entry
  58. //
  59. #define Dlog_vv if(1);else lglw_log
  60. //
  61. // Very-very-verbose log entry
  62. //
  63. #define Dlog_vvv if(1);else lglw_log
  64. //
  65. // Print to stdout
  66. //
  67. // Please use the Dlog* macros instead!
  68. //
  69. #define Dprintf if(0);else printf
  70. #define Dprintf_verbose if(1);else printf
  71. // ---------------------------------------------------------------------------- macros and defines
  72. #define LGLW(a) lglw_int_t *lglw = ((lglw_int_t*)(a))
  73. #define LGLW_DEFAULT_HIDDEN_W (800)
  74. #define LGLW_DEFAULT_HIDDEN_H (600)
  75. #define LGLW_MOUSE_TOUCH_LMB_TIMEOUT (250u)
  76. #define LGLW_MOUSE_TOUCH_RMB_TIMEOUT (500u)
  77. #define LGLW_MOUSE_TOUCH_RMB_STATE_IDLE (0u)
  78. #define LGLW_MOUSE_TOUCH_RMB_STATE_LMB (1u)
  79. #define LGLW_MOUSE_TOUCH_RMB_STATE_WAIT (2u)
  80. #define LGLW_MOUSE_TOUCH_RMB_STATE_RMB (3u)
  81. #define LGLW_MOUSE_TOUCH_RMB_MOVE_THRESHOLD (7u)
  82. #define sABS(x) (((x)>0)?(x):-(x))
  83. // ---------------------------------------------------------------------------- structs and typedefs
  84. typedef struct lglw_int_s {
  85. void *user_data; // arbitrary user data
  86. Display *xdsp;
  87. XVisualInfo *vi;
  88. Colormap cmap;
  89. Window parent_xwnd; // created by host
  90. struct {
  91. lglw_vec2i_t size;
  92. Window xwnd;
  93. } hidden;
  94. struct {
  95. lglw_vec2i_t size;
  96. Window xwnd;
  97. lglw_bool_t mapped;
  98. int32_t swap_interval;
  99. lglw_bool_t b_owner;
  100. } win;
  101. GLXContext ctx;
  102. struct {
  103. GLXContext ctx;
  104. GLXDrawable drw;
  105. } prev;
  106. struct {
  107. uint32_t kmod_state; // See LGLW_KMOD_xxx
  108. lglw_keyboard_fxn_t cbk;
  109. } keyboard;
  110. struct {
  111. lglw_vec2i_t p; // last seen mouse position
  112. uint32_t button_state;
  113. lglw_mouse_fxn_t cbk;
  114. struct {
  115. uint32_t mode;
  116. lglw_vec2i_t p; // grab-start mouse position
  117. lglw_bool_t b_queue_warp;
  118. lglw_vec2i_t last_p;
  119. } grab;
  120. struct {
  121. lglw_bool_t b_enable;
  122. lglw_bool_t b_update_queued;
  123. lglw_bool_t b_syn_rmb;
  124. uint32_t syn_rmb_hold_state; // see LGLW_MOUSE_TOUCH_RMB_STATE_xxx
  125. uint32_t hold_start_ms;
  126. lglw_vec2i_t hold_start_p;
  127. } touch;
  128. } mouse;
  129. struct {
  130. uint32_t state;
  131. lglw_focus_fxn_t cbk;
  132. } focus;
  133. struct {
  134. lglw_bool_t b_running;
  135. lglw_timer_fxn_t cbk;
  136. struct timeval tv_start;
  137. uint32_t interval_ms;
  138. uint32_t last_ms;
  139. } timer;
  140. struct {
  141. uint32_t numChars;
  142. char *data;
  143. } clipboard;
  144. struct {
  145. lglw_dropfiles_fxn_t cbk;
  146. } dropfiles;
  147. struct {
  148. lglw_redraw_fxn_t cbk;
  149. } redraw;
  150. } lglw_int_t;
  151. // ---------------------------------------------------------------------------- module fxn fwd decls
  152. static lglw_bool_t loc_create_hidden_window (lglw_int_t *lglw, int32_t _w, int32_t _h);
  153. static void loc_destroy_hidden_window(lglw_int_t *lglw);
  154. static lglw_bool_t loc_handle_key (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey);
  155. // static lglw_bool_t loc_touchkeyboard_get_rect (RECT *rect);
  156. // static lglw_bool_t loc_touchkeyboard_is_visible (void);
  157. extern lglw_bool_t lglw_int_touchkeyboard_toggle (void);
  158. static void loc_handle_mouseleave (lglw_int_t *lglw);
  159. static void loc_handle_mouseenter (lglw_int_t *lglw);
  160. static void loc_handle_mousebutton (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button);
  161. static void loc_handle_mousemotion (lglw_int_t *lglw);
  162. static void loc_handle_queued_mouse_warp (lglw_int_t *lglw);
  163. static void loc_enable_dropfiles (lglw_int_t *lglw, lglw_bool_t _bEnable);
  164. static void loc_eventProc (XEvent *xev, lglw_int_t *lglw);
  165. static void loc_XEventProc (void *_xevent);
  166. static void loc_setProperty (Display *_display, Window _window, const char *_name, void *_value);
  167. static void *loc_getProperty (Display *_display, Window _window, const char *_name);
  168. static void loc_setEventProc (Display *display, Window window);
  169. static void loc_millisec_init (lglw_int_t *lglw);
  170. static uint32_t loc_millisec_delta (lglw_int_t *lglw); // return millisec since init()
  171. static void loc_process_timer (lglw_int_t *lglw);
  172. // ---------------------------------------------------------------------------- lglw_millisec_init
  173. static void loc_millisec_init (lglw_int_t *lglw) {
  174. gettimeofday(&lglw->timer.tv_start, 0);
  175. }
  176. // ---------------------------------------------------------------------------- lglw_millisec_delta
  177. static uint32_t loc_millisec_delta (lglw_int_t *lglw) {
  178. struct timeval c; gettimeofday(&c, 0);
  179. const struct timeval*s = &lglw->timer.tv_start;
  180. return (uint32_t) ( (c.tv_sec-s->tv_sec)*1000 + (c.tv_usec-s->tv_usec)/1000 );
  181. }
  182. // TODO: remove and/or improve debug logging for a debug build
  183. // ---------------------------------------------------------------------------- lglw_log
  184. static FILE *logfile;
  185. void lglw_log(const char *logData, ...) {
  186. static char buf[16*1024];
  187. va_list va;
  188. va_start(va, logData);
  189. vsprintf(buf, logData, va);
  190. va_end(va);
  191. printf(buf);
  192. //fprintf(logfile, logData);
  193. fputs(buf, logfile);
  194. fflush(logfile);
  195. // printf(logData);
  196. }
  197. // TODO: remove, or maybe not in some specific use cases
  198. // ---------------------------------------------------------------------------- xerror_log
  199. static int xerror_handler(Display *display, XErrorEvent *error) {
  200. char error_text[1024];
  201. XGetErrorText(display, error->error_code, error_text, 1024);
  202. Dlog("XERROR (%d): %s, %d, %d\n", error->error_code, error_text, error->request_code, error->minor_code);
  203. return 0;
  204. }
  205. // ---------------------------------------------------------------------------- lglw_init
  206. lglw_t lglw_init(int32_t _w, int32_t _h) {
  207. lglw_int_t *lglw = malloc(sizeof(lglw_int_t));
  208. Dprintf("xxx lglw_init: sizeof(uint32_t)=%lu sizeof(long)=%lu sizeof(void*)=%lu\n", sizeof(uint32_t), sizeof(long), sizeof(void*));
  209. // TODO: remove/improve
  210. logfile = fopen("/tmp/lglw_log.txt", "w");
  211. XSetErrorHandler(xerror_handler);
  212. XInitThreads(); // fix GL crash, see <https://forum.juce.com/t/linux-vst-opengl-crash-because-xinitthreads-not-called/22821>
  213. loc_millisec_init(lglw);
  214. if(NULL != lglw)
  215. {
  216. memset(lglw, 0, sizeof(lglw_int_t));
  217. Dlog("lglw:lglw_init: 1\n");
  218. if(_w <= 16)
  219. _w = LGLW_DEFAULT_HIDDEN_W;
  220. if(_h <= 16)
  221. _h = LGLW_DEFAULT_HIDDEN_H;
  222. Dlog("lglw:lglw_init: 2\n");
  223. if(!loc_create_hidden_window(lglw, _w, _h))
  224. {
  225. free(lglw);
  226. lglw = NULL;
  227. }
  228. Dlog("lglw:lglw_init: 3\n");
  229. }
  230. Dlog("lglw:lglw_init: EXIT\n");
  231. return lglw;
  232. }
  233. // ---------------------------------------------------------------------------- lglw_exit
  234. void lglw_exit(lglw_t _lglw) {
  235. LGLW(_lglw);
  236. if(NULL != lglw)
  237. {
  238. Dlog("lglw:lglw_exit: 1\n");
  239. loc_destroy_hidden_window(lglw);
  240. Dlog("lglw:lglw_exit: 2\n");
  241. fclose(logfile);
  242. free(lglw);
  243. }
  244. }
  245. // ---------------------------------------------------------------------------- lglw_userdata_set
  246. void lglw_userdata_set(lglw_t _lglw, void *_userData) {
  247. LGLW(_lglw);
  248. if(NULL != lglw)
  249. {
  250. Dlog_vvv("lglw:lglw_userdata_set: ptr=%p\n", _userData);
  251. lglw->user_data = _userData;
  252. }
  253. }
  254. // ---------------------------------------------------------------------------- lglw_userdata_get
  255. void *lglw_userdata_get(lglw_t _lglw) {
  256. LGLW(_lglw);
  257. if(NULL != lglw)
  258. {
  259. Dlog_vvv("lglw:lglw_userdata_get: return ptr=%p\n", lglw->user_data);
  260. return lglw->user_data;
  261. }
  262. return NULL;
  263. }
  264. // ---------------------------------------------------------------------------- loc_create_hidden_window
  265. static lglw_bool_t loc_create_hidden_window(lglw_int_t *lglw, int32_t _w, int32_t _h) {
  266. // TODO: compare to 'WindowClass' from Windows implementation
  267. Dlog_v("lglw:loc_create_hidden_window: 1\n");
  268. XSetWindowAttributes swa;
  269. int attrib[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, None };
  270. int screen;
  271. Dlog_v("lglw:loc_create_hidden_window: 2\n");
  272. lglw->xdsp = XOpenDisplay(NULL);
  273. screen = DefaultScreen(lglw->xdsp);
  274. Dlog_v("lglw:loc_create_hidden_window: 3\n");
  275. lglw->vi = glXChooseVisual(lglw->xdsp, screen, attrib);
  276. Dlog_v("lglw:loc_create_hidden_window: 4\n");
  277. if(NULL == lglw->vi)
  278. {
  279. Dlog("[---] lglw: failed to find GLX Visual for hidden window\n");
  280. return LGLW_FALSE;
  281. }
  282. Dlog_v("lglw:loc_create_hidden_window: 5\n");
  283. lglw->ctx = glXCreateContext(lglw->xdsp, lglw->vi, None, True);
  284. if(NULL == lglw->ctx)
  285. {
  286. Dlog("lglw: FAILED to create context!!!\n");
  287. }
  288. Dlog_v("lglw:loc_create_hidden_window: 6\n");
  289. if(NULL == lglw->ctx)
  290. {
  291. Dlog("[---] lglw: failed to create GLX Context for hidden window\n");
  292. return LGLW_FALSE;
  293. }
  294. Dlog_v("lglw:loc_create_hidden_window: 7\n");
  295. lglw->cmap = XCreateColormap(lglw->xdsp, RootWindow(lglw->xdsp, lglw->vi->screen),
  296. lglw->vi->visual, AllocNone);
  297. Dlog_v("lglw:loc_create_hidden_window: 8\n");
  298. swa.border_pixel = 0;
  299. swa.colormap = lglw->cmap;
  300. lglw->hidden.xwnd = XCreateWindow(lglw->xdsp, DefaultRootWindow(lglw->xdsp),
  301. 0, 0, LGLW_DEFAULT_HIDDEN_W, LGLW_DEFAULT_HIDDEN_H, 0, CopyFromParent, InputOutput,
  302. lglw->vi->visual, CWBorderPixel | CWColormap, &swa);
  303. Dlog_v("lglw:loc_create_hidden_window: 9\n");
  304. XSetStandardProperties(lglw->xdsp, lglw->hidden.xwnd, "LGLW_hidden", "LGLW_hidden", None, NULL, 0, NULL);
  305. XSync(lglw->xdsp, False);
  306. Dlog_v("lglw:loc_create_hidden_window: EXIT\n");
  307. lglw->hidden.size.x = _w;
  308. lglw->hidden.size.y = _h;
  309. return LGLW_TRUE;
  310. }
  311. // ---------------------------------------------------------------------------- loc_destroy_hidden_window
  312. static void loc_destroy_hidden_window(lglw_int_t *lglw) {
  313. Dlog_v("lglw:loc_destroy_hidden_window: 1\n");
  314. if(NULL != lglw->xdsp && NULL != lglw->ctx)
  315. {
  316. glXMakeCurrent(lglw->xdsp, None, NULL);
  317. glXDestroyContext(lglw->xdsp, lglw->ctx);
  318. }
  319. Dlog_v("lglw:loc_destroy_hidden_window: 2\n");
  320. if(NULL != lglw->xdsp && 0 != lglw->hidden.xwnd) XDestroyWindow(lglw->xdsp, lglw->hidden.xwnd);
  321. Dlog_v("lglw:loc_destroy_hidden_window: 3\n");
  322. if(NULL != lglw->xdsp && 0 != lglw->cmap) XFreeColormap(lglw->xdsp, lglw->cmap);
  323. Dlog_v("lglw:loc_destroy_hidden_window: 4\n");
  324. if(NULL != lglw->vi) XFree(lglw->vi);
  325. Dlog_v("lglw:loc_destroy_hidden_window: 5\n");
  326. XSync(lglw->xdsp, False);
  327. if(NULL != lglw->xdsp) XCloseDisplay(lglw->xdsp);
  328. }
  329. // ---------------------------------------------------------------------------- loc_setEventProc
  330. // https://www.kvraudio.com/forum/viewtopic.php?t=387924
  331. // https://github.com/Ardour/ardour/blob/master/gtk2_ardour/linux_vst_gui_support.cc
  332. // https://discourse.ardour.org/t/overtonedsp-plugins/90115/22
  333. // https://github.com/amsynth/amsynth/blob/4a87798e650c6d71d70274a961c9b8d98fc6da7e/src/amsynth_vst.cpp
  334. // https://github.com/rsenn/eXT2/blob/7f00a09561ded8175ffed2f4912dad74e466a1c7/vstplugins/vstgui/vstgui.cpp
  335. // https://github.com/COx2/DistortionFilter/blob/c6a34fb56b503a6e95bf0975e00f438bbf4ff52a/juce/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  336. // Very simple function to test _XEventProc is properly called
  337. static void loc_eventProc(XEvent *xev, lglw_int_t *lglw) {
  338. Dlog_vvv("lglw:loc_eventProc: type=%d serial=%lu send_event=%d lglw=%p\n", xev->xany.type, xev->xany.serial, xev->xany.send_event, lglw);
  339. loc_process_timer(lglw);
  340. if(NULL != lglw)
  341. {
  342. lglw_bool_t eventHandled = LGLW_FALSE;
  343. switch(xev->type)
  344. {
  345. default:
  346. Dlog("lglw:loc_eventProc: unhandled X11 event type=%d\n", xev->type);
  347. eventHandled = LGLW_FALSE;
  348. break;
  349. case Expose:
  350. Dlog_vv("lglw:loc_eventProc: xev Expose\n");
  351. loc_handle_queued_mouse_warp(lglw);
  352. eventHandled = LGLW_FALSE;
  353. if(NULL != lglw->redraw.cbk)
  354. {
  355. lglw->redraw.cbk(lglw);
  356. eventHandled = LGLW_TRUE;
  357. }
  358. break;
  359. // TODO: Should FocusIn/Out be treated like WM_CAPTURECHANGED and reset the grab state?
  360. case FocusIn:
  361. Dlog_v("lglw:loc_eventProc: xev FocusIn\n");
  362. eventHandled = LGLW_FALSE;
  363. break;
  364. case FocusOut:
  365. Dlog_v("lglw:loc_eventProc: xev FocusOut\n");
  366. eventHandled = LGLW_FALSE;
  367. break;
  368. case EnterNotify:
  369. // Dlog_v("lglw:loc_eventProc: xev XEnterWindowEvent\n");
  370. ; // empty statement
  371. XEnterWindowEvent *wenter = (XEnterWindowEvent*)xev;
  372. Dlog_v("lglw:loc_eventProc: xev EnterNotify: mode:%i, detail:%i, state:%d\n", wenter->mode, wenter->detail, wenter->state);
  373. lglw->mouse.p.x = wenter->x;
  374. lglw->mouse.p.y = wenter->y;
  375. loc_handle_mousemotion(lglw);
  376. // EnterNotify messages can be pseudo-motion events (NotifyGrab, NotifyUngrab)
  377. // when buttons are pressed, which would trigger false focus changes
  378. // so, the callback is only sent when a normal entry happens
  379. if (wenter->mode == NotifyNormal)
  380. {
  381. loc_handle_mouseenter(lglw);
  382. }
  383. eventHandled = LGLW_TRUE;
  384. break;
  385. case LeaveNotify:
  386. // Dlog_v("lglw:loc_eventProc: xev XLeaveWindowEvent\n");
  387. ; // empty statement
  388. XLeaveWindowEvent *wexit = (XLeaveWindowEvent*)xev;
  389. Dlog_v("lglw:loc_eventProc: xev LeaveNotify: mode:%i, detail:%i, state:%d\n", wexit->mode, wexit->detail, wexit->state);
  390. // LeaveNotify messages can be pseudo-motion events (NotifyGrab, NotifyUngrab)
  391. // when buttons are pressed, which would trigger false focus changes
  392. // so, the callback is only sent when a normal entry happens
  393. if (wexit->mode == NotifyNormal)
  394. {
  395. loc_handle_mouseleave(lglw);
  396. }
  397. eventHandled = LGLW_TRUE;
  398. break;
  399. case MotionNotify:
  400. Dlog_vvv("lglw:loc_eventProc: xev MotionNotify\n");
  401. ; // empty statement
  402. XMotionEvent *motion = (XMotionEvent*)xev;
  403. if(LGLW_MOUSE_GRAB_WARP == lglw->mouse.grab.mode)
  404. {
  405. lglw->mouse.grab.b_queue_warp = LGLW_TRUE;
  406. lglw->mouse.p.x += (motion->x - lglw->mouse.grab.last_p.x);
  407. lglw->mouse.p.y += (motion->y - lglw->mouse.grab.last_p.y);
  408. lglw->mouse.grab.last_p.x = motion->x;
  409. lglw->mouse.grab.last_p.y = motion->y;
  410. }
  411. else
  412. {
  413. lglw->mouse.p.x = motion->x;
  414. lglw->mouse.p.y = motion->y;
  415. }
  416. loc_handle_mousemotion(lglw);
  417. eventHandled = LGLW_TRUE;
  418. break;
  419. case KeyPress:
  420. Dlog("lglw:loc_eventProc: xev KeyPress\n");
  421. XKeyPressedEvent *keyPress = (XKeyPressedEvent*)xev;
  422. eventHandled = LGLW_FALSE;
  423. KeySym xkp = XLookupKeysym(keyPress, 0);
  424. switch(xkp)
  425. {
  426. default:
  427. Dlog("lglw:loc_eventProc: xev KeyPress: %x or %lu\n", keyPress->keycode, xkp);
  428. if(0u != (lglw->keyboard.kmod_state & LGLW_KMOD_SHIFT))
  429. {
  430. KeySym xkpl;
  431. KeySym xkpu;
  432. XConvertCase(xkp, &xkpl, &xkpu);
  433. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, xkpu);
  434. }
  435. else
  436. {
  437. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, xkp);
  438. }
  439. break;
  440. case NoSymbol:
  441. Dlog("lglw:loc_eventProc: xev UNKNOWN KeyPress: %x\n", keyPress->keycode);
  442. break;
  443. case XK_Left:
  444. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LEFT);
  445. break;
  446. case XK_Right:
  447. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RIGHT);
  448. break;
  449. case XK_Up:
  450. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_UP);
  451. break;
  452. case XK_Down:
  453. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_DOWN);
  454. break;
  455. case XK_Insert:
  456. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_INSERT);
  457. break;
  458. case XK_Delete:
  459. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_DELETE);
  460. break;
  461. case XK_Home:
  462. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_HOME);
  463. break;
  464. case XK_End:
  465. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_END);
  466. break;
  467. case XK_Prior:
  468. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_PAGEUP);
  469. break;
  470. case XK_Next:
  471. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_PAGEDOWN);
  472. break;
  473. case XK_F1:
  474. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F1);
  475. break;
  476. case XK_F2:
  477. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F2);
  478. break;
  479. case XK_F3:
  480. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F3);
  481. break;
  482. case XK_F4:
  483. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F4);
  484. break;
  485. case XK_F5:
  486. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F5);
  487. break;
  488. case XK_F6:
  489. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F6);
  490. break;
  491. case XK_F7:
  492. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F7);
  493. break;
  494. case XK_F8:
  495. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F8);
  496. break;
  497. case XK_F9:
  498. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F9);
  499. break;
  500. case XK_F10:
  501. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F10);
  502. break;
  503. case XK_F11:
  504. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F11);
  505. break;
  506. case XK_F12:
  507. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F12);
  508. break;
  509. case XK_BackSpace:
  510. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_BACKSPACE);
  511. break;
  512. case XK_Tab:
  513. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_TAB);
  514. break;
  515. case XK_Return:
  516. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RETURN);
  517. break;
  518. case XK_Escape:
  519. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_ESCAPE);
  520. break;
  521. case XK_Shift_L:
  522. lglw->keyboard.kmod_state |= LGLW_KMOD_LSHIFT;
  523. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LSHIFT);
  524. eventHandled = LGLW_FALSE;
  525. break;
  526. case XK_Shift_R:
  527. lglw->keyboard.kmod_state |= LGLW_KMOD_RSHIFT;
  528. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RSHIFT);
  529. eventHandled = LGLW_FALSE;
  530. break;
  531. case XK_Control_L:
  532. lglw->keyboard.kmod_state |= LGLW_KMOD_LCTRL;
  533. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_KMOD_LCTRL);
  534. eventHandled = LGLW_FALSE;
  535. break;
  536. case XK_Control_R:
  537. lglw->keyboard.kmod_state |= LGLW_KMOD_RCTRL;
  538. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_KMOD_RCTRL);
  539. eventHandled = LGLW_FALSE;
  540. break;
  541. }
  542. break;
  543. case KeyRelease:
  544. Dlog("lglw:loc_eventProc: xev KeyRelease\n");
  545. XKeyReleasedEvent *keyRelease = (XKeyReleasedEvent*)xev;
  546. eventHandled = LGLW_FALSE;
  547. KeySym xkr = XLookupKeysym(keyRelease, 0);
  548. switch(xkr)
  549. {
  550. default:
  551. Dlog("lglw:loc_eventProc: xev KeyRelease: %x or %lu\n", keyRelease->keycode, xkr);
  552. if(0u != (lglw->keyboard.kmod_state & LGLW_KMOD_SHIFT))
  553. {
  554. KeySym xkrl;
  555. KeySym xkru;
  556. XConvertCase(xkr, &xkrl, &xkru);
  557. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, xkru);
  558. }
  559. else
  560. {
  561. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, xkr);
  562. }
  563. break;
  564. case NoSymbol:
  565. Dlog("lglw:loc_eventProc: xev UNKNOWN KeyRelease: %x\n", keyRelease->keycode);
  566. break;
  567. case XK_Left:
  568. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LEFT);
  569. break;
  570. case XK_Right:
  571. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RIGHT);
  572. break;
  573. case XK_Up:
  574. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_UP);
  575. break;
  576. case XK_Down:
  577. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_DOWN);
  578. break;
  579. case XK_Insert:
  580. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_INSERT);
  581. break;
  582. case XK_Delete:
  583. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_DELETE);
  584. break;
  585. case XK_Home:
  586. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_HOME);
  587. break;
  588. case XK_End:
  589. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_END);
  590. break;
  591. case XK_Prior:
  592. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_PAGEUP);
  593. break;
  594. case XK_Next:
  595. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_PAGEDOWN);
  596. break;
  597. case XK_F1:
  598. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F1);
  599. break;
  600. case XK_F2:
  601. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F2);
  602. break;
  603. case XK_F3:
  604. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F3);
  605. break;
  606. case XK_F4:
  607. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F4);
  608. break;
  609. case XK_F5:
  610. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F5);
  611. break;
  612. case XK_F6:
  613. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F6);
  614. break;
  615. case XK_F7:
  616. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F7);
  617. break;
  618. case XK_F8:
  619. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F8);
  620. break;
  621. case XK_F9:
  622. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F9);
  623. break;
  624. case XK_F10:
  625. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F10);
  626. break;
  627. case XK_F11:
  628. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F11);
  629. break;
  630. case XK_F12:
  631. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F12);
  632. break;
  633. case XK_BackSpace:
  634. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_BACKSPACE);
  635. break;
  636. case XK_Tab:
  637. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_TAB);
  638. break;
  639. case XK_Return:
  640. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RETURN);
  641. break;
  642. case XK_Escape:
  643. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_ESCAPE);
  644. break;
  645. case XK_Shift_L:
  646. lglw->keyboard.kmod_state &= ~LGLW_KMOD_LSHIFT;
  647. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LSHIFT);
  648. eventHandled = LGLW_FALSE;
  649. break;
  650. case XK_Shift_R:
  651. lglw->keyboard.kmod_state &= ~LGLW_KMOD_RSHIFT;
  652. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RSHIFT);
  653. eventHandled = LGLW_FALSE;
  654. break;
  655. case XK_Control_L:
  656. lglw->keyboard.kmod_state &= ~LGLW_KMOD_LCTRL;
  657. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LCTRL);
  658. eventHandled = LGLW_FALSE;
  659. break;
  660. case XK_Control_R:
  661. lglw->keyboard.kmod_state &= ~LGLW_KMOD_RCTRL;
  662. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RCTRL);
  663. eventHandled = LGLW_FALSE;
  664. break;
  665. }
  666. break;
  667. case ButtonPress:
  668. Dlog("lglw:loc_eventProc: xev ButtonPress\n");
  669. XButtonPressedEvent *btnPress = (XButtonPressedEvent*)xev;
  670. lglw->mouse.p.x = btnPress->x;
  671. lglw->mouse.p.y = btnPress->y;
  672. if(0u == (lglw->focus.state & LGLW_FOCUS_MOUSE))
  673. {
  674. loc_handle_mouseenter(lglw);
  675. }
  676. switch(btnPress->button)
  677. {
  678. default:
  679. Dlog("lglw:loc_eventProc: xev ButtonPress unhandled button: %i\n", btnPress->button);
  680. eventHandled = LGLW_FALSE;
  681. break;
  682. case Button1:
  683. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_LBUTTON);
  684. eventHandled = LGLW_TRUE;
  685. break;
  686. case Button2:
  687. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_MBUTTON);
  688. eventHandled = LGLW_TRUE;
  689. break;
  690. case Button3:
  691. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_RBUTTON);
  692. eventHandled = LGLW_TRUE;
  693. break;
  694. case Button4:
  695. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_WHEELUP);
  696. eventHandled = LGLW_TRUE;
  697. break;
  698. case Button5:
  699. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_WHEELDOWN);
  700. eventHandled = LGLW_TRUE;
  701. break;
  702. }
  703. break;
  704. case ButtonRelease:
  705. Dlog("lglw:loc_eventProc: xev ButtonRelease\n");
  706. XButtonReleasedEvent *btnRelease = (XButtonReleasedEvent*)xev;
  707. lglw->mouse.p.x = btnRelease->x;
  708. lglw->mouse.p.y = btnRelease->y;
  709. switch(btnRelease->button)
  710. {
  711. default:
  712. Dlog("lglw:loc_eventProc: xev ButtonRelease unhandled button: %i\n", btnRelease->button);
  713. eventHandled = LGLW_FALSE;
  714. break;
  715. case Button1:
  716. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_LBUTTON);
  717. eventHandled = LGLW_TRUE;
  718. break;
  719. case Button2:
  720. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_MBUTTON);
  721. eventHandled = LGLW_TRUE;
  722. break;
  723. case Button3:
  724. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_RBUTTON);
  725. eventHandled = LGLW_TRUE;
  726. break;
  727. case Button4:
  728. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_WHEELUP);
  729. eventHandled = LGLW_TRUE;
  730. break;
  731. case Button5:
  732. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_WHEELDOWN);
  733. eventHandled = LGLW_TRUE;
  734. break;
  735. }
  736. break;
  737. case SelectionClear:
  738. Dlog("lglw:loc_eventProc: xev SelectionClear\n");
  739. lglw->clipboard.numChars = 0;
  740. free(lglw->clipboard.data);
  741. eventHandled = LGLW_TRUE;
  742. break;
  743. case SelectionRequest:
  744. Dlog("lglw:loc_eventProc: xev SelectionRequest\n");
  745. XSelectionRequestEvent *cbReq = (XSelectionRequestEvent*)xev;
  746. XSelectionEvent cbRes;
  747. Atom utf8 = XInternAtom(lglw->xdsp, "UTF8_STRING", False);
  748. cbRes.type = SelectionNotify;
  749. cbRes.requestor = cbReq->requestor;
  750. cbRes.selection = cbReq->selection;
  751. cbRes.target = cbReq->target;
  752. cbRes.time = cbReq->time;
  753. if(cbReq->target == utf8)
  754. {
  755. XChangeProperty(lglw->xdsp, cbReq->requestor, cbReq->property, utf8, 8/*format*/, PropModeReplace,
  756. (unsigned char *)lglw->clipboard.data, lglw->clipboard.numChars);
  757. cbRes.property = cbReq->property;
  758. }
  759. else
  760. {
  761. cbRes.property = None;
  762. }
  763. XSendEvent(lglw->xdsp, cbReq->requestor, True, NoEventMask, (XEvent *)&cbRes);
  764. eventHandled = LGLW_TRUE;
  765. break;
  766. }
  767. if(LGLW_FALSE == eventHandled)
  768. {
  769. if(0 == lglw->parent_xwnd)
  770. {
  771. Dlog("lglw:loc_eventProc: no parent window to send events to\n");
  772. XSendEvent(lglw->xdsp, InputFocus, True/*propgate*/, NoEventMask, xev);
  773. }
  774. else
  775. {
  776. Dlog("lglw:loc_eventProc: sending event %i to parent\n", xev->type);
  777. XSendEvent(lglw->xdsp, lglw->parent_xwnd, True/*propgate*/, NoEventMask, xev);
  778. }
  779. }
  780. }
  781. }
  782. static void loc_XEventProc(void *_xevent) {
  783. XEvent *xev = (XEvent*)_xevent;
  784. Dlog_vvv("lglw:loc_XEventProc: ENTER\n");
  785. // Dlog_vvv("lglw: XEventProc, xev=%p\n", xev);
  786. if(NULL != xev)
  787. {
  788. LGLW(loc_getProperty(xev->xany.display, xev->xany.window, "_lglw")); // get instance pointer
  789. Dlog_vvv("lglw:loc_XEventProc: xev=%p lglw=%p\n", xev, lglw);
  790. loc_eventProc(xev, lglw);
  791. }
  792. Dlog_vvv("lglw:loc_XEventProc: LEAVE\n");
  793. }
  794. static void loc_setProperty(Display *_display, Window _window, const char *_name, void *_value) {
  795. size_t data = (size_t)_value;
  796. long temp[2];
  797. // Split the 64 bit pointer into a little-endian long array
  798. temp[0] = (long)(data & 0xffffffffUL);
  799. temp[1] = (long)(data >> 32L);
  800. Dlog_v("lglw:loc_setProperty: name=\"%s\" value=%p temp[0]=%08x temp[1]=%08x\n", _name, _value, (uint32_t)temp[0], (uint32_t)temp[1]);
  801. Atom atom = XInternAtom(_display, _name, False/*only_if_exists*/);
  802. // (note) what's quite weird here is that we're writing an array of 32bit values, yet the element format must be 64bit (long)
  803. XChangeProperty(_display, _window,
  804. atom/*property*/,
  805. atom/*type*/,
  806. 32/*format*/,
  807. PropModeReplace/*mode*/,
  808. (unsigned char*)temp/*data*/,
  809. 2/*nelements*/
  810. );
  811. }
  812. static void *loc_getProperty(Display *_display, Window _window, const char *_name) {
  813. int userSize;
  814. unsigned long bytes;
  815. unsigned long userCount;
  816. unsigned char *data;
  817. Atom userType;
  818. Atom atom = XInternAtom(_display, _name, False);
  819. // (note) 64bit properties need to be read with two XGetWindowProperty() calls.
  820. // When using just one call and setting the 'length' to 2, the upper 32bit (second array element) will be 0xFFFFffff.
  821. XGetWindowProperty(_display,
  822. _window,
  823. atom,
  824. 0/*offset*/,
  825. 1/*length*/,
  826. False/*delete*/,
  827. AnyPropertyType,
  828. &userType/*actual_type_return*/,
  829. &userSize/*actual_format_return*/,
  830. &userCount/*nitems_return*/,
  831. &bytes/*bytes_after_return / partial reads*/,
  832. &data);
  833. union {
  834. uint32_t ui[2];
  835. void *any;
  836. } uptr;
  837. uptr.any = 0;
  838. Dlog_vvv("lglw:loc_getProperty: LOWER userSize=%d userCount=%lu bytes=%lu data=%p\n", userSize, userCount, bytes, data);
  839. if(NULL != data)
  840. {
  841. if(userCount >= 1)
  842. {
  843. // lower 32-bit
  844. uptr.ui[0] = *(long*)data;
  845. uptr.ui[1] = 0;
  846. Dlog_vvv("lglw:loc_getProperty: lower=0x%08x\n", uptr.ui[0]);
  847. XFree(data);
  848. XGetWindowProperty(_display,
  849. _window,
  850. atom,
  851. 1/*offset*/,
  852. 1/*length*/,
  853. False/*delete*/,
  854. AnyPropertyType,
  855. &userType/*actual_type_return*/,
  856. &userSize/*actual_format_return*/,
  857. &userCount/*nitems_return*/,
  858. &bytes/*bytes_after_return / partial reads*/,
  859. &data);
  860. Dlog_vvv("lglw:loc_getProperty: UPPER userSize=%d userCount=%lu bytes=%lu data=%p\n", userSize, userCount, bytes, data);
  861. if(NULL != data)
  862. {
  863. // upper 32-bit
  864. uptr.ui[1] = *(long*)data;
  865. Dlog_vvv("lglw:loc_getProperty: upper=0x%08x\n", uptr.ui[1]);
  866. XFree(data);
  867. }
  868. }
  869. }
  870. Dlog_vvv("lglw:loc_getProperty: return value=%p\n", uptr.any);
  871. return uptr.any;
  872. }
  873. #ifdef ARCH_X64
  874. #if 0
  875. // Pulled from the Renoise 64-bit callback example
  876. // Unsure what data was supposed to be, but swapping it to a function name did not work
  877. // This does nothing, no event proc found
  878. static void loc_setEventProc (Display *display, Window window) {
  879. size_t data = (size_t)loc_eventProc;
  880. long temp[2];
  881. Dlog("lglw: setEventProc (2*32bit). window=%lu loc_eventProc=%p\n", window, &loc_eventProc);
  882. // Split the 64 bit pointer into a little-endian unsigned int array
  883. temp[0] = (uint32_t)(data & 0xffffffffUL);
  884. temp[1] = (uint32_t)(data >> 32L);
  885. Atom atom = XInternAtom(display, "_XEventProc", False);
  886. XChangeProperty(display, window,
  887. atom/*property*/,
  888. atom/*type*/,
  889. 32/*format*/,
  890. PropModeReplace/*mode*/,
  891. (unsigned char*)temp/*data*/,
  892. 2/*nelements*/
  893. );
  894. }
  895. #else
  896. // GPL code pulled from the amsynth example <https://github.com/amsynth/amsynth/blob/4a87798e650c6d71d70274a961c9b8d98fc6da7e/src/amsynth_vst.cpp>
  897. // Simply swapped out the function names, crashes Ardour in the same was as the others
  898. static void loc_setEventProc (Display *display, Window window) {
  899. //
  900. // JUCE calls XGetWindowProperty with long_length = 1 which means it only fetches the lower 32 bits of the address.
  901. // Therefore we need to ensure we return an address in the lower 32-bits of address space.
  902. //
  903. // based on mach_override
  904. static const unsigned char kJumpInstructions[] = {
  905. 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
  906. 0x00, 0x00, 0x00, 0x00,
  907. 0x00, 0x00, 0x00, 0x00
  908. };
  909. static const int kJumpAddress = 6;
  910. static char *ptr = 0;
  911. if (!ptr) {
  912. ptr = (char *)mmap(0,
  913. getpagesize()/*PAGE_SIZE*/,
  914. PROT_READ | PROT_WRITE | PROT_EXEC,
  915. MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT,
  916. 0, 0);
  917. if (ptr == MAP_FAILED) {
  918. perror("mmap");
  919. ptr = 0;
  920. return;
  921. } else {
  922. memcpy(ptr, kJumpInstructions, sizeof(kJumpInstructions));
  923. *((uint64_t *)(ptr + kJumpAddress)) = (uint64_t)(&loc_XEventProc);
  924. msync(ptr, sizeof(kJumpInstructions), MS_INVALIDATE);
  925. Dlog("lglw: 64bit trampoline installed\n");
  926. }
  927. }
  928. long temp[2] = {(uint32_t)(((size_t)ptr)&0xFFFFfffful), 0};
  929. Atom atom = XInternAtom(display, "_XEventProc", False);
  930. XChangeProperty(display, window,
  931. atom/*property*/,
  932. atom/*type*/,
  933. 32/*format*/,
  934. PropModeReplace/*mode*/,
  935. (unsigned char *)temp/*data*/,
  936. 2/*nelements*/
  937. );
  938. }
  939. #endif
  940. #else
  941. // Pulled from the eXT2 example
  942. static void loc_setEventProc (Display *display, Window window) {
  943. void* data = (void*)&loc_XEventProc; // swapped the function name here
  944. // (note) 32-bit only
  945. Atom atom = XInternAtom(display, "_XEventProc", False);
  946. XChangeProperty(display, window,
  947. atom/*property*/,
  948. atom/*type*/,
  949. 32/*format*/,
  950. PropModeReplace/*mode*/,
  951. (unsigned char*)&data/*data*/,
  952. 1/*nelements*/
  953. );
  954. }
  955. #endif // ARCH_X64
  956. // ---------------------------------------------------------------------------- lglw_window_open
  957. lglw_bool_t lglw_window_open (lglw_t _lglw, void *_parentHWNDOrNull, int32_t _x, int32_t _y, int32_t _w, int32_t _h) {
  958. lglw_bool_t r = LGLW_FALSE;
  959. LGLW(_lglw);
  960. if(NULL != lglw)
  961. {
  962. Dlog_v("lglw:lglw_window_open: 1, %p, %i p=(%d; %d) s=(%d; %d)\n", (Window)_parentHWNDOrNull, (Window)_parentHWNDOrNull, _x, _y, _w, _h);
  963. lglw->parent_xwnd = (0 == _parentHWNDOrNull) ? DefaultRootWindow(lglw->xdsp) : (Window)_parentHWNDOrNull;
  964. Dlog_v("lglw:lglw_window_open: 2 lglw=%p\n", lglw);
  965. if(_w <= 16)
  966. _w = lglw->hidden.size.x;
  967. Dlog_v("lglw:lglw_window_open: 3\n");
  968. if(_h <= 16)
  969. _h = lglw->hidden.size.y;
  970. // TODO: compare to 'WindowClass' from Windows implementation
  971. Dlog_v("lglw:lglw_window_open: 4\n");
  972. XSetWindowAttributes swa;
  973. XSync(lglw->xdsp, False);
  974. Dlog_v("lglw:lglw_window_open: 5\n");
  975. swa.border_pixel = 0;
  976. swa.colormap = lglw->cmap;
  977. swa.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonMotionMask | ExposureMask | FocusChangeMask; // NoEventMask to bubble-up to parent
  978. lglw->win.xwnd = XCreateWindow(lglw->xdsp/*display*/,
  979. DefaultRootWindow(lglw->xdsp)/*parent. see Cameron's comment below.*/,
  980. 0/*x*/,
  981. 0/*y*/,
  982. _w/*width*/,
  983. _h/*height*/,
  984. 0/*border_width*/,
  985. CopyFromParent/*depth*/,
  986. InputOutput/*class*/,
  987. lglw->vi->visual,
  988. CWBorderPixel | CWColormap | CWEventMask/*value_mask*/,
  989. &swa/*attributes*/
  990. );
  991. Dlog_v("lglw:lglw_window_open: 6\n");
  992. XSetStandardProperties(lglw->xdsp/*display*/,
  993. lglw->win.xwnd/*window*/,
  994. "LGLW"/*window_name*/,
  995. "LGLW"/*icon_name*/,
  996. None/*icon_pixmap*/,
  997. NULL/*argv*/,
  998. 0/*argc*/,
  999. NULL/*XSizeHints*/
  1000. );
  1001. Dlog_v("lglw:lglw_window_open: 7\n");
  1002. #ifdef USE_XEVENTPROC
  1003. loc_setEventProc(lglw->xdsp, lglw->win.xwnd);
  1004. #else
  1005. {
  1006. void *nowarn = &loc_setEventProc;
  1007. (void)nowarn;
  1008. }
  1009. #endif
  1010. loc_setProperty(lglw->xdsp, lglw->win.xwnd, "_lglw", (void*)lglw); // set instance pointer
  1011. if(0 != _parentHWNDOrNull)
  1012. {
  1013. #ifdef USE_XEVENTPROC
  1014. loc_setEventProc(lglw->xdsp, lglw->parent_xwnd);
  1015. #endif // USE_XEVENTPROC
  1016. loc_setProperty(lglw->xdsp, lglw->parent_xwnd, "_lglw", (void*)lglw); // set instance pointer
  1017. }
  1018. // Some hosts only check and store the callback when the Window is reparented
  1019. // Since creating the Window with a Parent may or may not do that, but the callback is not set,
  1020. // ... it's created as a root window, the callback is set, and then it's reparented
  1021. if (0 != _parentHWNDOrNull)
  1022. {
  1023. Dlog_v("lglw:lglw_window_open: 8\n");
  1024. XReparentWindow(lglw->xdsp, lglw->win.xwnd, lglw->parent_xwnd, 0, 0);
  1025. }
  1026. lglw->win.b_owner = LGLW_TRUE;
  1027. Dlog_v("lglw:lglw_window_open: 9\n");
  1028. if(lglw->win.b_owner)
  1029. {
  1030. // // XMapRaised(lglw->xdsp, lglw->win.xwnd);
  1031. XMapWindow(lglw->xdsp, lglw->win.xwnd);
  1032. }
  1033. XSync(lglw->xdsp, False);
  1034. lglw->win.mapped = LGLW_TRUE;
  1035. Dlog_v("lglw:lglw_window_open: 10\n");
  1036. lglw->win.size.x = _w;
  1037. lglw->win.size.y = _h;
  1038. Dlog_v("lglw:lglw_window_open: 11\n");
  1039. loc_enable_dropfiles(lglw, (NULL != lglw->dropfiles.cbk));
  1040. Dlog_v("lglw:lglw_window_open: EXIT\n");
  1041. r = LGLW_TRUE;
  1042. }
  1043. return r;
  1044. }
  1045. // ---------------------------------------------------------------------------- lglw_window_resize
  1046. lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) {
  1047. lglw_bool_t r = LGLW_FALSE;
  1048. LGLW(_lglw);
  1049. if(NULL != lglw)
  1050. {
  1051. if(0 != lglw->win.xwnd)
  1052. {
  1053. Dlog_v("lglw:lglw_window_resize: 1\n");
  1054. r = LGLW_TRUE;
  1055. Dlog_v("lglw:lglw_window_resize: 2\n");
  1056. XResizeWindow(lglw->xdsp, lglw->win.xwnd, _w, _h);
  1057. XRaiseWindow(lglw->xdsp, lglw->win.xwnd);
  1058. Dlog_v("lglw:lglw_window_resize: 3\n");
  1059. int deltaW = _w - lglw->win.size.x;
  1060. int deltaH = _h - lglw->win.size.y;
  1061. Dlog_v("lglw:lglw_window_resize: 4\n");
  1062. lglw->win.size.x = _w;
  1063. lglw->win.size.y = _h;
  1064. Dlog_v("lglw:lglw_window_resize: 5\n");
  1065. Window root, parent, *children = NULL;
  1066. unsigned int num_children;
  1067. Dlog_v("lglw:lglw_window_resize: 6\n");
  1068. if(!XQueryTree(lglw->xdsp, lglw->win.xwnd, &root, &parent, &children, &num_children))
  1069. return r;
  1070. Dlog_v("lglw:lglw_window_resize: 7\n");
  1071. if(children)
  1072. XFree((char *)children);
  1073. Dlog_v("lglw:lglw_window_resize: 8\n");
  1074. // Resize parent window (if any)
  1075. if(0 != parent)
  1076. {
  1077. Dlog_v("lglw:lglw_window_resize: 8.1\n");
  1078. int x, y;
  1079. unsigned int width, height;
  1080. unsigned int border_width;
  1081. unsigned int depth;
  1082. Dlog_v("lglw:lglw_window_resize: 8.2\n");
  1083. if(!XGetGeometry(lglw->xdsp, lglw->win.xwnd, &root, &x, &y, &width, &height, &border_width, &depth))
  1084. return r;
  1085. Dlog_v("lglw:lglw_window_resize: 8.3\n");
  1086. XResizeWindow(lglw->xdsp, parent, width + deltaW, height + deltaH);
  1087. }
  1088. Dlog_v("lglw:lglw_window_resize: EXIT\n");
  1089. }
  1090. }
  1091. return r;
  1092. }
  1093. // ---------------------------------------------------------------------------- lglw_window_close
  1094. void lglw_window_close (lglw_t _lglw) {
  1095. LGLW(_lglw);
  1096. if(NULL != lglw)
  1097. {
  1098. if(0 != lglw->win.xwnd)
  1099. {
  1100. Dlog_v("lglw:lglw_window_close: 1\n");
  1101. lglw_timer_stop(_lglw);
  1102. Dlog_v("lglw:lglw_window_close: 2\n");
  1103. glXMakeCurrent(lglw->xdsp, None, NULL);
  1104. Dlog_v("lglw:lglw_window_close: 3\n");
  1105. if(lglw->win.b_owner)
  1106. {
  1107. XUnmapWindow(lglw->xdsp, lglw->win.xwnd);
  1108. XDestroyWindow(lglw->xdsp, lglw->win.xwnd);
  1109. lglw->win.b_owner = LGLW_FALSE;
  1110. }
  1111. XSync(lglw->xdsp, False);
  1112. lglw->win.xwnd = 0;
  1113. lglw->win.mapped = LGLW_FALSE;
  1114. {
  1115. XEvent xev;
  1116. int queued = XPending(lglw->xdsp);
  1117. Dlog_vvv("lglw:lglw_window_close: consume %d pending events\n", queued);
  1118. while(queued)
  1119. {
  1120. XNextEvent(lglw->xdsp, &xev);
  1121. queued--;
  1122. }
  1123. }
  1124. }
  1125. }
  1126. Dlog_v("lglw:lglw_window_close: EXIT\n");
  1127. }
  1128. // ---------------------------------------------------------------------------- lglw_window_show
  1129. void lglw_window_show(lglw_t _lglw) {
  1130. LGLW(_lglw);
  1131. if(NULL != lglw)
  1132. {
  1133. Dlog_v("lglw:lglw_window_show: 1\n");
  1134. XMapRaised(lglw->xdsp, lglw->win.xwnd);
  1135. lglw->win.mapped = LGLW_TRUE;
  1136. }
  1137. Dlog_v("lglw:lglw_window_show: EXIT\n");
  1138. }
  1139. // ---------------------------------------------------------------------------- lglw_window_hide
  1140. void lglw_window_hide(lglw_t _lglw) {
  1141. LGLW(_lglw);
  1142. if(NULL != lglw)
  1143. {
  1144. Dlog_v("lglw:lglw_window_hide: 1\n");
  1145. XUnmapWindow(lglw->xdsp, lglw->win.xwnd);
  1146. lglw->win.mapped = LGLW_FALSE;
  1147. }
  1148. Dlog_v("lglw:lglw_window_hide: EXIT\n");
  1149. }
  1150. // ---------------------------------------------------------------------------- lglw_window_is_visible
  1151. lglw_bool_t lglw_window_is_visible(lglw_t _lglw) {
  1152. lglw_bool_t r = LGLW_FALSE;
  1153. LGLW(_lglw);
  1154. // Dlog_vvv("lglw:lglw_window_is_visible: 1\n");
  1155. if(NULL != lglw && 0 != lglw->win.xwnd)
  1156. {
  1157. // Dlog_vvv("lglw:lglw_window_is_visible: 2\n");
  1158. r = lglw->win.mapped;
  1159. }
  1160. // Dlog_vvv("lglw:lglw_window_is_visible: EXIT\n");
  1161. return r;
  1162. }
  1163. // ---------------------------------------------------------------------------- lglw_window_size_get
  1164. void lglw_window_size_get(lglw_t _lglw, int32_t *_retX, int32_t *_retY) {
  1165. LGLW(_lglw);
  1166. Dlog_vvv("lglw:lglw_window_size_get: 1\n");
  1167. if(NULL != lglw)
  1168. {
  1169. if(0 != lglw->win.xwnd)
  1170. {
  1171. if(NULL != _retX)
  1172. *_retX = lglw->win.size.x;
  1173. if(NULL != _retY)
  1174. *_retY = lglw->win.size.y;
  1175. }
  1176. }
  1177. Dlog_vvv("lglw:lglw_window_size_get: EXIT\n");
  1178. }
  1179. // ---------------------------------------------------------------------------- lglw_redraw
  1180. void lglw_redraw(lglw_t _lglw) {
  1181. LGLW(_lglw);
  1182. if(NULL != lglw)
  1183. {
  1184. if(0 != lglw->win.xwnd)
  1185. {
  1186. Dlog_vvv("lglw:lglw_redraw: 1\n");
  1187. XEvent xev;
  1188. xev.xany.type = Expose;
  1189. xev.xany.serial = 0;
  1190. xev.xany.send_event = True;
  1191. xev.xany.display = lglw->xdsp;
  1192. xev.xany.window = lglw->win.xwnd;
  1193. xev.xexpose.x = 0;
  1194. xev.xexpose.y = 0;
  1195. xev.xexpose.width = lglw->win.size.x;
  1196. xev.xexpose.height = lglw->win.size.y;
  1197. xev.xexpose.count = 0;
  1198. XSendEvent(lglw->xdsp, lglw->win.xwnd,
  1199. True/*propagate*/,
  1200. ExposureMask/*event_mask*/,
  1201. &xev
  1202. );
  1203. XFlush(lglw->xdsp);
  1204. }
  1205. }
  1206. }
  1207. // ---------------------------------------------------------------------------- lglw_redraw_callback_set
  1208. void lglw_redraw_callback_set(lglw_t _lglw, lglw_redraw_fxn_t _cbk) {
  1209. LGLW(_lglw);
  1210. if(NULL != lglw)
  1211. {
  1212. lglw->redraw.cbk = _cbk;
  1213. }
  1214. }
  1215. // ---------------------------------------------------------------------------- lglw_glcontext_push
  1216. void lglw_glcontext_push(lglw_t _lglw) {
  1217. LGLW(_lglw);
  1218. if(NULL != lglw)
  1219. {
  1220. lglw->prev.drw = glXGetCurrentDrawable();
  1221. lglw->prev.ctx = glXGetCurrentContext();
  1222. Dlog_vvv("lglw:lglw_glcontext_push: win.xwnd=%p hidden.xwnd=%p ctx=%p\n",
  1223. lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx);
  1224. if(!glXMakeCurrent(lglw->xdsp, (0 == lglw->win.xwnd) ? lglw->hidden.xwnd : lglw->win.xwnd, lglw->ctx))
  1225. {
  1226. Dlog("[---] lglw_glcontext_push: glXMakeCurrent() failed. win.xwnd=%p hidden.xwnd=%p ctx=%p glGetError()=%d\n", lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx, glGetError());
  1227. }
  1228. // Dlog_vvv("lglw:lglw_glcontext_push: LEAVE\n");
  1229. }
  1230. }
  1231. // ---------------------------------------------------------------------------- lglw_glcontext_rebind
  1232. void lglw_glcontext_rebind(lglw_t _lglw) {
  1233. LGLW(_lglw);
  1234. // printf("xxx lglw_glcontext_rebind\n");
  1235. if(NULL != lglw)
  1236. {
  1237. Dlog_vvv("lglw:lglw_glcontext_rebind: win.xwnd=%p hidden.xwnd=%p ctx=%p\n",
  1238. lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx);
  1239. if(!glXMakeCurrent(lglw->xdsp, (0 == lglw->win.xwnd) ? lglw->hidden.xwnd : lglw->win.xwnd, lglw->ctx))
  1240. {
  1241. Dlog("[---] lglw_glcontext_rebind: glXMakeCurrent() failed. win.xwnd=%p hidden.xwnd=%p ctx=%p glGetError()=%d\n", lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx, glGetError());
  1242. }
  1243. }
  1244. }
  1245. // ---------------------------------------------------------------------------- lglw_glcontext_pop
  1246. void lglw_glcontext_pop(lglw_t _lglw) {
  1247. LGLW(_lglw);
  1248. if(NULL != lglw)
  1249. {
  1250. Dlog_vvv("lglw:lglw_glcontext_pop: prev.drw=%p prev.ctx=%p\n",
  1251. lglw->prev.drw, lglw->prev.ctx);
  1252. if(!glXMakeCurrent(lglw->xdsp, lglw->prev.drw, lglw->prev.ctx))
  1253. {
  1254. Dlog("[---] lglw_glcontext_pop: glXMakeCurrent() failed. prev.drw=%p ctx=%p glGetError()=%d\n", lglw->prev.drw, lglw->prev.ctx, glGetError());
  1255. }
  1256. }
  1257. }
  1258. // ---------------------------------------------------------------------------- lglw_swap_buffers
  1259. void lglw_swap_buffers(lglw_t _lglw) {
  1260. LGLW(_lglw);
  1261. if(NULL != lglw)
  1262. {
  1263. if(0 != lglw->win.xwnd)
  1264. {
  1265. Dlog_vv("lglw:lglw_swap_buffers: 1\n");
  1266. glXSwapBuffers(lglw->xdsp, lglw->win.xwnd);
  1267. }
  1268. }
  1269. }
  1270. // ---------------------------------------------------------------------------- lglw_swap_interval_set
  1271. typedef void (APIENTRY *PFNWGLEXTSWAPINTERVALPROC) (Display *, GLXDrawable, int);
  1272. void lglw_swap_interval_set(lglw_t _lglw, int32_t _ival) {
  1273. LGLW(_lglw);
  1274. if(NULL != lglw)
  1275. {
  1276. if(0 != lglw->win.xwnd)
  1277. {
  1278. Dlog_vv("lglw:lglw_swap_interval_set: 1\n");
  1279. PFNWGLEXTSWAPINTERVALPROC glXSwapIntervalEXT;
  1280. glXSwapIntervalEXT = (PFNWGLEXTSWAPINTERVALPROC) glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
  1281. if(NULL != glXSwapIntervalEXT)
  1282. {
  1283. Dlog_vv("lglw:lglw_swap_interval_set: 2\n");
  1284. glXSwapIntervalEXT(lglw->xdsp, lglw->win.xwnd, _ival);
  1285. lglw->win.swap_interval = _ival;
  1286. }
  1287. }
  1288. }
  1289. }
  1290. // ---------------------------------------------------------------------------- lglw_swap_interval_get
  1291. int32_t lglw_swap_interval_get(lglw_t _lglw) {
  1292. LGLW(_lglw);
  1293. int32_t r = 0;
  1294. if(NULL != lglw)
  1295. {
  1296. r = lglw->win.swap_interval;
  1297. }
  1298. return r;
  1299. }
  1300. // ---------------------------------------------------------------------------- loc_handle_mouseleave
  1301. static void loc_handle_mouseleave(lglw_int_t *lglw) {
  1302. lglw->focus.state &= ~LGLW_FOCUS_MOUSE;
  1303. if(NULL != lglw->focus.cbk)
  1304. {
  1305. lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE);
  1306. }
  1307. Dlog_vv("xxx lglw:loc_handle_mouseleave: LEAVE\n");
  1308. }
  1309. // ---------------------------------------------------------------------------- loc_handle_mouseenter
  1310. static void loc_handle_mouseenter(lglw_int_t *lglw) {
  1311. lglw->focus.state |= LGLW_FOCUS_MOUSE;
  1312. if(NULL != lglw->focus.cbk)
  1313. {
  1314. lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE);
  1315. }
  1316. Dlog_vv("xxx lglw:loc_handle_mouseenter: LEAVE\n");
  1317. }
  1318. // ---------------------------------------------------------------------------- loc_handle_mousebutton
  1319. static void loc_handle_mousebutton(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button) {
  1320. if(_bPressed)
  1321. lglw->mouse.button_state |= _button;
  1322. else
  1323. lglw->mouse.button_state &= ~_button;
  1324. if(NULL != lglw->mouse.cbk)
  1325. {
  1326. lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, _button);
  1327. }
  1328. }
  1329. // ---------------------------------------------------------------------------- loc_handle_mousemotion
  1330. static void loc_handle_mousemotion(lglw_int_t *lglw) {
  1331. if(NULL != lglw->mouse.cbk)
  1332. {
  1333. lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, 0u/*changedbuttonstate*/);
  1334. }
  1335. }
  1336. // ---------------------------------------------------------------------------- lglw_mouse_callback_set
  1337. void lglw_mouse_callback_set(lglw_t _lglw, lglw_mouse_fxn_t _cbk) {
  1338. LGLW(_lglw);
  1339. if(NULL != lglw)
  1340. {
  1341. lglw->mouse.cbk = _cbk;
  1342. }
  1343. }
  1344. // ---------------------------------------------------------------------------- lglw_mouse_callback_set
  1345. void lglw_focus_callback_set(lglw_t _lglw, lglw_focus_fxn_t _cbk) {
  1346. LGLW(_lglw);
  1347. if(NULL != lglw)
  1348. {
  1349. lglw->focus.cbk = _cbk;
  1350. }
  1351. }
  1352. // ---------------------------------------------------------------------------- loc_handle_key
  1353. static lglw_bool_t loc_handle_key(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey) {
  1354. lglw_bool_t r = LGLW_FALSE;
  1355. if(NULL != lglw->keyboard.cbk)
  1356. {
  1357. r = lglw->keyboard.cbk(lglw, _vkey, lglw->keyboard.kmod_state, _bPressed);
  1358. }
  1359. return r;
  1360. }
  1361. // ---------------------------------------------------------------------------- lglw_keyboard_callback_set
  1362. void lglw_keyboard_callback_set(lglw_t _lglw, lglw_keyboard_fxn_t _cbk) {
  1363. LGLW(_lglw);
  1364. if(NULL != lglw)
  1365. {
  1366. lglw->keyboard.cbk = _cbk;
  1367. }
  1368. }
  1369. // ---------------------------------------------------------------------------- lglw_keyboard_get_modifiers
  1370. uint32_t lglw_keyboard_get_modifiers(lglw_t _lglw) {
  1371. uint32_t r = 0u;
  1372. LGLW(_lglw);
  1373. if(NULL != lglw)
  1374. {
  1375. r = lglw->keyboard.kmod_state;
  1376. }
  1377. return r;
  1378. }
  1379. // ---------------------------------------------------------------------------- lglw_touchkeyboard_show
  1380. void lglw_touchkeyboard_show(lglw_t _lglw, lglw_bool_t _bEnable) {
  1381. LGLW(_lglw);
  1382. // (todo) implement me
  1383. if(NULL != lglw)
  1384. {
  1385. }
  1386. }
  1387. // ---------------------------------------------------------------------------- lglw_mouse_get_buttons
  1388. uint32_t lglw_mouse_get_buttons(lglw_t _lglw) {
  1389. uint32_t r = 0u;
  1390. LGLW(_lglw);
  1391. if(NULL != lglw)
  1392. {
  1393. r = lglw->mouse.button_state;
  1394. }
  1395. return r;
  1396. }
  1397. // ---------------------------------------------------------------------------- lglw_mouse_grab
  1398. void lglw_mouse_grab(lglw_t _lglw, uint32_t _grabMode) {
  1399. LGLW(_lglw);
  1400. if(NULL != lglw)
  1401. {
  1402. if(0 != lglw->win.xwnd)
  1403. {
  1404. if(!lglw->mouse.touch.b_enable)
  1405. {
  1406. if(lglw->mouse.grab.mode != _grabMode)
  1407. {
  1408. lglw_mouse_ungrab(_lglw);
  1409. }
  1410. int result;
  1411. switch(_grabMode)
  1412. {
  1413. default:
  1414. case LGLW_MOUSE_GRAB_NONE:
  1415. break;
  1416. case LGLW_MOUSE_GRAB_CAPTURE:
  1417. result = XGrabPointer(lglw->xdsp, lglw->win.xwnd,
  1418. True/*owner_events*/,
  1419. NoEventMask/*event_mask*/,
  1420. GrabModeAsync/*pointer_mode*/,
  1421. GrabModeAsync/*keyboard_mode*/,
  1422. lglw->win.xwnd/*confine_to*/,
  1423. None/*cursor*/,
  1424. CurrentTime/*time*/);
  1425. if(GrabSuccess != result)
  1426. {
  1427. Dlog("lglw: Grab Result: %i\n", result);
  1428. }
  1429. else
  1430. {
  1431. lglw->mouse.grab.mode = _grabMode;
  1432. }
  1433. break;
  1434. case LGLW_MOUSE_GRAB_WARP:
  1435. result = XGrabPointer(lglw->xdsp, lglw->win.xwnd,
  1436. True/*owner_events*/,
  1437. NoEventMask/*event_mask*/,
  1438. GrabModeAsync/*pointer_mode*/,
  1439. GrabModeAsync/*keyboard_mode*/,
  1440. lglw->win.xwnd/*confine_to*/,
  1441. None/*cursor*/,
  1442. CurrentTime/*time*/);
  1443. if(GrabSuccess != result)
  1444. {
  1445. Dlog("lglw: Grab Result: %i\n", result);
  1446. }
  1447. else
  1448. {
  1449. lglw_mouse_cursor_show(_lglw, LGLW_FALSE);
  1450. lglw->mouse.grab.p = lglw->mouse.p;
  1451. lglw->mouse.grab.last_p = lglw->mouse.p;
  1452. lglw->mouse.grab.mode = _grabMode;
  1453. }
  1454. break;
  1455. }
  1456. }
  1457. }
  1458. }
  1459. }
  1460. // ---------------------------------------------------------------------------- lglw_mouse_ungrab
  1461. void lglw_mouse_ungrab(lglw_t _lglw) {
  1462. LGLW(_lglw);
  1463. if(NULL != lglw)
  1464. {
  1465. if(0 != lglw->win.xwnd)
  1466. {
  1467. if(!lglw->mouse.touch.b_enable)
  1468. {
  1469. switch(lglw->mouse.grab.mode)
  1470. {
  1471. default:
  1472. case LGLW_MOUSE_GRAB_NONE:
  1473. break;
  1474. case LGLW_MOUSE_GRAB_CAPTURE:
  1475. XUngrabPointer(lglw->xdsp, CurrentTime);
  1476. lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE;
  1477. break;
  1478. case LGLW_MOUSE_GRAB_WARP:
  1479. XUngrabPointer(lglw->xdsp, CurrentTime);
  1480. lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE;
  1481. lglw->mouse.grab.b_queue_warp = LGLW_TRUE;
  1482. lglw_mouse_cursor_show(_lglw, LGLW_TRUE);
  1483. break;
  1484. }
  1485. }
  1486. }
  1487. }
  1488. }
  1489. // ---------------------------------------------------------------------------- lglw_mouse_warp
  1490. void lglw_mouse_warp(lglw_t _lglw, int32_t _x, int32_t _y) {
  1491. LGLW(_lglw);
  1492. if(NULL != lglw)
  1493. {
  1494. if(0 != lglw->win.xwnd)
  1495. {
  1496. XWarpPointer(lglw->xdsp,
  1497. None/*src_w*/,
  1498. lglw->win.xwnd/*dest_w*/,
  1499. 0/*src_x*/,
  1500. 0/*src_y*/,
  1501. 0/*src_width*/,
  1502. 0/*src_height*/,
  1503. _x/*dest_x*/,
  1504. _y/*dest_y*/);
  1505. }
  1506. }
  1507. }
  1508. // ---------------------------------------------------------------------------- loc_handle_queued_mouse_warp
  1509. static void loc_handle_queued_mouse_warp(lglw_int_t *lglw) {
  1510. if(lglw->mouse.grab.b_queue_warp)
  1511. {
  1512. lglw->mouse.grab.b_queue_warp = LGLW_FALSE;
  1513. lglw_mouse_warp(lglw, lglw->mouse.grab.p.x, lglw->mouse.grab.p.y);
  1514. lglw->mouse.grab.last_p = lglw->mouse.grab.p;
  1515. }
  1516. }
  1517. // ---------------------------------------------------------------------------- lglw_mouse_cursor_show
  1518. void lglw_mouse_cursor_show (lglw_t _lglw, lglw_bool_t _bShow) {
  1519. LGLW(_lglw);
  1520. if(NULL != lglw)
  1521. {
  1522. if(LGLW_FALSE == _bShow)
  1523. {
  1524. Pixmap noPxm;
  1525. Cursor noCursor;
  1526. XColor black, dummy;
  1527. static char pxmNoData[] = {0, 0, 0, 0, 0, 0, 0, 0};
  1528. XAllocNamedColor(lglw->xdsp, lglw->cmap, "black", &black, &dummy);
  1529. noPxm = XCreateBitmapFromData(lglw->xdsp, lglw->win.xwnd, pxmNoData, 8, 8);
  1530. noCursor = XCreatePixmapCursor(lglw->xdsp, noPxm, noPxm, &black, &black, 0, 0);
  1531. XDefineCursor(lglw->xdsp, lglw->win.xwnd, noCursor);
  1532. XFreeCursor(lglw->xdsp, noCursor);
  1533. if(noPxm != None)
  1534. {
  1535. XFreePixmap(lglw->xdsp, noPxm);
  1536. }
  1537. }
  1538. else
  1539. {
  1540. XUndefineCursor(lglw->xdsp, lglw->win.xwnd);
  1541. }
  1542. }
  1543. }
  1544. // ---------------------------------------------------------------------------- lglw_timer_start
  1545. void lglw_timer_start(lglw_t _lglw, uint32_t _millisec) {
  1546. LGLW(_lglw);
  1547. if(NULL != lglw)
  1548. {
  1549. Dlog_v("lglw:lglw_timer_start: interval=%u\n", _millisec);
  1550. lglw->timer.interval_ms = _millisec;
  1551. lglw->timer.b_running = LGLW_TRUE;
  1552. }
  1553. }
  1554. // ---------------------------------------------------------------------------- lglw_timer_stop
  1555. void lglw_timer_stop(lglw_t _lglw) {
  1556. LGLW(_lglw);
  1557. if(NULL != lglw)
  1558. {
  1559. Dlog_v("lglw:lglw_timer_stop\n");
  1560. lglw->timer.b_running = LGLW_FALSE;
  1561. }
  1562. }
  1563. // ---------------------------------------------------------------------------- lglw_timer_callback_set
  1564. void lglw_timer_callback_set(lglw_t _lglw, lglw_timer_fxn_t _cbk) {
  1565. LGLW(_lglw);
  1566. if(NULL != lglw)
  1567. {
  1568. lglw->timer.cbk = _cbk;
  1569. }
  1570. }
  1571. // ---------------------------------------------------------------------------- loc_process_timer()
  1572. static void loc_process_timer(lglw_int_t *lglw) {
  1573. if(lglw->timer.b_running)
  1574. {
  1575. uint32_t ms = loc_millisec_delta(lglw);
  1576. if( (ms - lglw->timer.last_ms) >= lglw->timer.interval_ms )
  1577. {
  1578. lglw->timer.last_ms = ms;
  1579. if(NULL != lglw->timer.cbk)
  1580. {
  1581. Dlog_vvv("lglw: invoke timer callback\n");
  1582. lglw->timer.cbk(lglw);
  1583. }
  1584. }
  1585. }
  1586. }
  1587. // ---------------------------------------------------------------------------- loc_enable_dropfiles
  1588. static void loc_enable_dropfiles(lglw_int_t *lglw, lglw_bool_t _bEnable) {
  1589. // (todo) implement me
  1590. }
  1591. // ---------------------------------------------------------------------------- lglw_dropfiles_callback_set
  1592. void lglw_dropfiles_callback_set(lglw_t _lglw, lglw_dropfiles_fxn_t _cbk) {
  1593. LGLW(_lglw);
  1594. if(NULL != _lglw)
  1595. {
  1596. lglw->dropfiles.cbk = _cbk;
  1597. loc_enable_dropfiles(lglw, (NULL != _cbk));
  1598. }
  1599. }
  1600. // ---------------------------------------------------------------------------- lglw_touchinput_set
  1601. void lglw_touchinput_set(lglw_t _lglw, lglw_bool_t _bEnable) {
  1602. LGLW(_lglw);
  1603. if(NULL != _lglw)
  1604. {
  1605. lglw->mouse.touch.b_enable = _bEnable;
  1606. lglw->mouse.touch.b_update_queued = LGLW_TRUE;
  1607. }
  1608. }
  1609. // ---------------------------------------------------------------------------- lglw_touchinput_get
  1610. lglw_bool_t lglw_touchinput_get(lglw_t _lglw) {
  1611. lglw_bool_t r = LGLW_FALSE;
  1612. LGLW(_lglw);
  1613. if(NULL != _lglw)
  1614. {
  1615. r = lglw->mouse.touch.b_enable;
  1616. }
  1617. return r;
  1618. }
  1619. // ---------------------------------------------------------------------------- lglw_clipboard_text_set
  1620. void lglw_clipboard_text_set(lglw_t _lglw, const uint32_t _numChars, const char *_text) {
  1621. LGLW(_lglw);
  1622. (void)_numChars;
  1623. if(NULL != _text)
  1624. {
  1625. if(NULL != _lglw)
  1626. {
  1627. if(0 != lglw->win.xwnd)
  1628. {
  1629. uint32_t numChars = (0u == _numChars) ? ((uint32_t)strlen(_text)+1u) : _numChars;
  1630. if(numChars > 0u)
  1631. {
  1632. lglw->clipboard.numChars = numChars;
  1633. lglw->clipboard.data = malloc(numChars+1);
  1634. uint32_t i;
  1635. for(i = 0u; i < numChars; i++)
  1636. {
  1637. lglw->clipboard.data[i] = _text[i];
  1638. }
  1639. lglw->clipboard.data[numChars - 1] = 0;
  1640. Dlog("xxx lglw_clipboard_text_set(%i): %s\n", lglw->clipboard.numChars, lglw->clipboard.data);
  1641. Atom clipboard = XInternAtom(lglw->xdsp, "CLIPBOARD", False);
  1642. XSetSelectionOwner(lglw->xdsp, clipboard, lglw->win.xwnd, CurrentTime);
  1643. XSync(lglw->xdsp, False);
  1644. }
  1645. }
  1646. }
  1647. }
  1648. }
  1649. // ---------------------------------------------------------------------------- loc_is_clipboard_event
  1650. static Bool loc_is_clipboard_event(Display *_display, XEvent *_xevent, XPointer _xarg) {
  1651. return _xevent->type == SelectionNotify;
  1652. }
  1653. // ---------------------------------------------------------------------------- lglw_clipboard_text_get
  1654. void lglw_clipboard_text_get(lglw_t _lglw, uint32_t _maxChars, uint32_t *_retNumChars, char *_retText) {
  1655. LGLW(_lglw);
  1656. if(NULL != _retNumChars)
  1657. *_retNumChars = 0u;
  1658. if(NULL != _retText)
  1659. *_retText = 0;
  1660. if(_maxChars > 0u)
  1661. {
  1662. if(NULL != _lglw)
  1663. {
  1664. if(0 != lglw->win.xwnd)
  1665. {
  1666. Window owner;
  1667. XEvent xev;
  1668. XSelectionEvent *cbReq;
  1669. Atom clipboard = XInternAtom(lglw->xdsp, "CLIPBOARD", False);
  1670. Atom utf8 = XInternAtom(lglw->xdsp, "UTF8_STRING", False);
  1671. Atom target = XInternAtom(lglw->xdsp, "_clipboard_result", False);
  1672. owner = XGetSelectionOwner(lglw->xdsp, clipboard);
  1673. if(owner == None)
  1674. {
  1675. Dlog("xxx lglw_clipboard_text_get: No Window can provide a clipboard result\n");
  1676. return;
  1677. }
  1678. if(owner == lglw->win.xwnd)
  1679. {
  1680. Dlog("xxx lglw_clipboard_text_get: We are the owner of the clipboard, skip X interactions\n");
  1681. uint32_t i = 0u;
  1682. for(; i < _maxChars; i++)
  1683. {
  1684. _retText[i] = lglw->clipboard.data[i];
  1685. if(0 == _retText[i])
  1686. break;
  1687. }
  1688. _retText[_maxChars - 1u] = 0;
  1689. if(NULL != _retNumChars)
  1690. *_retNumChars = i;
  1691. Dlog("xxx lglw_clipboard_text_get: (result on next line)\n%s\n", _retText);
  1692. return;
  1693. }
  1694. XConvertSelection(lglw->xdsp, clipboard, utf8, target, lglw->win.xwnd, CurrentTime);
  1695. XIfEvent(lglw->xdsp, &xev, &loc_is_clipboard_event, None);
  1696. cbReq = (XSelectionEvent*)&xev;
  1697. if(None == cbReq->property)
  1698. {
  1699. Dlog("xxx lglw_clipboard_text_get: Clipboard was not converted to UTF-8 string\n");
  1700. return;
  1701. }
  1702. Atom returnType;
  1703. int returnFormat;
  1704. unsigned long size, returnSize, bytesLeft;
  1705. unsigned char *propertyValue = NULL;
  1706. XGetWindowProperty(lglw->xdsp, lglw->win.xwnd, target,
  1707. 0/*offset*/,
  1708. 0/*length*/,
  1709. False/*delete*/,
  1710. AnyPropertyType/*req_type*/,
  1711. &returnType/*actual_type_return*/,
  1712. &returnFormat/*actual_format_return*/,
  1713. &returnSize/*nitems_return*/,
  1714. &size/*bytes_after_return*/,
  1715. &propertyValue/*prop_return*/);
  1716. XFree(propertyValue);
  1717. if(utf8 != returnType)
  1718. {
  1719. Dlog("xxx lglw_clipboard_text_get: Clipboard result is not a UTF-8 string\n");
  1720. return;
  1721. }
  1722. if(8u != returnFormat)
  1723. {
  1724. Dlog("xxx lglw_clipboard_text_get: Clipboard format is not a char array\n");
  1725. return;
  1726. }
  1727. if(_maxChars < size)
  1728. size = _maxChars;
  1729. size = 1 + ((size - 1) / 4);
  1730. // TODO: Even with the largest current use-case, multiple calls aren't necessary. do it anyway just in case
  1731. XGetWindowProperty(lglw->xdsp, lglw->win.xwnd, target,
  1732. 0/*offset*/,
  1733. size/*length*/,
  1734. True/*delete*/,
  1735. AnyPropertyType/*req_type*/,
  1736. &returnType/*actual_type_return*/,
  1737. &returnFormat/*actual_format_return*/,
  1738. &returnSize/*nitems_return*/,
  1739. &bytesLeft/*bytes_after_return*/,
  1740. &propertyValue/*prop_return*/);
  1741. if(returnSize == 0)
  1742. {
  1743. Dlog("xxx lglw_clipboard_text_get: No Clipboard result after final request\n");
  1744. return;
  1745. }
  1746. uint32_t i = 0u;
  1747. for(; i < _maxChars; i++)
  1748. {
  1749. _retText[i] = propertyValue[i];
  1750. if(0 == _retText[i])
  1751. break;
  1752. }
  1753. _retText[_maxChars - 1u] = 0;
  1754. if(NULL != _retNumChars)
  1755. *_retNumChars = i;
  1756. Dlog("xxx lglw_clipboard_text_get: (result on next line)\n%s\n", _retText);
  1757. XFree(propertyValue);
  1758. }
  1759. }
  1760. }
  1761. }
  1762. // ---------------------------------------------------------------------------- lglw_events
  1763. void lglw_events(lglw_t _lglw) {
  1764. LGLW(_lglw);
  1765. if(NULL != lglw)
  1766. {
  1767. if(0 != lglw->win.xwnd)
  1768. {
  1769. XEvent xev;
  1770. int queued = XPending(lglw->xdsp);
  1771. if(queued > 0)
  1772. {
  1773. Dlog_vvv("lglw:lglw_events: (events: %i)\n", queued);
  1774. }
  1775. while(queued)
  1776. {
  1777. XNextEvent(lglw->xdsp, &xev);
  1778. loc_eventProc(&xev, lglw);
  1779. queued--;
  1780. }
  1781. loc_process_timer(lglw);
  1782. }
  1783. }
  1784. }