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.

1224 lines
36KB

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