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.

1159 lines
34KB

  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. #define Dprintf if(0);else printf
  41. // #define Dprintf if(1);else printf
  42. // ---------------------------------------------------------------------------- macros and defines
  43. #define LGLW(a) lglw_int_t *lglw = ((lglw_int_t*)(a))
  44. #define LGLW_DEFAULT_HIDDEN_W (800)
  45. #define LGLW_DEFAULT_HIDDEN_H (600)
  46. #define LGLW_MOUSE_TOUCH_LMB_TIMEOUT (250u)
  47. #define LGLW_MOUSE_TOUCH_RMB_TIMEOUT (500u)
  48. #define LGLW_MOUSE_TOUCH_RMB_STATE_IDLE (0u)
  49. #define LGLW_MOUSE_TOUCH_RMB_STATE_LMB (1u)
  50. #define LGLW_MOUSE_TOUCH_RMB_STATE_WAIT (2u)
  51. #define LGLW_MOUSE_TOUCH_RMB_STATE_RMB (3u)
  52. #define LGLW_MOUSE_TOUCH_RMB_MOVE_THRESHOLD (7u)
  53. #define sABS(x) (((x)>0)?(x):-(x))
  54. // ---------------------------------------------------------------------------- structs and typedefs
  55. typedef struct lglw_int_s {
  56. void *user_data; // arbitrary user data
  57. Display *xdsp;
  58. XVisualInfo *vi;
  59. Colormap cmap;
  60. Window parent_xwnd; // created by host
  61. struct {
  62. lglw_vec2i_t size;
  63. Window xwnd;
  64. } hidden;
  65. struct {
  66. lglw_vec2i_t size;
  67. Window xwnd;
  68. lglw_bool_t mapped;
  69. int32_t swap_interval;
  70. } win;
  71. GLXContext ctx;
  72. struct {
  73. GLXContext ctx;
  74. GLXDrawable drw;
  75. } prev;
  76. struct {
  77. uint32_t kmod_state; // See LGLW_KMOD_xxx
  78. lglw_keyboard_fxn_t cbk;
  79. } keyboard;
  80. struct {
  81. lglw_vec2i_t p; // last seen mouse position
  82. uint32_t button_state;
  83. lglw_mouse_fxn_t cbk;
  84. struct {
  85. uint32_t mode;
  86. lglw_vec2i_t p; // grab-start mouse position
  87. lglw_bool_t b_queue_warp;
  88. lglw_vec2i_t last_p;
  89. } grab;
  90. struct {
  91. lglw_bool_t b_enable;
  92. lglw_bool_t b_update_queued;
  93. lglw_bool_t b_syn_rmb;
  94. uint32_t syn_rmb_hold_state; // see LGLW_MOUSE_TOUCH_RMB_STATE_xxx
  95. uint32_t hold_start_ms;
  96. lglw_vec2i_t hold_start_p;
  97. } touch;
  98. } mouse;
  99. struct {
  100. uint32_t state;
  101. lglw_focus_fxn_t cbk;
  102. } focus;
  103. struct {
  104. lglw_bool_t b_running;
  105. lglw_timer_fxn_t cbk;
  106. } timer;
  107. struct {
  108. lglw_dropfiles_fxn_t cbk;
  109. } dropfiles;
  110. struct {
  111. lglw_redraw_fxn_t cbk;
  112. } redraw;
  113. } lglw_int_t;
  114. // ---------------------------------------------------------------------------- module fxn fwd decls
  115. static lglw_bool_t loc_create_hidden_window (lglw_int_t *lglw, int32_t _w, int32_t _h);
  116. static void loc_destroy_hidden_window(lglw_int_t *lglw);
  117. static void loc_key_hook(lglw_int_t *lglw);
  118. static void loc_key_unhook(lglw_int_t *lglw);
  119. static lglw_bool_t loc_handle_key (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey);
  120. // static lglw_bool_t loc_touchkeyboard_get_rect (RECT *rect);
  121. // static lglw_bool_t loc_touchkeyboard_is_visible (void);
  122. extern lglw_bool_t lglw_int_touchkeyboard_toggle (void);
  123. static void loc_handle_mouseleave (lglw_int_t *lglw);
  124. static void loc_handle_mouseenter (lglw_int_t *lglw);
  125. static void loc_handle_mousebutton (lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button);
  126. static void loc_handle_mousemotion (lglw_int_t *lglw);
  127. static void loc_handle_queued_mouse_warp (lglw_int_t *lglw);
  128. static void loc_touchinput_update (lglw_int_t *lglw);
  129. static void loc_enable_dropfiles (lglw_int_t *lglw, lglw_bool_t _bEnable);
  130. // ---------------------------------------------------------------------------- module vars
  131. static lglw_int_t *khook_lglw = NULL; // currently key-hooked lglw instance (one at a time)
  132. // TODO: remove and/or improve debug logging for a debug build
  133. // ---------------------------------------------------------------------------- lglw_log
  134. static FILE *logfile;
  135. void lglw_log(const char *logData, ...) {
  136. fprintf(logfile, logData);
  137. fflush(logfile);
  138. }
  139. // TODO: remove, or maybe not in some specific use cases
  140. // ---------------------------------------------------------------------------- xerror_log
  141. static int xerror_handler(Display *display, XErrorEvent *error) {
  142. char error_text[1024];
  143. XGetErrorText(display, error->error_code, error_text, 1024);
  144. lglw_log("XERROR (%d): %s, %d, %d\n", error->error_code, error_text, error->request_code, error->minor_code);
  145. return 0;
  146. }
  147. // ---------------------------------------------------------------------------- lglw_init
  148. lglw_t lglw_init(int32_t _w, int32_t _h) {
  149. lglw_int_t *lglw = malloc(sizeof(lglw_int_t));
  150. // TODO: remove/improve
  151. logfile = fopen("/tmp/lglw_log.txt", "w");
  152. XSetErrorHandler(xerror_handler);
  153. XInitThreads(); // fix GL crash, see <https://forum.juce.com/t/linux-vst-opengl-crash-because-xinitthreads-not-called/22821>
  154. if(NULL != lglw)
  155. {
  156. memset(lglw, 0, sizeof(lglw_int_t));
  157. lglw_log("lglw:lglw_init: 1\n");
  158. if(_w <= 16)
  159. _w = LGLW_DEFAULT_HIDDEN_W;
  160. if(_h <= 16)
  161. _h = LGLW_DEFAULT_HIDDEN_H;
  162. lglw_log("lglw:lglw_init: 2\n");
  163. if(!loc_create_hidden_window(lglw, _w, _h))
  164. {
  165. free(lglw);
  166. lglw = NULL;
  167. }
  168. lglw_log("lglw:lglw_init: 3\n");
  169. }
  170. lglw_log("lglw:lglw_init: EXIT\n");
  171. return lglw;
  172. }
  173. // ---------------------------------------------------------------------------- lglw_exit
  174. void lglw_exit(lglw_t _lglw) {
  175. LGLW(_lglw);
  176. if(NULL != lglw)
  177. {
  178. lglw_log("lglw:lglw_exit: 1\n");
  179. loc_destroy_hidden_window(lglw);
  180. lglw_log("lglw:lglw_exit: 2\n");
  181. fclose(logfile);
  182. free(lglw);
  183. }
  184. }
  185. // ---------------------------------------------------------------------------- lglw_userdata_set
  186. void lglw_userdata_set(lglw_t _lglw, void *_userData) {
  187. LGLW(_lglw);
  188. if(NULL != lglw)
  189. {
  190. lglw_log("lglw:lglw_userdata_set: 1\n");
  191. lglw->user_data = _userData;
  192. }
  193. }
  194. // ---------------------------------------------------------------------------- lglw_userdata_get
  195. void *lglw_userdata_get(lglw_t _lglw) {
  196. LGLW(_lglw);
  197. if(NULL != lglw)
  198. {
  199. lglw_log("lglw:lglw_userdata_get: 1\n");
  200. return lglw->user_data;
  201. }
  202. return NULL;
  203. }
  204. // ---------------------------------------------------------------------------- loc_create_hidden_window
  205. static lglw_bool_t loc_create_hidden_window(lglw_int_t *lglw, int32_t _w, int32_t _h) {
  206. // TODO: compare to 'WindowClass' from Windows implementation
  207. lglw_log("lglw:loc_create_hidden_window: 1\n");
  208. XSetWindowAttributes swa;
  209. int attrib[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, None };
  210. int screen;
  211. lglw_log("lglw:loc_create_hidden_window: 2\n");
  212. lglw->xdsp = XOpenDisplay(NULL);
  213. screen = DefaultScreen(lglw->xdsp);
  214. lglw_log("lglw:loc_create_hidden_window: 3\n");
  215. lglw->vi = glXChooseVisual(lglw->xdsp, screen, attrib);
  216. lglw_log("lglw:loc_create_hidden_window: 4\n");
  217. if(NULL == lglw->vi)
  218. {
  219. lglw_log("[---] lglw: failed to find GLX Visual for hidden window\n");
  220. return LGLW_FALSE;
  221. }
  222. lglw_log("lglw:loc_create_hidden_window: 5\n");
  223. lglw->ctx = glXCreateContext(lglw->xdsp, lglw->vi, None, True);
  224. lglw_log("lglw:loc_create_hidden_window: 6\n");
  225. if(NULL == lglw->ctx)
  226. {
  227. lglw_log("[---] lglw: failed to create GLX Context for hidden window\n");
  228. return LGLW_FALSE;
  229. }
  230. lglw_log("lglw:loc_create_hidden_window: 7\n");
  231. lglw->cmap = XCreateColormap(lglw->xdsp, RootWindow(lglw->xdsp, lglw->vi->screen),
  232. lglw->vi->visual, AllocNone);
  233. lglw_log("lglw:loc_create_hidden_window: 8\n");
  234. swa.border_pixel = 0;
  235. swa.colormap = lglw->cmap;
  236. lglw->hidden.xwnd = XCreateWindow(lglw->xdsp, DefaultRootWindow(lglw->xdsp),
  237. 0, 0, LGLW_DEFAULT_HIDDEN_W, LGLW_DEFAULT_HIDDEN_H, 0, CopyFromParent, InputOutput,
  238. lglw->vi->visual, CWBorderPixel | CWColormap, &swa);
  239. lglw_log("lglw:loc_create_hidden_window: 9\n");
  240. XSetStandardProperties(lglw->xdsp, lglw->hidden.xwnd, "LGLW_hidden", "LGLW_hidden", None, NULL, 0, NULL);
  241. XSync(lglw->xdsp, False);
  242. lglw_log("lglw:loc_create_hidden_window: EXIT\n");
  243. lglw->hidden.size.x = _w;
  244. lglw->hidden.size.y = _h;
  245. return LGLW_TRUE;
  246. }
  247. // ---------------------------------------------------------------------------- loc_destroy_hidden_window
  248. static void loc_destroy_hidden_window(lglw_int_t *lglw) {
  249. lglw_log("lglw:loc_destroy_hidden_window: 1\n");
  250. if(NULL != lglw->xdsp && NULL != lglw->ctx)
  251. {
  252. glXMakeCurrent(lglw->xdsp, None, NULL);
  253. glXDestroyContext(lglw->xdsp, lglw->ctx);
  254. }
  255. lglw_log("lglw:loc_destroy_hidden_window: 2\n");
  256. if(NULL != lglw->xdsp && 0 != lglw->hidden.xwnd) XDestroyWindow(lglw->xdsp, lglw->hidden.xwnd);
  257. lglw_log("lglw:loc_destroy_hidden_window: 3\n");
  258. if(NULL != lglw->xdsp && 0 != lglw->cmap) XFreeColormap(lglw->xdsp, lglw->cmap);
  259. lglw_log("lglw:loc_destroy_hidden_window: 4\n");
  260. if(NULL != lglw->vi) XFree(lglw->vi);
  261. lglw_log("lglw:loc_destroy_hidden_window: 5\n");
  262. XSync(lglw->xdsp, False);
  263. if(NULL != lglw->xdsp) XCloseDisplay(lglw->xdsp);
  264. }
  265. // ---------------------------------------------------------------------------- loc_setEventProc
  266. // https://www.kvraudio.com/forum/viewtopic.php?t=387924
  267. // https://github.com/Ardour/ardour/blob/master/gtk2_ardour/linux_vst_gui_support.cc
  268. // https://discourse.ardour.org/t/overtonedsp-plugins/90115/22
  269. // https://github.com/amsynth/amsynth/blob/4a87798e650c6d71d70274a961c9b8d98fc6da7e/src/amsynth_vst.cpp
  270. // https://github.com/rsenn/eXT2/blob/7f00a09561ded8175ffed2f4912dad74e466a1c7/vstplugins/vstgui/vstgui.cpp
  271. // https://github.com/COx2/DistortionFilter/blob/c6a34fb56b503a6e95bf0975e00f438bbf4ff52a/juce/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  272. // Very simple function to test _XEventProc is properly called
  273. void loc_eventProc(void *xevent) {
  274. // lglw_log("XEventProc");
  275. printf("XEventProc\n");
  276. }
  277. // Pulled from the Renoise 64-bit callback example
  278. // Unsure what data was supposed to be, but swapping it to a function name did not work
  279. // This does nothing, no event proc found
  280. // TODO: 32-bit support
  281. // void loc_setEventProc (Display *display, Window window) {
  282. // size_t data = (size_t)loc_eventProc;
  283. // long temp[2];
  284. // // Split the 64 bit pointer into a little-endian long array
  285. // temp[0] = (long)(data & 0xffffffffUL);
  286. // temp[1] = (long)(data >> 32L);
  287. // Atom atom = XInternAtom(display, "_XEventProc", False);
  288. // XChangeProperty(display, window, atom, atom, 32,
  289. // PropModeReplace, (unsigned char*)temp, 2);
  290. // }
  291. // Pulled from the eXT2 example
  292. // TODO: 32-bit support
  293. void loc_setEventProc (Display *display, Window window) {
  294. void* data = (void*)&loc_eventProc; // swapped the function name here
  295. Atom atom = XInternAtom(display, "_XEventProc", False);
  296. XChangeProperty(display, window, atom, atom, 32,
  297. PropModeReplace, (unsigned char*)&data, 1); // 1 instead of 2 will crash Ardour, 2 will not do anything
  298. }
  299. // Pulled from the amsynth example
  300. // Simply swapped out the function names, crashes Ardour in the same was as the others
  301. // void loc_setEventProc (Display *display, Window window) {
  302. // #ifdef BUILD_64
  303. // //
  304. // // JUCE calls XGetWindowProperty with long_length = 1 which means it only fetches the lower 32 bits of the address.
  305. // // Therefore we need to ensure we return an address in the lower 32-bits of address space.
  306. // //
  307. // // based on mach_override
  308. // static const unsigned char kJumpInstructions[] = {
  309. // 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00,
  310. // 0x00, 0x00, 0x00, 0x00,
  311. // 0x00, 0x00, 0x00, 0x00
  312. // };
  313. // static const int kJumpAddress = 6;
  314. // static char *ptr;
  315. // if (!ptr) {
  316. // ptr = (char *)mmap(0,
  317. // PAGE_SIZE,
  318. // PROT_READ | PROT_WRITE | PROT_EXEC,
  319. // MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT,
  320. // 0, 0);
  321. // if (ptr == MAP_FAILED) {
  322. // perror("mmap");
  323. // ptr = 0;
  324. // return;
  325. // } else {
  326. // memcpy(ptr, kJumpInstructions, sizeof(kJumpInstructions));
  327. // *((uint64_t *)(ptr + kJumpAddress)) = (uint64_t)(&loc_eventProc);
  328. // msync(ptr, sizeof(kJumpInstructions), MS_INVALIDATE);
  329. // }
  330. // }
  331. // long temp[2] = {(long)ptr, 0};
  332. // Atom atom = XInternAtom(display, "_XEventProc", False);
  333. // XChangeProperty(display, window, atom, atom, 32, PropModeReplace, (unsigned char *)temp, 2);
  334. // #else
  335. // long temp[1] = {(long)(void *)(&loc_eventProc)};
  336. // Atom atom = XInternAtom(display, "_XEventProc", False);
  337. // XChangeProperty(display, window, atom, atom, 32, PropModeReplace, (unsigned char *)temp, 1);
  338. // #endif
  339. // }
  340. // ---------------------------------------------------------------------------- lglw_window_open
  341. lglw_bool_t lglw_window_open (lglw_t _lglw, void *_parentHWNDOrNull, int32_t _x, int32_t _y, int32_t _w, int32_t _h) {
  342. lglw_bool_t r = LGLW_FALSE;
  343. LGLW(_lglw);
  344. if(NULL != lglw)
  345. {
  346. lglw_log("lglw:lglw_window_open: 1, %p, %i\n", (Window)_parentHWNDOrNull, (Window)_parentHWNDOrNull);
  347. lglw->parent_xwnd = (0 == _parentHWNDOrNull) ? DefaultRootWindow(lglw->xdsp) : (Window)_parentHWNDOrNull;
  348. lglw_log("lglw:lglw_window_open: 2\n");
  349. if(_w <= 16)
  350. _w = lglw->hidden.size.x;
  351. lglw_log("lglw:lglw_window_open: 3\n");
  352. if(_h <= 16)
  353. _h = lglw->hidden.size.y;
  354. // TODO: compare to 'WindowClass' from Windows implementation
  355. lglw_log("lglw:lglw_window_open: 4\n");
  356. XSetWindowAttributes swa;
  357. XEvent event;
  358. XSync(lglw->xdsp, False);
  359. lglw_log("lglw:lglw_window_open: 5\n");
  360. swa.border_pixel = 0;
  361. swa.colormap = lglw->cmap;
  362. swa.event_mask = EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask;
  363. lglw->win.xwnd = XCreateWindow(lglw->xdsp, DefaultRootWindow(lglw->xdsp),
  364. 0, 0, _w, _h, 0, CopyFromParent, InputOutput,
  365. lglw->vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa);
  366. lglw_log("lglw:lglw_window_open: 6\n");
  367. XSetStandardProperties(lglw->xdsp, lglw->win.xwnd, "LGLW", "LGLW", None, NULL, 0, NULL);
  368. // Setup the event proc now, on the parent window as well just for the debug host
  369. // It was simpler to do this than check in the debug host for the reparent event
  370. lglw_log("lglw:lglw_window_open: 7\n");
  371. loc_setEventProc(lglw->xdsp, lglw->win.xwnd);
  372. loc_setEventProc(lglw->xdsp, lglw->parent_xwnd);
  373. // Some hosts only check and store the callback when the Window is reparented
  374. // Since creating the Window with a Parent may or may not do that, but the callback is not set,
  375. // ... it's created as a root window, the callback is set, and then it's reparented
  376. if (0 != _parentHWNDOrNull)
  377. {
  378. lglw_log("lglw:lglw_window_open: 8\n");
  379. XReparentWindow(lglw->xdsp, lglw->win.xwnd, lglw->parent_xwnd, 0, 0);
  380. }
  381. lglw_log("lglw:lglw_window_open: 9\n");
  382. XMapRaised(lglw->xdsp, lglw->win.xwnd);
  383. XSync(lglw->xdsp, False);
  384. lglw->win.mapped = LGLW_TRUE;
  385. lglw_log("lglw:lglw_window_open: 10\n");
  386. lglw->win.size.x = _w;
  387. lglw->win.size.y = _h;
  388. lglw_log("lglw:lglw_window_open: 11\n");
  389. loc_enable_dropfiles(lglw, (NULL != lglw->dropfiles.cbk));
  390. lglw_log("lglw:lglw_window_open: EXIT\n");
  391. }
  392. return r;
  393. }
  394. // ---------------------------------------------------------------------------- lglw_window_resize
  395. lglw_bool_t lglw_window_resize (lglw_t _lglw, int32_t _w, int32_t _h) {
  396. lglw_bool_t r = LGLW_FALSE;
  397. LGLW(_lglw);
  398. if(NULL != lglw)
  399. {
  400. if(0 != lglw->win.xwnd)
  401. {
  402. lglw_log("lglw:lglw_window_resize: 1\n");
  403. r = LGLW_TRUE;
  404. lglw_log("lglw:lglw_window_resize: 2\n");
  405. XResizeWindow(lglw->xdsp, lglw->win.xwnd, _w, _h);
  406. XRaiseWindow(lglw->xdsp, lglw->win.xwnd);
  407. lglw_log("lglw:lglw_window_resize: 3\n");
  408. int deltaW = _w - lglw->win.size.x;
  409. int deltaH = _h - lglw->win.size.y;
  410. lglw_log("lglw:lglw_window_resize: 4\n");
  411. lglw->win.size.x = _w;
  412. lglw->win.size.y = _h;
  413. lglw_log("lglw:lglw_window_resize: 5\n");
  414. Window root, parent, *children = NULL;
  415. unsigned int num_children;
  416. lglw_log("lglw:lglw_window_resize: 6\n");
  417. if(!XQueryTree(lglw->xdsp, lglw->win.xwnd, &root, &parent, &children, &num_children))
  418. return r;
  419. lglw_log("lglw:lglw_window_resize: 7\n");
  420. if(children)
  421. XFree((char *)children);
  422. lglw_log("lglw:lglw_window_resize: 8\n");
  423. // Resize parent window (if any)
  424. if(0 != parent)
  425. {
  426. lglw_log("lglw:lglw_window_resize: 8.1\n");
  427. int x, y;
  428. unsigned int width, height;
  429. unsigned int border_width;
  430. unsigned int depth;
  431. lglw_log("lglw:lglw_window_resize: 8.2\n");
  432. if(!XGetGeometry(lglw->xdsp, lglw->win.xwnd, &root, &x, &y, &width, &height, &border_width, &depth))
  433. return r;
  434. lglw_log("lglw:lglw_window_resize: 8.3\n");
  435. XResizeWindow(lglw->xdsp, parent, width + deltaW, height + deltaH);
  436. }
  437. lglw_log("lglw:lglw_window_resize: EXIT\n");
  438. }
  439. }
  440. return r;
  441. }
  442. // ---------------------------------------------------------------------------- lglw_window_close
  443. void lglw_window_close (lglw_t _lglw) {
  444. LGLW(_lglw);
  445. if(NULL != lglw)
  446. {
  447. if(0 != lglw->win.xwnd)
  448. {
  449. lglw_log("lglw:lglw_window_close: 1\n");
  450. lglw_timer_stop(_lglw);
  451. lglw_log("lglw:lglw_window_close: 2\n");
  452. loc_key_unhook(lglw);
  453. lglw_log("lglw:lglw_window_close: 3\n");
  454. glXMakeCurrent(lglw->xdsp, None, NULL);
  455. lglw_log("lglw:lglw_window_close: 4\n");
  456. XDestroyWindow(lglw->xdsp, lglw->win.xwnd);
  457. XSync(lglw->xdsp, False);
  458. lglw->win.xwnd = 0;
  459. lglw->win.mapped = LGLW_FALSE;
  460. }
  461. }
  462. lglw_log("lglw:lglw_window_close: EXIT\n");
  463. }
  464. // ---------------------------------------------------------------------------- lglw_window_show
  465. void lglw_window_show(lglw_t _lglw) {
  466. LGLW(_lglw);
  467. if(NULL != lglw)
  468. {
  469. lglw_log("lglw:lglw_window_show: 1\n");
  470. XMapRaised(lglw->xdsp, lglw->win.xwnd);
  471. lglw->win.mapped = LGLW_TRUE;
  472. }
  473. lglw_log("lglw:lglw_window_show: EXIT\n");
  474. }
  475. // ---------------------------------------------------------------------------- lglw_window_hide
  476. void lglw_window_hide(lglw_t _lglw) {
  477. LGLW(_lglw);
  478. if(NULL != lglw)
  479. {
  480. lglw_log("lglw:lglw_window_hide: 1\n");
  481. XUnmapWindow(lglw->xdsp, lglw->win.xwnd);
  482. lglw->win.mapped = LGLW_FALSE;
  483. }
  484. lglw_log("lglw:lglw_window_hide: EXIT\n");
  485. }
  486. // ---------------------------------------------------------------------------- lglw_window_is_visible
  487. lglw_bool_t lglw_window_is_visible(lglw_t _lglw) {
  488. lglw_bool_t r = LGLW_FALSE;
  489. LGLW(_lglw);
  490. // lglw_log("lglw:lglw_window_is_visible: 1\n");
  491. if(NULL != lglw && 0 != lglw->win.xwnd)
  492. {
  493. // lglw_log("lglw:lglw_window_is_visible: 2\n");
  494. r = lglw->win.mapped;
  495. }
  496. // lglw_log("lglw:lglw_window_is_visible: EXIT\n");
  497. return r;
  498. }
  499. // ---------------------------------------------------------------------------- lglw_window_size_get
  500. void lglw_window_size_get(lglw_t _lglw, int32_t *_retX, int32_t *_retY) {
  501. LGLW(_lglw);
  502. lglw_log("lglw:lglw_window_size_get: 1\n");
  503. if(NULL != lglw)
  504. {
  505. if(0 != lglw->win.xwnd)
  506. {
  507. if(NULL != _retX)
  508. *_retX = lglw->win.size.x;
  509. if(NULL != _retY)
  510. *_retY = lglw->win.size.y;
  511. }
  512. }
  513. lglw_log("lglw:lglw_window_size_get: EXIT\n");
  514. }
  515. // ---------------------------------------------------------------------------- lglw_redraw
  516. void lglw_redraw(lglw_t _lglw) {
  517. LGLW(_lglw);
  518. // (todo) implement me
  519. if(NULL != lglw)
  520. {
  521. if(0 != lglw->win.xwnd)
  522. {
  523. // TODO Event Loop
  524. lglw_log("lglw:lglw_redraw: 1\n");
  525. XClearArea(lglw->xdsp, lglw->win.xwnd, 0, 0, 1, 1, True); // clear tiny area for exposing
  526. XFlush(lglw->xdsp);
  527. }
  528. }
  529. }
  530. // ---------------------------------------------------------------------------- lglw_redraw_callback_set
  531. void lglw_redraw_callback_set(lglw_t _lglw, lglw_redraw_fxn_t _cbk) {
  532. LGLW(_lglw);
  533. if(NULL != lglw)
  534. {
  535. lglw->redraw.cbk = _cbk;
  536. }
  537. }
  538. // ---------------------------------------------------------------------------- lglw_glcontext_push
  539. void lglw_glcontext_push(lglw_t _lglw) {
  540. LGLW(_lglw);
  541. if(NULL != lglw)
  542. {
  543. lglw->prev.drw = glXGetCurrentDrawable();
  544. lglw->prev.ctx = glXGetCurrentContext();
  545. // lglw_log("lglw:lglw_glcontext_push: win.xwnd=%p hidden.xwnd=%p ctx=%p\n",
  546. // lglw->win.xwnd, lglw->hidden.xwnd, lglw->ctx);
  547. if(!glXMakeCurrent(lglw->xdsp, (0 == lglw->win.xwnd) ? lglw->hidden.xwnd : lglw->win.xwnd, lglw->ctx))
  548. {
  549. 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());
  550. }
  551. }
  552. }
  553. // ---------------------------------------------------------------------------- lglw_glcontext_pop
  554. void lglw_glcontext_pop(lglw_t _lglw) {
  555. LGLW(_lglw);
  556. if(NULL != lglw)
  557. {
  558. // lglw_log("lglw:lglw_glcontext_pop: prev.drw=%p prev.ctx=%p\n",
  559. // lglw->prev.drw, lglw->prev.ctx);
  560. if(!glXMakeCurrent(lglw->xdsp, lglw->prev.drw, lglw->prev.ctx))
  561. {
  562. lglw_log("[---] lglw_glcontext_pop: glXMakeCurrent() failed. prev.drw=%p ctx=%p glGetError()=%d\n", lglw->prev.drw, lglw->prev.ctx, glGetError());
  563. }
  564. }
  565. }
  566. // ---------------------------------------------------------------------------- lglw_swap_buffers
  567. void lglw_swap_buffers(lglw_t _lglw) {
  568. LGLW(_lglw);
  569. if(NULL != lglw)
  570. {
  571. if(0 != lglw->win.xwnd)
  572. {
  573. // lglw_log("lglw:lglw_swap_buffers: 1\n");
  574. glXSwapBuffers(lglw->xdsp, lglw->win.xwnd);
  575. }
  576. }
  577. }
  578. // ---------------------------------------------------------------------------- lglw_swap_interval_set
  579. typedef void (APIENTRY *PFNWGLEXTSWAPINTERVALPROC) (int);
  580. void lglw_swap_interval_set(lglw_t _lglw, int32_t _ival) {
  581. LGLW(_lglw);
  582. if(NULL != lglw)
  583. {
  584. lglw_log("lglw:lglw_swap_interval_set: 1\n");
  585. PFNWGLEXTSWAPINTERVALPROC glXSwapIntervalEXT;
  586. glXSwapIntervalEXT = (PFNWGLEXTSWAPINTERVALPROC) glXGetProcAddress("glXSwapIntervalEXT");
  587. if(NULL != glXSwapIntervalEXT)
  588. {
  589. lglw_log("lglw:lglw_swap_interval_set: 2\n");
  590. glXSwapIntervalEXT(_ival);
  591. lglw->win.swap_interval = _ival;
  592. }
  593. }
  594. }
  595. // ---------------------------------------------------------------------------- lglw_swap_interval_get
  596. int32_t lglw_swap_interval_get(lglw_t _lglw) {
  597. LGLW(_lglw);
  598. int32_t r = 0;
  599. if(NULL != lglw)
  600. {
  601. r = lglw->win.swap_interval;
  602. }
  603. return r;
  604. }
  605. // ---------------------------------------------------------------------------- loc_key_hook
  606. static void loc_key_hook(lglw_int_t *lglw) {
  607. loc_key_unhook(lglw);
  608. // (todo) implement me
  609. khook_lglw = lglw;
  610. }
  611. // ---------------------------------------------------------------------------- loc_key_unhook
  612. static void loc_key_unhook(lglw_int_t *lglw) {
  613. // (todo) implement me
  614. if(khook_lglw == lglw)
  615. khook_lglw = NULL;
  616. }
  617. // ---------------------------------------------------------------------------- loc_handle_mouseleave
  618. static void loc_handle_mouseleave(lglw_int_t *lglw) {
  619. loc_key_unhook(lglw);
  620. lglw->focus.state &= ~LGLW_FOCUS_MOUSE;
  621. if(NULL != lglw->focus.cbk)
  622. {
  623. lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE);
  624. }
  625. lglw_log("xxx lglw:loc_handle_mouseleave: LEAVE\n");
  626. }
  627. // ---------------------------------------------------------------------------- loc_handle_mouseenter
  628. static void loc_handle_mouseenter(lglw_int_t *lglw) {
  629. loc_key_hook(lglw);
  630. lglw->focus.state |= LGLW_FOCUS_MOUSE;
  631. if(NULL != lglw->focus.cbk)
  632. {
  633. lglw->focus.cbk(lglw, lglw->focus.state, LGLW_FOCUS_MOUSE);
  634. }
  635. lglw_log("xxx lglw:loc_handle_mouseenter: LEAVE\n");
  636. }
  637. // ---------------------------------------------------------------------------- loc_handle_mousebutton
  638. static void loc_handle_mousebutton(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _button) {
  639. if(_bPressed)
  640. lglw->mouse.button_state |= _button;
  641. else
  642. lglw->mouse.button_state &= ~_button;
  643. if(NULL != lglw->mouse.cbk)
  644. {
  645. lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, _button);
  646. }
  647. }
  648. // ---------------------------------------------------------------------------- loc_handle_mousemotion
  649. static void loc_handle_mousemotion(lglw_int_t *lglw) {
  650. if(NULL != lglw->mouse.cbk)
  651. {
  652. lglw->mouse.cbk(lglw, lglw->mouse.p.x, lglw->mouse.p.y, lglw->mouse.button_state, 0u/*changedbuttonstate*/);
  653. }
  654. }
  655. // ---------------------------------------------------------------------------- lglw_mouse_callback_set
  656. void lglw_mouse_callback_set(lglw_t _lglw, lglw_mouse_fxn_t _cbk) {
  657. LGLW(_lglw);
  658. if(NULL != lglw)
  659. {
  660. lglw->mouse.cbk = _cbk;
  661. }
  662. }
  663. // ---------------------------------------------------------------------------- lglw_mouse_callback_set
  664. void lglw_focus_callback_set(lglw_t _lglw, lglw_focus_fxn_t _cbk) {
  665. LGLW(_lglw);
  666. if(NULL != lglw)
  667. {
  668. lglw->focus.cbk = _cbk;
  669. }
  670. }
  671. // ---------------------------------------------------------------------------- loc_handle_key
  672. static lglw_bool_t loc_handle_key(lglw_int_t *lglw, lglw_bool_t _bPressed, uint32_t _vkey) {
  673. lglw_bool_t r = LGLW_FALSE;
  674. if(NULL != lglw->keyboard.cbk)
  675. {
  676. r = lglw->keyboard.cbk(lglw, _vkey, lglw->keyboard.kmod_state, _bPressed);
  677. }
  678. return r;
  679. }
  680. // ---------------------------------------------------------------------------- lglw_keyboard_callback_set
  681. void lglw_keyboard_callback_set(lglw_t _lglw, lglw_keyboard_fxn_t _cbk) {
  682. LGLW(_lglw);
  683. if(NULL != lglw)
  684. {
  685. lglw->keyboard.cbk = _cbk;
  686. }
  687. }
  688. // ---------------------------------------------------------------------------- lglw_keyboard_get_modifiers
  689. uint32_t lglw_keyboard_get_modifiers(lglw_t _lglw) {
  690. uint32_t r = 0u;
  691. LGLW(_lglw);
  692. if(NULL != lglw)
  693. {
  694. r = lglw->keyboard.kmod_state;
  695. }
  696. return r;
  697. }
  698. // ---------------------------------------------------------------------------- lglw_touchkeyboard_show
  699. void lglw_touchkeyboard_show(lglw_t _lglw, lglw_bool_t _bEnable) {
  700. LGLW(_lglw);
  701. // (todo) implement me
  702. if(NULL != lglw)
  703. {
  704. }
  705. }
  706. // ---------------------------------------------------------------------------- lglw_mouse_get_buttons
  707. uint32_t lglw_mouse_get_buttons(lglw_t _lglw) {
  708. uint32_t r = 0u;
  709. LGLW(_lglw);
  710. if(NULL != lglw)
  711. {
  712. r = lglw->mouse.button_state;
  713. }
  714. return r;
  715. }
  716. // ---------------------------------------------------------------------------- lglw_mouse_grab
  717. void lglw_mouse_grab(lglw_t _lglw, uint32_t _grabMode) {
  718. LGLW(_lglw);
  719. if(NULL != lglw)
  720. {
  721. // (todo) implement me
  722. // if(NULL != lglw->win.hwnd)
  723. {
  724. if(!lglw->mouse.touch.b_enable)
  725. {
  726. if(lglw->mouse.grab.mode != _grabMode)
  727. {
  728. lglw_mouse_ungrab(_lglw);
  729. }
  730. switch(_grabMode)
  731. {
  732. default:
  733. case LGLW_MOUSE_GRAB_NONE:
  734. break;
  735. case LGLW_MOUSE_GRAB_CAPTURE:
  736. // (todo) implement me
  737. // (void)SetCapture(lglw->win.hwnd);
  738. lglw->mouse.grab.mode = _grabMode;
  739. break;
  740. case LGLW_MOUSE_GRAB_WARP:
  741. // (todo) implement me
  742. // (void)SetCapture(lglw->win.hwnd);
  743. lglw_mouse_cursor_show(_lglw, LGLW_FALSE);
  744. lglw->mouse.grab.p = lglw->mouse.p;
  745. lglw->mouse.grab.last_p = lglw->mouse.p;
  746. lglw->mouse.grab.mode = _grabMode;
  747. break;
  748. }
  749. }
  750. }
  751. }
  752. }
  753. // ---------------------------------------------------------------------------- lglw_mouse_ungrab
  754. void lglw_mouse_ungrab(lglw_t _lglw) {
  755. LGLW(_lglw);
  756. if(NULL != lglw)
  757. {
  758. // (todo) implement me
  759. // if(NULL != lglw->win.hwnd)
  760. {
  761. if(!lglw->mouse.touch.b_enable)
  762. {
  763. switch(lglw->mouse.grab.mode)
  764. {
  765. default:
  766. case LGLW_MOUSE_GRAB_NONE:
  767. break;
  768. case LGLW_MOUSE_GRAB_CAPTURE:
  769. // (todo) implement me
  770. // (void)ReleaseCapture();
  771. lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE;
  772. break;
  773. case LGLW_MOUSE_GRAB_WARP:
  774. // (todo) implement me
  775. // (void)ReleaseCapture();
  776. lglw->mouse.grab.mode = LGLW_MOUSE_GRAB_NONE;
  777. lglw->mouse.grab.b_queue_warp = LGLW_TRUE;
  778. lglw_mouse_cursor_show(_lglw, LGLW_TRUE);
  779. break;
  780. }
  781. }
  782. }
  783. }
  784. }
  785. // ---------------------------------------------------------------------------- lglw_mouse_warp
  786. void lglw_mouse_warp(lglw_t _lglw, int32_t _x, int32_t _y) {
  787. LGLW(_lglw);
  788. // (todo) implement me
  789. if(NULL != lglw)
  790. {
  791. }
  792. }
  793. // ---------------------------------------------------------------------------- loc_handle_queued_mouse_warp
  794. static void loc_handle_queued_mouse_warp(lglw_int_t *lglw) {
  795. if(lglw->mouse.grab.b_queue_warp)
  796. {
  797. lglw->mouse.grab.b_queue_warp = LGLW_FALSE;
  798. lglw_mouse_warp(lglw, lglw->mouse.grab.p.x, lglw->mouse.grab.p.y);
  799. lglw->mouse.grab.last_p = lglw->mouse.grab.p;
  800. }
  801. }
  802. // ---------------------------------------------------------------------------- lglw_mouse_cursor_show
  803. void lglw_mouse_cursor_show (lglw_t _lglw, lglw_bool_t _bShow) {
  804. LGLW(_lglw);
  805. // (todo) implement me
  806. if(NULL != lglw)
  807. {
  808. }
  809. }
  810. // ---------------------------------------------------------------------------- lglw_timer_start
  811. void lglw_timer_start(lglw_t _lglw, uint32_t _millisec) {
  812. LGLW(_lglw);
  813. // (todo) implement me
  814. if(NULL != lglw)
  815. {
  816. }
  817. }
  818. // ---------------------------------------------------------------------------- lglw_timer_stop
  819. void lglw_timer_stop(lglw_t _lglw) {
  820. LGLW(_lglw);
  821. // (todo) implement me
  822. if(NULL != lglw)
  823. {
  824. }
  825. }
  826. // ---------------------------------------------------------------------------- lglw_timer_callback_set
  827. void lglw_timer_callback_set(lglw_t _lglw, lglw_timer_fxn_t _cbk) {
  828. LGLW(_lglw);
  829. if(NULL != lglw)
  830. {
  831. lglw->timer.cbk = _cbk;
  832. }
  833. }
  834. // ---------------------------------------------------------------------------- loc_enable_dropfiles
  835. static void loc_enable_dropfiles(lglw_int_t *lglw, lglw_bool_t _bEnable) {
  836. // (todo) implement me
  837. }
  838. // ---------------------------------------------------------------------------- lglw_dropfiles_callback_set
  839. void lglw_dropfiles_callback_set(lglw_t _lglw, lglw_dropfiles_fxn_t _cbk) {
  840. LGLW(_lglw);
  841. if(NULL != _lglw)
  842. {
  843. lglw->dropfiles.cbk = _cbk;
  844. loc_enable_dropfiles(lglw, (NULL != _cbk));
  845. }
  846. }
  847. // ---------------------------------------------------------------------------- loc_touchinput_update
  848. static void loc_touchinput_update(lglw_int_t *lglw) {
  849. // (todo) implement me
  850. }
  851. // ---------------------------------------------------------------------------- lglw_touchinput_set
  852. void lglw_touchinput_set(lglw_t _lglw, lglw_bool_t _bEnable) {
  853. LGLW(_lglw);
  854. if(NULL != _lglw)
  855. {
  856. lglw->mouse.touch.b_enable = _bEnable;
  857. lglw->mouse.touch.b_update_queued = LGLW_TRUE;
  858. }
  859. }
  860. // ---------------------------------------------------------------------------- lglw_touchinput_get
  861. lglw_bool_t lglw_touchinput_get(lglw_t _lglw) {
  862. lglw_bool_t r = LGLW_FALSE;
  863. LGLW(_lglw);
  864. if(NULL != _lglw)
  865. {
  866. r = lglw->mouse.touch.b_enable;
  867. }
  868. return r;
  869. }
  870. // ---------------------------------------------------------------------------- lglw_clipboard_text_set
  871. void lglw_clipboard_text_set(lglw_t _lglw, const uint32_t _numChars, const char *_text) {
  872. LGLW(_lglw);
  873. (void)_numChars;
  874. // (todo) implement me
  875. if(NULL != _text)
  876. {
  877. }
  878. }
  879. // ---------------------------------------------------------------------------- lglw_clipboard_text_get
  880. void lglw_clipboard_text_get(lglw_t _lglw, uint32_t _maxChars, uint32_t *_retNumChars, char *_retText) {
  881. LGLW(_lglw);
  882. if(NULL != _retNumChars)
  883. *_retNumChars = 0u;
  884. if(NULL != _retText)
  885. *_retText = 0;
  886. if(_maxChars > 0u)
  887. {
  888. if(NULL != _lglw)
  889. {
  890. // (todo) implement me
  891. }
  892. }
  893. }