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

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