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.

2157 lines
69KB

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