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.

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