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.

1779 lines
57KB

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