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.

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