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.

2247 lines
72KB

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