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.

2195 lines
71KB

  1. /* ----
  2. * ---- file : lglw_linux.c (**stub**)
  3. * ---- author : bsp
  4. * ---- legal : Distributed under terms of the MIT LICENSE (MIT).
  5. * ----
  6. * ---- Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * ---- of this software and associated documentation files (the "Software"), to deal
  8. * ---- in the Software without restriction, including without limitation the rights
  9. * ---- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * ---- copies of the Software, and to permit persons to whom the Software is
  11. * ---- furnished to do so, subject to the following conditions:
  12. * ----
  13. * ---- The above copyright notice and this permission notice shall be included in
  14. * ---- all copies or substantial portions of the Software.
  15. * ----
  16. * ---- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * ---- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * ---- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * ---- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * ---- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * ---- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * ---- THE SOFTWARE.
  23. * ----
  24. * ---- info : This is part of the "lglw" package.
  25. * ----
  26. * ---- created: 04Aug2018
  27. * ---- changed: 05Aug2018, 06Aug2018, 07Aug2018, 08Aug2018, 09Aug2018, 18Aug2018, 10Oct2018, 16Oct2018
  28. * ----
  29. * ----
  30. */
  31. #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_F1:
  459. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F1);
  460. break;
  461. case XK_F2:
  462. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F2);
  463. break;
  464. case XK_F3:
  465. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F3);
  466. break;
  467. case XK_F4:
  468. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F4);
  469. break;
  470. case XK_F5:
  471. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F5);
  472. break;
  473. case XK_F6:
  474. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F6);
  475. break;
  476. case XK_F7:
  477. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F7);
  478. break;
  479. case XK_F8:
  480. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F8);
  481. break;
  482. case XK_F9:
  483. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F9);
  484. break;
  485. case XK_F10:
  486. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F10);
  487. break;
  488. case XK_F11:
  489. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F11);
  490. break;
  491. case XK_F12:
  492. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_F12);
  493. break;
  494. case XK_BackSpace:
  495. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_BACKSPACE);
  496. break;
  497. case XK_Tab:
  498. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_TAB);
  499. break;
  500. case XK_Return:
  501. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RETURN);
  502. break;
  503. case XK_Escape:
  504. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_ESCAPE);
  505. break;
  506. case XK_Shift_L:
  507. lglw->keyboard.kmod_state |= LGLW_KMOD_LSHIFT;
  508. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_LSHIFT);
  509. eventHandled = LGLW_FALSE;
  510. break;
  511. case XK_Shift_R:
  512. lglw->keyboard.kmod_state |= LGLW_KMOD_RSHIFT;
  513. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_VKEY_RSHIFT);
  514. eventHandled = LGLW_FALSE;
  515. break;
  516. case XK_Control_L:
  517. lglw->keyboard.kmod_state |= LGLW_KMOD_LCTRL;
  518. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_KMOD_LCTRL);
  519. eventHandled = LGLW_FALSE;
  520. break;
  521. case XK_Control_R:
  522. lglw->keyboard.kmod_state |= LGLW_KMOD_RCTRL;
  523. eventHandled = loc_handle_key(lglw, LGLW_TRUE/*bPressed*/, LGLW_KMOD_RCTRL);
  524. eventHandled = LGLW_FALSE;
  525. break;
  526. }
  527. break;
  528. case KeyRelease:
  529. Dlog("lglw:loc_eventProc: xev KeyRelease\n");
  530. XKeyReleasedEvent *keyRelease = (XKeyReleasedEvent*)xev;
  531. eventHandled = LGLW_FALSE;
  532. KeySym xkr = XLookupKeysym(keyRelease, 0);
  533. switch(xkr)
  534. {
  535. default:
  536. Dlog("lglw:loc_eventProc: xev KeyRelease: %x or %lu\n", keyRelease->keycode, xkr);
  537. if(0u != (lglw->keyboard.kmod_state & LGLW_KMOD_SHIFT))
  538. {
  539. KeySym xkrl;
  540. KeySym xkru;
  541. XConvertCase(xkr, &xkrl, &xkru);
  542. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, xkru);
  543. }
  544. else
  545. {
  546. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, xkr);
  547. }
  548. break;
  549. case NoSymbol:
  550. Dlog("lglw:loc_eventProc: xev UNKNOWN KeyRelease: %x\n", keyRelease->keycode);
  551. break;
  552. case XK_Left:
  553. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LEFT);
  554. break;
  555. case XK_Right:
  556. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RIGHT);
  557. break;
  558. case XK_Up:
  559. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_UP);
  560. break;
  561. case XK_Down:
  562. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_DOWN);
  563. break;
  564. case XK_Insert:
  565. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_INSERT);
  566. break;
  567. case XK_Delete:
  568. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_DELETE);
  569. break;
  570. case XK_Home:
  571. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_HOME);
  572. break;
  573. case XK_End:
  574. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_END);
  575. break;
  576. case XK_F1:
  577. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F1);
  578. break;
  579. case XK_F2:
  580. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F2);
  581. break;
  582. case XK_F3:
  583. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F3);
  584. break;
  585. case XK_F4:
  586. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F4);
  587. break;
  588. case XK_F5:
  589. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F5);
  590. break;
  591. case XK_F6:
  592. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F6);
  593. break;
  594. case XK_F7:
  595. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F7);
  596. break;
  597. case XK_F8:
  598. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F8);
  599. break;
  600. case XK_F9:
  601. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F9);
  602. break;
  603. case XK_F10:
  604. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F10);
  605. break;
  606. case XK_F11:
  607. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F11);
  608. break;
  609. case XK_F12:
  610. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_F12);
  611. break;
  612. case XK_BackSpace:
  613. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_BACKSPACE);
  614. break;
  615. case XK_Tab:
  616. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_TAB);
  617. break;
  618. case XK_Return:
  619. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RETURN);
  620. break;
  621. case XK_Escape:
  622. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_ESCAPE);
  623. break;
  624. case XK_Shift_L:
  625. lglw->keyboard.kmod_state &= ~LGLW_KMOD_LSHIFT;
  626. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LSHIFT);
  627. eventHandled = LGLW_FALSE;
  628. break;
  629. case XK_Shift_R:
  630. lglw->keyboard.kmod_state &= ~LGLW_KMOD_RSHIFT;
  631. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RSHIFT);
  632. eventHandled = LGLW_FALSE;
  633. break;
  634. case XK_Control_L:
  635. lglw->keyboard.kmod_state &= ~LGLW_KMOD_LCTRL;
  636. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_LCTRL);
  637. eventHandled = LGLW_FALSE;
  638. break;
  639. case XK_Control_R:
  640. lglw->keyboard.kmod_state &= ~LGLW_KMOD_RCTRL;
  641. eventHandled = loc_handle_key(lglw, LGLW_FALSE/*bPressed*/, LGLW_VKEY_RCTRL);
  642. eventHandled = LGLW_FALSE;
  643. break;
  644. }
  645. break;
  646. case ButtonPress:
  647. Dlog("lglw:loc_eventProc: xev ButtonPress\n");
  648. XButtonPressedEvent *btnPress = (XButtonPressedEvent*)xev;
  649. lglw->mouse.p.x = btnPress->x;
  650. lglw->mouse.p.y = btnPress->y;
  651. if(0u == (lglw->focus.state & LGLW_FOCUS_MOUSE))
  652. {
  653. loc_handle_mouseenter(lglw);
  654. }
  655. switch(btnPress->button)
  656. {
  657. default:
  658. Dlog("lglw:loc_eventProc: xev ButtonPress unhandled button: %i\n", btnPress->button);
  659. eventHandled = LGLW_FALSE;
  660. break;
  661. case Button1:
  662. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_LBUTTON);
  663. eventHandled = LGLW_TRUE;
  664. break;
  665. case Button2:
  666. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_MBUTTON);
  667. eventHandled = LGLW_TRUE;
  668. break;
  669. case Button3:
  670. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_RBUTTON);
  671. eventHandled = LGLW_TRUE;
  672. break;
  673. case Button4:
  674. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_WHEELUP);
  675. eventHandled = LGLW_TRUE;
  676. break;
  677. case Button5:
  678. loc_handle_mousebutton(lglw, LGLW_TRUE/*bPressed*/, LGLW_MOUSE_WHEELDOWN);
  679. eventHandled = LGLW_TRUE;
  680. break;
  681. }
  682. break;
  683. case ButtonRelease:
  684. Dlog("lglw:loc_eventProc: xev ButtonRelease\n");
  685. XButtonReleasedEvent *btnRelease = (XButtonReleasedEvent*)xev;
  686. lglw->mouse.p.x = btnRelease->x;
  687. lglw->mouse.p.y = btnRelease->y;
  688. switch(btnRelease->button)
  689. {
  690. default:
  691. Dlog("lglw:loc_eventProc: xev ButtonRelease unhandled button: %i\n", btnRelease->button);
  692. eventHandled = LGLW_FALSE;
  693. break;
  694. case Button1:
  695. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_LBUTTON);
  696. eventHandled = LGLW_TRUE;
  697. break;
  698. case Button2:
  699. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_MBUTTON);
  700. eventHandled = LGLW_TRUE;
  701. break;
  702. case Button3:
  703. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_RBUTTON);
  704. eventHandled = LGLW_TRUE;
  705. break;
  706. case Button4:
  707. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_WHEELUP);
  708. eventHandled = LGLW_TRUE;
  709. break;
  710. case Button5:
  711. loc_handle_mousebutton(lglw, LGLW_FALSE/*bPressed*/, LGLW_MOUSE_WHEELDOWN);
  712. eventHandled = LGLW_TRUE;
  713. break;
  714. }
  715. break;
  716. case SelectionClear:
  717. Dlog("lglw:loc_eventProc: xev SelectionClear\n");
  718. lglw->clipboard.numChars = 0;
  719. free(lglw->clipboard.data);
  720. eventHandled = LGLW_TRUE;
  721. break;
  722. case SelectionRequest:
  723. Dlog("lglw:loc_eventProc: xev SelectionRequest\n");
  724. XSelectionRequestEvent *cbReq = (XSelectionRequestEvent*)xev;
  725. XSelectionEvent cbRes;
  726. Atom utf8 = XInternAtom(lglw->xdsp, "UTF8_STRING", False);
  727. cbRes.type = SelectionNotify;
  728. cbRes.requestor = cbReq->requestor;
  729. cbRes.selection = cbReq->selection;
  730. cbRes.target = cbReq->target;
  731. cbRes.time = cbReq->time;
  732. if(cbReq->target == utf8)
  733. {
  734. XChangeProperty(lglw->xdsp, cbReq->requestor, cbReq->property, utf8, 8/*format*/, PropModeReplace,
  735. (unsigned char *)lglw->clipboard.data, lglw->clipboard.numChars);
  736. cbRes.property = cbReq->property;
  737. }
  738. else
  739. {
  740. cbRes.property = None;
  741. }
  742. XSendEvent(lglw->xdsp, cbReq->requestor, True, NoEventMask, (XEvent *)&cbRes);
  743. eventHandled = LGLW_TRUE;
  744. break;
  745. }
  746. #if 0
  747. // (note) this causes a feedback loop in the VST2 debug host
  748. if(LGLW_FALSE == eventHandled)
  749. {
  750. if(0 == lglw->parent_xwnd)
  751. {
  752. Dlog("lglw:loc_eventProc: no parent window to send events to");
  753. XSendEvent(lglw->xdsp, InputFocus, True/*propgate*/, NoEventMask, xev);
  754. }
  755. else
  756. {
  757. XSendEvent(lglw->xdsp, lglw->parent_xwnd, True/*propgate*/, NoEventMask, xev);
  758. }
  759. }
  760. #else
  761. (void)eventHandled;
  762. #endif
  763. }
  764. }
  765. static void loc_XEventProc(void *_xevent) {
  766. XEvent *xev = (XEvent*)_xevent;
  767. Dlog_vvv("lglw:loc_XEventProc: ENTER\n");
  768. // Dlog_vvv("lglw: XEventProc, xev=%p\n", xev);
  769. if(NULL != xev)
  770. {
  771. LGLW(loc_getProperty(xev->xany.display, xev->xany.window, "_lglw")); // get instance pointer
  772. Dlog_vvv("lglw:loc_XEventProc: xev=%p lglw=%p\n", xev, lglw);
  773. loc_eventProc(xev, lglw);
  774. }
  775. Dlog_vvv("lglw:loc_XEventProc: LEAVE\n");
  776. }
  777. static void loc_setProperty(Display *_display, Window _window, const char *_name, void *_value) {
  778. size_t data = (size_t)_value;
  779. long temp[2];
  780. // Split the 64 bit pointer into a little-endian long array
  781. temp[0] = (long)(data & 0xffffffffUL);
  782. temp[1] = (long)(data >> 32L);
  783. 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]);
  784. Atom atom = XInternAtom(_display, _name, False/*only_if_exists*/);
  785. // (note) what's quite weird here is that we're writing an array of 32bit values, yet the element format must be 64bit (long)
  786. XChangeProperty(_display, _window,
  787. atom/*property*/,
  788. atom/*type*/,
  789. 32/*format*/,
  790. PropModeReplace/*mode*/,
  791. (unsigned char*)temp/*data*/,
  792. 2/*nelements*/
  793. );
  794. }
  795. static void *loc_getProperty(Display *_display, Window _window, const char *_name) {
  796. int userSize;
  797. unsigned long bytes;
  798. unsigned long userCount;
  799. unsigned char *data;
  800. Atom userType;
  801. Atom atom = XInternAtom(_display, _name, False);
  802. // (note) 64bit properties need to be read with two XGetWindowProperty() calls.
  803. // When using just one call and setting the 'length' to 2, the upper 32bit (second array element) will be 0xFFFFffff.
  804. XGetWindowProperty(_display,
  805. _window,
  806. atom,
  807. 0/*offset*/,
  808. 1/*length*/,
  809. False/*delete*/,
  810. AnyPropertyType,
  811. &userType/*actual_type_return*/,
  812. &userSize/*actual_format_return*/,
  813. &userCount/*nitems_return*/,
  814. &bytes/*bytes_after_return / partial reads*/,
  815. &data);
  816. union {
  817. uint32_t ui[2];
  818. void *any;
  819. } uptr;
  820. uptr.any = 0;
  821. Dlog_vvv("lglw:loc_getProperty: LOWER userSize=%d userCount=%lu bytes=%lu data=%p\n", userSize, userCount, bytes, data);
  822. if(NULL != data)
  823. {
  824. if(userCount >= 1)
  825. {
  826. // lower 32-bit
  827. uptr.ui[0] = *(long*)data;
  828. uptr.ui[1] = 0;
  829. Dlog_vvv("lglw:loc_getProperty: lower=0x%08x\n", uptr.ui[0]);
  830. XFree(data);
  831. XGetWindowProperty(_display,
  832. _window,
  833. atom,
  834. 1/*offset*/,
  835. 1/*length*/,
  836. False/*delete*/,
  837. AnyPropertyType,
  838. &userType/*actual_type_return*/,
  839. &userSize/*actual_format_return*/,
  840. &userCount/*nitems_return*/,
  841. &bytes/*bytes_after_return / partial reads*/,
  842. &data);
  843. Dlog_vvv("lglw:loc_getProperty: UPPER userSize=%d userCount=%lu bytes=%lu data=%p\n", userSize, userCount, bytes, data);
  844. if(NULL != data)
  845. {
  846. // upper 32-bit
  847. uptr.ui[1] = *(long*)data;
  848. Dlog_vvv("lglw:loc_getProperty: upper=0x%08x\n", uptr.ui[1]);
  849. XFree(data);
  850. }
  851. }
  852. }
  853. Dlog_vvv("lglw:loc_getProperty: return value=%p\n", uptr.any);
  854. return uptr.any;
  855. }
  856. #ifdef ARCH_X64
  857. #if 0
  858. // Pulled from the Renoise 64-bit callback example
  859. // Unsure what data was supposed to be, but swapping it to a function name did not work
  860. // This does nothing, no event proc found
  861. static void loc_setEventProc (Display *display, Window window) {
  862. size_t data = (size_t)loc_eventProc;
  863. long temp[2];
  864. Dlog("lglw: setEventProc (2*32bit). window=%lu loc_eventProc=%p\n", window, &loc_eventProc);
  865. // Split the 64 bit pointer into a little-endian unsigned int array
  866. temp[0] = (uint32_t)(data & 0xffffffffUL);
  867. temp[1] = (uint32_t)(data >> 32L);
  868. Atom atom = XInternAtom(display, "_XEventProc", False);
  869. XChangeProperty(display, window,
  870. atom/*property*/,
  871. atom/*type*/,
  872. 32/*format*/,
  873. PropModeReplace/*mode*/,
  874. (unsigned char*)temp/*data*/,
  875. 2/*nelements*/
  876. );
  877. }
  878. #else
  879. // GPL code pulled from the amsynth example <https://github.com/amsynth/amsynth/blob/4a87798e650c6d71d70274a961c9b8d98fc6da7e/src/amsynth_vst.cpp>
  880. // Simply swapped out the function names, crashes Ardour in the same was as the others
  881. static void loc_setEventProc (Display *display, Window window) {
  882. //
  883. // JUCE calls XGetWindowProperty with long_length = 1 which means it only fetches the lower 32 bits of the address.
  884. // Therefore we need to ensure we return an address in the lower 32-bits of address space.
  885. //
  886. // based on mach_override
  887. static const unsigned char kJumpInstructions[] = {
  888. 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
  889. 0x00, 0x00, 0x00, 0x00,
  890. 0x00, 0x00, 0x00, 0x00
  891. };
  892. static const int kJumpAddress = 6;
  893. static char *ptr = 0;
  894. if (!ptr) {
  895. ptr = (char *)mmap(0,
  896. getpagesize()/*PAGE_SIZE*/,
  897. PROT_READ | PROT_WRITE | PROT_EXEC,
  898. MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT,
  899. 0, 0);
  900. if (ptr == MAP_FAILED) {
  901. perror("mmap");
  902. ptr = 0;
  903. return;
  904. } else {
  905. memcpy(ptr, kJumpInstructions, sizeof(kJumpInstructions));
  906. *((uint64_t *)(ptr + kJumpAddress)) = (uint64_t)(&loc_XEventProc);
  907. msync(ptr, sizeof(kJumpInstructions), MS_INVALIDATE);
  908. Dlog("lglw: 64bit trampoline installed\n");
  909. }
  910. }
  911. long temp[2] = {(uint32_t)(((size_t)ptr)&0xFFFFfffful), 0};
  912. Atom atom = XInternAtom(display, "_XEventProc", False);
  913. XChangeProperty(display, window,
  914. atom/*property*/,
  915. atom/*type*/,
  916. 32/*format*/,
  917. PropModeReplace/*mode*/,
  918. (unsigned char *)temp/*data*/,
  919. 2/*nelements*/
  920. );
  921. }
  922. #endif
  923. #else
  924. // Pulled from the eXT2 example
  925. static void loc_setEventProc (Display *display, Window window) {
  926. void* data = (void*)&loc_XEventProc; // swapped the function name here
  927. // (note) 32-bit only
  928. Atom atom = XInternAtom(display, "_XEventProc", False);
  929. XChangeProperty(display, window,
  930. atom/*property*/,
  931. atom/*type*/,
  932. 32/*format*/,
  933. PropModeReplace/*mode*/,
  934. (unsigned char*)&data/*data*/,
  935. 1/*nelements*/
  936. );
  937. }
  938. #endif // ARCH_X64
  939. // ---------------------------------------------------------------------------- lglw_window_open
  940. lglw_bool_t lglw_window_open (lglw_t _lglw, void *_parentHWNDOrNull, int32_t _x, int32_t _y, int32_t _w, int32_t _h) {
  941. lglw_bool_t r = LGLW_FALSE;
  942. LGLW(_lglw);
  943. if(NULL != lglw)
  944. {
  945. Dlog_v("lglw:lglw_window_open: 1, %p, %i \n", (Window)_parentHWNDOrNull, (Window)_parentHWNDOrNull);
  946. lglw->parent_xwnd = (0 == _parentHWNDOrNull) ? DefaultRootWindow(lglw->xdsp) : (Window)_parentHWNDOrNull;
  947. Dlog_v("lglw:lglw_window_open: 2 lglw=%p\n", lglw);
  948. if(_w <= 16)
  949. _w = lglw->hidden.size.x;
  950. Dlog_v("lglw:lglw_window_open: 3\n");
  951. if(_h <= 16)
  952. _h = lglw->hidden.size.y;
  953. // TODO: compare to 'WindowClass' from Windows implementation
  954. Dlog_v("lglw:lglw_window_open: 4\n");
  955. XSetWindowAttributes swa;
  956. // // XEvent event;
  957. XSync(lglw->xdsp, False);
  958. #if 1
  959. Dlog_v("lglw:lglw_window_open: 5\n");
  960. swa.border_pixel = 0;
  961. swa.colormap = lglw->cmap;
  962. // (note) [bsp] setting this to NoEventMask causes all events to be propagated to the parent (host) window.
  963. // The host then reports the event to the plugin by calling its eventProc function (set via "_XEventProc").
  964. swa.event_mask = NoEventMask;/////ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonMotionMask | FocusChangeMask;
  965. lglw->win.xwnd = XCreateWindow(lglw->xdsp/*display*/,
  966. DefaultRootWindow(lglw->xdsp)/*parent. see Cameron's comment below.*/,
  967. 0/*x*/,
  968. 0/*y*/,
  969. _w/*width*/,
  970. _h/*height*/,
  971. 0/*border_width*/,
  972. CopyFromParent/*depth*/,
  973. InputOutput/*class*/,
  974. lglw->vi->visual,
  975. CWBorderPixel | CWColormap | CWEventMask/*value_mask*/,
  976. &swa/*attributes*/
  977. );
  978. Dlog_v("lglw:lglw_window_open: 6\n");
  979. XSetStandardProperties(lglw->xdsp/*display*/,
  980. lglw->win.xwnd/*window*/,
  981. "LGLW"/*window_name*/,
  982. "LGLW"/*icon_name*/,
  983. None/*icon_pixmap*/,
  984. NULL/*argv*/,
  985. 0/*argc*/,
  986. NULL/*XSizeHints*/
  987. );
  988. // Setup the event proc now, on the parent window as well just for the debug host
  989. // It was simpler to do this than check in the debug host for the reparent event
  990. Dlog_v("lglw:lglw_window_open: 7\n");
  991. loc_setEventProc(lglw->xdsp, lglw->win.xwnd);
  992. loc_setProperty(lglw->xdsp, lglw->win.xwnd, "_lglw", (void*)lglw); // set instance pointer
  993. if(0 != _parentHWNDOrNull)
  994. {
  995. loc_setEventProc(lglw->xdsp, lglw->parent_xwnd);
  996. loc_setProperty(lglw->xdsp, lglw->parent_xwnd, "_lglw", (void*)lglw); // set instance pointer
  997. }
  998. // Some hosts only check and store the callback when the Window is reparented
  999. // Since creating the Window with a Parent may or may not do that, but the callback is not set,
  1000. // ... it's created as a root window, the callback is set, and then it's reparented
  1001. #if 1
  1002. // (note) [cameronleger] In Ardour's code-base, the only time it looks for the _XEventProc is during a ReparentNotify event
  1003. if (0 != _parentHWNDOrNull)
  1004. {
  1005. Dlog_v("lglw:lglw_window_open: 8\n");
  1006. XReparentWindow(lglw->xdsp, lglw->win.xwnd, lglw->parent_xwnd, 0, 0);
  1007. }
  1008. #endif
  1009. lglw->win.b_owner = LGLW_TRUE;
  1010. #else
  1011. lglw->win.xwnd = (Window)_parentHWNDOrNull;
  1012. lglw->win.b_owner = LGLW_FALSE;
  1013. #endif
  1014. Dlog_v("lglw:lglw_window_open: 9\n");
  1015. if(lglw->win.b_owner)
  1016. {
  1017. // // XMapRaised(lglw->xdsp, lglw->win.xwnd);
  1018. XMapWindow(lglw->xdsp, lglw->win.xwnd);
  1019. }
  1020. #if 0
  1021. XSelectInput(lglw->xdsp, lglw->win.xwnd,
  1022. ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ButtonMotionMask | FocusChangeMask
  1023. );
  1024. XGrabKeyboard(lglw->xdsp, lglw->win.xwnd,
  1025. False/*owner_events*/,
  1026. GrabModeAsync/*pointer_mode*/,
  1027. GrabModeAsync/*keyboard_mode*/,
  1028. CurrentTime/*time*/
  1029. );
  1030. #endif
  1031. #if 0
  1032. XSetInputFocus(lglw->xdsp/*display*/,
  1033. PointerRoot/*focus*/,
  1034. RevertToPointerRoot/*revert_to*/,
  1035. CurrentTime
  1036. );
  1037. #endif
  1038. XSync(lglw->xdsp, False);
  1039. lglw->win.mapped = LGLW_TRUE;
  1040. Dlog_v("lglw:lglw_window_open: 10\n");
  1041. lglw->win.size.x = _w;
  1042. lglw->win.size.y = _h;
  1043. Dlog_v("lglw:lglw_window_open: 11\n");
  1044. loc_enable_dropfiles(lglw, (NULL != lglw->dropfiles.cbk));
  1045. Dlog_v("lglw:lglw_window_open: EXIT\n");
  1046. r = LGLW_TRUE;
  1047. }
  1048. return r;
  1049. }
  1050. // ---------------------------------------------------------------------------- lglw_window_resize
  1051. lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) {
  1052. lglw_bool_t r = LGLW_FALSE;
  1053. LGLW(_lglw);
  1054. if(NULL != lglw)
  1055. {
  1056. if(0 != lglw->win.xwnd)
  1057. {
  1058. Dlog_v("lglw:lglw_window_resize: 1\n");
  1059. r = LGLW_TRUE;
  1060. Dlog_v("lglw:lglw_window_resize: 2\n");
  1061. XResizeWindow(lglw->xdsp, lglw->win.xwnd, _w, _h);
  1062. XRaiseWindow(lglw->xdsp, lglw->win.xwnd);
  1063. Dlog_v("lglw:lglw_window_resize: 3\n");
  1064. int deltaW = _w - lglw->win.size.x;
  1065. int deltaH = _h - lglw->win.size.y;
  1066. Dlog_v("lglw:lglw_window_resize: 4\n");
  1067. lglw->win.size.x = _w;
  1068. lglw->win.size.y = _h;
  1069. Dlog_v("lglw:lglw_window_resize: 5\n");
  1070. Window root, parent, *children = NULL;
  1071. unsigned int num_children;
  1072. Dlog_v("lglw:lglw_window_resize: 6\n");
  1073. if(!XQueryTree(lglw->xdsp, lglw->win.xwnd, &root, &parent, &children, &num_children))
  1074. return r;
  1075. Dlog_v("lglw:lglw_window_resize: 7\n");
  1076. if(children)
  1077. XFree((char *)children);
  1078. Dlog_v("lglw:lglw_window_resize: 8\n");
  1079. // Resize parent window (if any)
  1080. if(0 != parent)
  1081. {
  1082. Dlog_v("lglw:lglw_window_resize: 8.1\n");
  1083. int x, y;
  1084. unsigned int width, height;
  1085. unsigned int border_width;
  1086. unsigned int depth;
  1087. Dlog_v("lglw:lglw_window_resize: 8.2\n");
  1088. if(!XGetGeometry(lglw->xdsp, lglw->win.xwnd, &root, &x, &y, &width, &height, &border_width, &depth))
  1089. return r;
  1090. Dlog_v("lglw:lglw_window_resize: 8.3\n");
  1091. XResizeWindow(lglw->xdsp, parent, width + deltaW, height + deltaH);
  1092. }
  1093. Dlog_v("lglw:lglw_window_resize: EXIT\n");
  1094. }
  1095. }
  1096. return r;
  1097. }
  1098. // ---------------------------------------------------------------------------- lglw_window_close
  1099. void lglw_window_close (lglw_t _lglw) {
  1100. LGLW(_lglw);
  1101. if(NULL != lglw)
  1102. {
  1103. if(0 != lglw->win.xwnd)
  1104. {
  1105. Dlog_v("lglw:lglw_window_close: 1\n");
  1106. lglw_timer_stop(_lglw);
  1107. Dlog_v("lglw:lglw_window_close: 2\n");
  1108. glXMakeCurrent(lglw->xdsp, None, NULL);
  1109. Dlog_v("lglw:lglw_window_close: 3\n");
  1110. if(lglw->win.b_owner)
  1111. {
  1112. XDestroyWindow(lglw->xdsp, lglw->win.xwnd);
  1113. lglw->win.b_owner = LGLW_FALSE;
  1114. }
  1115. XSync(lglw->xdsp, False);
  1116. lglw->win.xwnd = 0;
  1117. lglw->win.mapped = LGLW_FALSE;
  1118. }
  1119. }
  1120. Dlog_v("lglw:lglw_window_close: EXIT\n");
  1121. }
  1122. // ---------------------------------------------------------------------------- lglw_window_show
  1123. void lglw_window_show(lglw_t _lglw) {
  1124. LGLW(_lglw);
  1125. if(NULL != lglw)
  1126. {
  1127. Dlog_v("lglw:lglw_window_show: 1\n");
  1128. XMapRaised(lglw->xdsp, lglw->win.xwnd);
  1129. lglw->win.mapped = LGLW_TRUE;
  1130. }
  1131. Dlog_v("lglw:lglw_window_show: EXIT\n");
  1132. }
  1133. // ---------------------------------------------------------------------------- lglw_window_hide
  1134. void lglw_window_hide(lglw_t _lglw) {
  1135. LGLW(_lglw);
  1136. if(NULL != lglw)
  1137. {
  1138. Dlog_v("lglw:lglw_window_hide: 1\n");
  1139. XUnmapWindow(lglw->xdsp, lglw->win.xwnd);
  1140. lglw->win.mapped = LGLW_FALSE;
  1141. }
  1142. Dlog_v("lglw:lglw_window_hide: EXIT\n");
  1143. }
  1144. // ---------------------------------------------------------------------------- lglw_window_is_visible
  1145. lglw_bool_t lglw_window_is_visible(lglw_t _lglw) {
  1146. lglw_bool_t r = LGLW_FALSE;
  1147. LGLW(_lglw);
  1148. Dlog_vvv("lglw:lglw_window_is_visible: 1\n");
  1149. if(NULL != lglw && 0 != lglw->win.xwnd)
  1150. {
  1151. Dlog_vvv("lglw:lglw_window_is_visible: 2\n");
  1152. r = lglw->win.mapped;
  1153. }
  1154. Dlog_vvv("lglw:lglw_window_is_visible: EXIT\n");
  1155. return r;
  1156. }
  1157. // ---------------------------------------------------------------------------- lglw_window_size_get
  1158. void lglw_window_size_get(lglw_t _lglw, int32_t *_retX, int32_t *_retY) {
  1159. LGLW(_lglw);
  1160. Dlog_vvv("lglw:lglw_window_size_get: 1\n");
  1161. if(NULL != lglw)
  1162. {
  1163. if(0 != lglw->win.xwnd)
  1164. {
  1165. if(NULL != _retX)
  1166. *_retX = lglw->win.size.x;
  1167. if(NULL != _retY)
  1168. *_retY = lglw->win.size.y;
  1169. }
  1170. }
  1171. Dlog_vvv("lglw:lglw_window_size_get: EXIT\n");
  1172. }
  1173. // ---------------------------------------------------------------------------- lglw_redraw
  1174. void lglw_redraw(lglw_t _lglw) {
  1175. LGLW(_lglw);
  1176. // (todo) implement me
  1177. if(NULL != lglw)
  1178. {
  1179. if(0 != lglw->win.xwnd)
  1180. {
  1181. // TODO Event Loop
  1182. Dlog_vvv("lglw:lglw_redraw: 1\n");
  1183. XEvent xev;
  1184. xev.xany.type = Expose;
  1185. xev.xany.serial = 0;
  1186. xev.xany.send_event = True;
  1187. xev.xany.display = lglw->xdsp;
  1188. xev.xany.window = lglw->win.xwnd;
  1189. xev.xexpose.x = 0;
  1190. xev.xexpose.y = 0;
  1191. xev.xexpose.width = lglw->win.size.x;
  1192. xev.xexpose.height = lglw->win.size.y;
  1193. xev.xexpose.count = 0;
  1194. XSendEvent(lglw->xdsp, lglw->win.xwnd,
  1195. True/*propagate*/,
  1196. ExposureMask/*event_mask*/,
  1197. &xev
  1198. );
  1199. XFlush(lglw->xdsp);
  1200. }
  1201. }
  1202. }
  1203. // ---------------------------------------------------------------------------- lglw_redraw_callback_set
  1204. void lglw_redraw_callback_set(lglw_t _lglw, lglw_redraw_fxn_t _cbk) {
  1205. LGLW(_lglw);
  1206. if(NULL != lglw)
  1207. {
  1208. lglw->redraw.cbk = _cbk;
  1209. }
  1210. }
  1211. // ---------------------------------------------------------------------------- lglw_glcontext_push
  1212. void lglw_glcontext_push(lglw_t _lglw) {
  1213. LGLW(_lglw);
  1214. if(NULL != lglw)
  1215. {
  1216. lglw->prev.drw = glXGetCurrentDrawable();
  1217. lglw->prev.ctx = glXGetCurrentContext();
  1218. Dlog_vvv("lglw:lglw_glcontext_push: win.xwnd=%p hidden.xwnd=%p ctx=%p\n",
  1219. lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx);
  1220. if(!glXMakeCurrent(lglw->xdsp, (0 == lglw->win.xwnd) ? lglw->hidden.xwnd : lglw->win.xwnd, lglw->ctx))
  1221. {
  1222. 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());
  1223. }
  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. ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | ButtonMotionMask | KeymapStateMask/*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. ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | PointerMotionHintMask | ButtonMotionMask | KeymapStateMask/*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. #if 0
  1751. //int numEv = XEventsQueued(lglw->xdsp, QueuedAlready);
  1752. int numEv = XEventsQueued(lglw->xdsp, QueuedAfterReading);
  1753. // => numEv is always 0
  1754. int evIdx = 0;
  1755. Dlog_v("xxx lglw_events: numEv=%d\n", numEv);
  1756. //for(; evIdx < numEv; evIdx++)
  1757. for(;;)
  1758. {
  1759. XEvent xev;
  1760. XNextEvent(lglw->xdsp, &xev);
  1761. Dlog_v("xxx lglw_events: XNextEvent[%d]\n", evIdx++);
  1762. loc_eventProc(&xev, lglw);
  1763. }
  1764. #else
  1765. // // int evIdx = 0;
  1766. // // for(;;)
  1767. // // {
  1768. // // XEvent xev;
  1769. // // // => blocks forever
  1770. // // XNextEvent(lglw->xdsp, &xev);
  1771. // // Dlog_v("xxx XNextEvent[%d]\n", evIdx++);
  1772. // // }
  1773. loc_process_timer(lglw);
  1774. #endif
  1775. }
  1776. }
  1777. }