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.

2184 lines
70KB

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