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.

748 lines
23KB

  1. #include "global_pre.hpp"
  2. #include "window.hpp"
  3. #include "app.hpp"
  4. #include "asset.hpp"
  5. #include "settings.hpp"
  6. #include "gamepad.hpp"
  7. #include "keyboard.hpp"
  8. #include "util/color.hpp"
  9. #include <map>
  10. #include <queue>
  11. #include <thread>
  12. #include "osdialog.h"
  13. #define NANOVG_GL2_IMPLEMENTATION 1
  14. // #define NANOVG_GL3_IMPLEMENTATION 1
  15. // #define NANOVG_GLES2_IMPLEMENTATION 1
  16. // #define NANOVG_GLES3_IMPLEMENTATION 1
  17. #include "nanovg_gl.h"
  18. // Hack to get framebuffer objects working on OpenGL 2 (we blindly assume the extension is supported)
  19. #define NANOVG_FBO_VALID 1
  20. #include "nanovg_gl_utils.h"
  21. #define BLENDISH_IMPLEMENTATION
  22. #include "blendish.h"
  23. #define NANOSVG_IMPLEMENTATION
  24. #define NANOSVG_ALL_COLOR_KEYWORDS
  25. #include "nanosvg.h"
  26. #ifdef ARCH_MAC
  27. // For CGAssociateMouseAndMouseCursorPosition
  28. #include <ApplicationServices/ApplicationServices.h>
  29. #endif
  30. #include "global.hpp"
  31. #include "global_ui.hpp"
  32. #ifndef RACK_PLUGIN_SHARED_LIB_BUILD
  33. extern void vst2_set_globals (void *_wrapper);
  34. extern "C" extern void lglw_timer_cbk (lglw_t _lglw); // implemented in vst2_main.cpp
  35. extern "C" extern void lglw_redraw_cbk (lglw_t _lglw); // implemented in vst2_main.cpp
  36. #else
  37. void vst2_set_globals(void *) { }
  38. void vst2_window_size_set(int, int) { }
  39. void vst2_refresh_rate_set(float) { }
  40. void lglw_timer_cbk(lglw_t) { }
  41. void lglw_redraw_cbk(lglw_t) { }
  42. #endif // RACK_PLUGIN_SHARED_LIB_BUILD
  43. namespace rack {
  44. extern "C" {
  45. static void lglw_mouse_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _buttonState, uint32_t _changedButtonState) {
  46. // printf("xxx lglw_mouse_cbk: lglw=%p p=(%d; %d) bt=0x%08x changedBt=0x%08x\n", _lglw, _x, _y, _buttonState, _changedButtonState);
  47. vst2_set_globals(lglw_userdata_get(_lglw));
  48. // (note) assumes that GL context is never touched during regular mouse move
  49. // (note) mouse clicks may cause new SVGs to be loaded, which in turn may cause new GL textures to be created
  50. // if(0u != _changedButtonState)
  51. lglw_glcontext_push(global_ui->window.lglw);
  52. if(LGLW_MOUSE_WHEELUP == _buttonState)
  53. {
  54. // onScroll
  55. EventScroll e;
  56. e.pos = global_ui->window.gMousePos;
  57. Vec scrollRel = Vec(0, 1);
  58. e.scrollRel = scrollRel.mult(50.0);
  59. global_ui->ui.gScene->onScroll(e);
  60. }
  61. else if(LGLW_MOUSE_WHEELDOWN == _buttonState)
  62. {
  63. // onScroll
  64. EventScroll e;
  65. e.pos = global_ui->window.gMousePos;
  66. Vec scrollRel = Vec(0, -1);
  67. e.scrollRel = scrollRel.mult(50.0);
  68. global_ui->ui.gScene->onScroll(e);
  69. }
  70. else if(0u == _changedButtonState)
  71. {
  72. // onMouseMotion
  73. Vec mousePos = Vec(_x, _y);//.div(global_ui->window.gPixelRatio / global_ui->window.gWindowRatio).round();
  74. Vec mouseRel = mousePos.minus(global_ui->window.gMousePos);
  75. // #ifdef ARCH_MAC
  76. // // Workaround for Mac. We can't use GLFW_CURSOR_DISABLED because it's buggy, so implement it on our own.
  77. // // This is not an ideal implementation. For example, if the user drags off the screen, the new mouse position will be clamped.
  78. // if (cursorMode == GLFW_CURSOR_HIDDEN) {
  79. // // CGSetLocalEventsSuppressionInterval(0.0);
  80. // glfwSetCursorPos(global_ui->window.gWindow, global_ui->window.gMousePos.x, global_ui->window.gMousePos.y);
  81. // CGAssociateMouseAndMouseCursorPosition(true);
  82. // mousePos = global_ui->window.gMousePos;
  83. // }
  84. // // Because sometimes the cursor turns into an arrow when its position is on the boundary of the window
  85. // glfwSetCursor(global_ui->window.gWindow, NULL);
  86. // #endif
  87. global_ui->window.gMousePos = mousePos;
  88. global_ui->widgets.gTempWidget = NULL;
  89. // onMouseMove
  90. {
  91. EventMouseMove e;
  92. e.pos = mousePos;
  93. e.mouseRel = mouseRel;
  94. global_ui->ui.gScene->onMouseMove(e);
  95. global_ui->widgets.gTempWidget = e.target;
  96. }
  97. if (global_ui->widgets.gDraggedWidget) {
  98. // onDragMove
  99. EventDragMove e;
  100. e.mouseRel = mouseRel;
  101. global_ui->widgets.gDraggedWidget->onDragMove(e);
  102. if (global_ui->widgets.gTempWidget != global_ui->widgets.gDragHoveredWidget) {
  103. if (global_ui->widgets.gDragHoveredWidget) {
  104. EventDragEnter e;
  105. e.origin = global_ui->widgets.gDraggedWidget;
  106. global_ui->widgets.gDragHoveredWidget->onDragLeave(e);
  107. }
  108. global_ui->widgets.gDragHoveredWidget = global_ui->widgets.gTempWidget;
  109. if (global_ui->widgets.gDragHoveredWidget) {
  110. EventDragEnter e;
  111. e.origin = global_ui->widgets.gDraggedWidget;
  112. global_ui->widgets.gDragHoveredWidget->onDragEnter(e);
  113. }
  114. }
  115. }
  116. else {
  117. if (global_ui->widgets.gTempWidget != global_ui->widgets.gHoveredWidget) {
  118. if (global_ui->widgets.gHoveredWidget) {
  119. // onMouseLeave
  120. EventMouseLeave e;
  121. global_ui->widgets.gHoveredWidget->onMouseLeave(e);
  122. }
  123. global_ui->widgets.gHoveredWidget = global_ui->widgets.gTempWidget;
  124. if (global_ui->widgets.gHoveredWidget) {
  125. // onMouseEnter
  126. EventMouseEnter e;
  127. global_ui->widgets.gHoveredWidget->onMouseEnter(e);
  128. }
  129. }
  130. }
  131. global_ui->widgets.gTempWidget = NULL;
  132. if(0u != (_buttonState & LGLW_MOUSE_MBUTTON)) {
  133. // TODO
  134. // Define a new global called gScrollWidget, which remembers the widget where middle-click was first pressed
  135. EventScroll e;
  136. e.pos = mousePos;
  137. e.scrollRel = mouseRel;
  138. global_ui->ui.gScene->onScroll(e);
  139. }
  140. }
  141. else
  142. {
  143. // Mouse button state changed
  144. // #ifdef ARCH_MAC
  145. // // Ctrl-left click --> right click
  146. // if (button == GLFW_MOUSE_BUTTON_LEFT) {
  147. // if (glfwGetKey(global_ui->window.gWindow, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(global_ui->window.gWindow, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) {
  148. // button = GLFW_MOUSE_BUTTON_RIGHT;
  149. // }
  150. // }
  151. // #endif
  152. int button =
  153. (0u != (_changedButtonState & LGLW_MOUSE_LBUTTON)) ? RACK_MOUSE_BUTTON_LEFT/*0*/ :
  154. (0u != (_changedButtonState & LGLW_MOUSE_RBUTTON)) ? RACK_MOUSE_BUTTON_RIGHT/*1*/ :
  155. (0u != (_changedButtonState & LGLW_MOUSE_MBUTTON)) ? RACK_MOUSE_BUTTON_MIDDLE/*2*/ :
  156. -1;
  157. bool bPressed = (0u != (_changedButtonState & _buttonState));
  158. if (bPressed) {
  159. global_ui->widgets.gTempWidget = NULL;
  160. // onMouseDown
  161. {
  162. EventMouseDown e;
  163. e.pos = global_ui->window.gMousePos;
  164. e.button = button;
  165. global_ui->ui.gScene->onMouseDown(e);
  166. global_ui->widgets.gTempWidget = e.target;
  167. }
  168. if (RACK_MOUSE_BUTTON_LEFT/*0*/ == button) {
  169. if (global_ui->widgets.gTempWidget) {
  170. // onDragStart
  171. EventDragStart e;
  172. global_ui->widgets.gTempWidget->onDragStart(e);
  173. }
  174. global_ui->widgets.gDraggedWidget = global_ui->widgets.gTempWidget;
  175. if (global_ui->widgets.gTempWidget != global_ui->widgets.gFocusedWidget) {
  176. if (global_ui->widgets.gFocusedWidget) {
  177. // onDefocus
  178. EventDefocus e;
  179. global_ui->widgets.gFocusedWidget->onDefocus(e);
  180. }
  181. global_ui->widgets.gFocusedWidget = NULL;
  182. if (global_ui->widgets.gTempWidget) {
  183. // onFocus
  184. EventFocus e;
  185. global_ui->widgets.gTempWidget->onFocus(e);
  186. if (e.consumed) {
  187. global_ui->widgets.gFocusedWidget = global_ui->widgets.gTempWidget;
  188. }
  189. }
  190. }
  191. }
  192. global_ui->widgets.gTempWidget = NULL;
  193. }
  194. else {
  195. // onMouseUp
  196. global_ui->widgets.gTempWidget = NULL;
  197. {
  198. EventMouseUp e;
  199. e.pos = global_ui->window.gMousePos;
  200. e.button = button;
  201. global_ui->ui.gScene->onMouseUp(e);
  202. global_ui->widgets.gTempWidget = e.target;
  203. }
  204. if (RACK_MOUSE_BUTTON_LEFT/*0*/ == button) {
  205. if (global_ui->widgets.gDraggedWidget) {
  206. // onDragDrop
  207. EventDragDrop e;
  208. e.origin = global_ui->widgets.gDraggedWidget;
  209. global_ui->widgets.gTempWidget->onDragDrop(e);
  210. }
  211. // gDraggedWidget might have been set to null in the last event, recheck here
  212. if (global_ui->widgets.gDraggedWidget) {
  213. // onDragEnd
  214. EventDragEnd e;
  215. global_ui->widgets.gDraggedWidget->onDragEnd(e);
  216. }
  217. global_ui->widgets.gDraggedWidget = NULL;
  218. global_ui->widgets.gDragHoveredWidget = NULL;
  219. }
  220. global_ui->widgets.gTempWidget = NULL;
  221. }
  222. }
  223. // if(0u != _changedButtonState)
  224. lglw_glcontext_pop(global_ui->window.lglw);
  225. }
  226. static void lglw_focus_cbk(lglw_t _lglw, uint32_t _focusState, uint32_t _changedFocusState) {
  227. // printf("xxx lglw_focus_cbk: lglw=%p focusState=0x%08x changedFocusState=0x%08x\n", _lglw, _focusState, _changedFocusState);
  228. vst2_set_globals(lglw_userdata_get(_lglw));
  229. if(0u != (_changedFocusState & LGLW_FOCUS_MOUSE))
  230. {
  231. if(0u != (_focusState & LGLW_FOCUS_MOUSE))
  232. {
  233. // onMouseEnter
  234. }
  235. else
  236. {
  237. if (global_ui->widgets.gHoveredWidget) {
  238. // onMouseLeave
  239. EventMouseLeave e;
  240. global_ui->widgets.gHoveredWidget->onMouseLeave(e);
  241. }
  242. global_ui->widgets.gHoveredWidget = NULL;
  243. }
  244. }
  245. }
  246. static lglw_bool_t lglw_keyboard_cbk(lglw_t _lglw, uint32_t _vkey, uint32_t _kmod, lglw_bool_t _bPressed) {
  247. // printf("xxx lglw_keyboard_cbk: lglw=%p vkey=0x%08x (\'%c\') kmod=0x%08x bPressed=%d\n", _lglw, _vkey, (char)_vkey, _kmod, _bPressed);
  248. lglw_bool_t bHandled = LGLW_FALSE;
  249. vst2_set_globals(lglw_userdata_get(_lglw));
  250. if( (0u == (_vkey & LGLW_VKEY_EXT)) && (0u == (_kmod & (LGLW_KMOD_LCTRL | LGLW_KMOD_RCTRL))) )
  251. {
  252. // Unicode or ASCII character
  253. if(_bPressed)
  254. {
  255. if (global_ui->widgets.gFocusedWidget) {
  256. // onText
  257. EventText e;
  258. e.codepoint = _vkey;
  259. global_ui->widgets.gFocusedWidget->onText(e);
  260. if(e.consumed)
  261. return LGLW_TRUE;
  262. }
  263. }
  264. }
  265. if(_bPressed)
  266. {
  267. if (global_ui->widgets.gFocusedWidget) {
  268. // onKey
  269. EventKey e;
  270. e.key = _vkey;
  271. global_ui->widgets.gFocusedWidget->onKey(e);
  272. if (e.consumed)
  273. return LGLW_TRUE;
  274. }
  275. // onHoverKey
  276. EventHoverKey e;
  277. e.pos = global_ui->window.gMousePos;
  278. e.key = _vkey;
  279. global_ui->ui.gScene->onHoverKey(e);
  280. }
  281. return bHandled;
  282. }
  283. int vst2_handle_effeditkeydown(unsigned int _vkey) {
  284. // (note) only used for touch keyboard input
  285. lglw_bool_t bHandled = lglw_keyboard_cbk(rack::global_ui->window.lglw, _vkey, 0u/*kmod*/, LGLW_TRUE/*bPressed*/);
  286. lglw_keyboard_cbk(rack::global_ui->window.lglw, _vkey, 0u/*kmod*/, LGLW_FALSE/*bPressed*/);
  287. return bHandled;
  288. }
  289. void lglw_dropfiles_cbk(lglw_t _lglw, int32_t _x, int32_t _y, uint32_t _numFiles, const char **_pathNames) {
  290. // onPathDrop
  291. vst2_set_globals(lglw_userdata_get(_lglw));
  292. lglw_glcontext_push(global_ui->window.lglw);
  293. EventPathDrop e;
  294. e.pos = Vec(_x, _y);
  295. for(uint32_t i = 0u; i < _numFiles; i++) {
  296. e.paths.push_back(_pathNames[i]);
  297. }
  298. global_ui->ui.gScene->onPathDrop(e);
  299. lglw_glcontext_pop(global_ui->window.lglw);
  300. }
  301. } // extern C
  302. void renderGui() {
  303. int width, height;
  304. // printf("xxx renderGui: ENTER\n");
  305. lglw_window_size_get(global_ui->window.lglw, &width, &height);
  306. // Update and render
  307. nvgBeginFrame(global_ui->window.gVg, width, height, global_ui->window.gPixelRatio);
  308. nvgReset(global_ui->window.gVg);
  309. nvgScale(global_ui->window.gVg, global_ui->window.gPixelRatio, global_ui->window.gPixelRatio);
  310. // printf("xxx renderGui: gScene->draw() BEGIN\n");
  311. global_ui->ui.gScene->draw(global_ui->window.gVg);
  312. // printf("xxx renderGui: gScene->draw() END\n");
  313. glViewport(0, 0, width, height);
  314. glClearColor(0.0, 0.0, 0.0, 1.0);
  315. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
  316. nvgEndFrame(global_ui->window.gVg);
  317. // printf("xxx renderGui: LEAVE\n");
  318. }
  319. void windowInit() {
  320. int err;
  321. // (note) the hidden LGLW context window must have the same size as the real window created later on
  322. settingsLoad(assetLocal("settings.json"), true/*bWindowSizeOnly*/);
  323. global_ui->window.lglw = lglw_init(global_ui->window.windowWidth, global_ui->window.windowHeight);
  324. lglw_userdata_set(global_ui->window.lglw, global->vst2.wrapper);
  325. global_ui->window.lastWindowTitle = "";
  326. lglw_glcontext_push(global_ui->window.lglw);
  327. lglw_swap_interval_set(global_ui->window.lglw, 1); // can be overridden via settings.json:"vsync" property
  328. lglw_mouse_callback_set (global_ui->window.lglw, &lglw_mouse_cbk);
  329. lglw_focus_callback_set (global_ui->window.lglw, &lglw_focus_cbk);
  330. lglw_keyboard_callback_set (global_ui->window.lglw, &lglw_keyboard_cbk);
  331. lglw_timer_callback_set (global_ui->window.lglw, &lglw_timer_cbk);
  332. lglw_dropfiles_callback_set (global_ui->window.lglw, &lglw_dropfiles_cbk);
  333. lglw_redraw_callback_set (global_ui->window.lglw, &lglw_redraw_cbk);
  334. // Set up GLEW
  335. glewExperimental = GL_TRUE;
  336. err = glewInit();
  337. if (err != GLEW_OK) {
  338. osdialog_message(OSDIALOG_ERROR, OSDIALOG_OK, "Could not initialize GLEW. Does your graphics card support OpenGL 2.0 or greater? If so, make sure you have the latest graphics drivers installed.");
  339. lglw_glcontext_pop(global_ui->window.lglw);
  340. exit(1);
  341. }
  342. // GLEW generates GL error because it calls glGetString(GL_EXTENSIONS), we'll consume it here.
  343. glGetError();
  344. // Set up NanoVG
  345. int nvgFlags = NVG_ANTIALIAS;
  346. #if defined NANOVG_GL2
  347. global_ui->window.gVg = nvgCreateGL2(nvgFlags);
  348. #elif defined NANOVG_GL3
  349. global_ui->window.gVg = nvgCreateGL3(nvgFlags);
  350. #elif defined NANOVG_GLES2
  351. global_ui->window.gVg = nvgCreateGLES2(nvgFlags);
  352. #endif
  353. assert(global_ui->window.gVg);
  354. #if defined NANOVG_GL2
  355. global_ui->window.gFramebufferVg = nvgCreateGL2(nvgFlags);
  356. #elif defined NANOVG_GL3
  357. global_ui->window.gFramebufferVg = nvgCreateGL3(nvgFlags);
  358. #elif defined NANOVG_GLES2
  359. global_ui->window.gFramebufferVg = nvgCreateGLES2(nvgFlags);
  360. #endif
  361. assert(global_ui->window.gFramebufferVg);
  362. // Set up Blendish
  363. global_ui->window.gGuiFont = Font::load(assetGlobal("res/fonts/DejaVuSans.ttf"));
  364. bndSetFont(global_ui->window.gGuiFont->handle);
  365. windowSetTheme(nvgRGB(0x33, 0x33, 0x33), nvgRGB(0xf0, 0xf0, 0xf0));
  366. lglw_glcontext_pop(global_ui->window.lglw);
  367. }
  368. void windowDestroy() {
  369. printf("xxx vstrack_plugin: windowDestroy()\n");
  370. lglw_glcontext_push(global_ui->window.lglw);
  371. global_ui->window.gGuiFont.reset();
  372. #if defined NANOVG_GL2
  373. nvgDeleteGL2(global_ui->window.gVg);
  374. #elif defined NANOVG_GL3
  375. nvgDeleteGL3(global_ui->window.gVg);
  376. #elif defined NANOVG_GLES2
  377. nvgDeleteGLES2(global_ui->window.gVg);
  378. #endif
  379. #if defined NANOVG_GL2
  380. nvgDeleteGL2(global_ui->window.gFramebufferVg);
  381. #elif defined NANOVG_GL3
  382. nvgDeleteGL3(global_ui->window.gFramebufferVg);
  383. #elif defined NANOVG_GLES2
  384. nvgDeleteGLES2(global_ui->window.gFramebufferVg);
  385. #endif
  386. lglw_glcontext_pop(global_ui->window.lglw);
  387. lglw_exit(global_ui->window.lglw);
  388. global_ui->window.lglw = NULL;
  389. }
  390. void vst2_editor_redraw(void) {
  391. // (note) the GL context is set by the caller
  392. global_ui->window.gGuiFrame++;
  393. // Find/validate hover param
  394. if(NULL != global_ui->param_info.last_param_widget)
  395. {
  396. int uniqueParamId;
  397. ParamWidget *paramWidget =
  398. global_ui->app.gRackWidget->findParamWidgetAndUniqueParamIdByWidgetRef(global_ui->param_info.last_param_widget,
  399. &uniqueParamId
  400. );
  401. if(NULL != paramWidget)
  402. {
  403. global_ui->param_info.last_param_gid = uniqueParamId;
  404. global_ui->param_info.last_param_value = paramWidget->value;
  405. #if 0
  406. printf("xxx vst2_editor_redraw: param_info: uniqueParamId=%d value=%f clipboardValue=%f\n",
  407. global_ui->param_info.last_param_gid,
  408. global_ui->param_info.last_param_value,
  409. global_ui->param_info.value_clipboard
  410. );
  411. #endif
  412. char buf[64];
  413. sprintf(buf, "%d", global_ui->param_info.last_param_gid);
  414. global_ui->param_info.tf_id->setTextQuiet(buf);
  415. sprintf(buf, "%f", global_ui->param_info.last_param_value);
  416. // Delete trailing zeros
  417. {
  418. char *d = buf;
  419. while(0 != *d)
  420. d++;
  421. d--;
  422. if(d > buf)
  423. {
  424. while('0' == *d)
  425. {
  426. if(((d-1) > buf) && ('.' != d[-1]))
  427. *d-- = 0;
  428. else
  429. break;
  430. }
  431. }
  432. }
  433. global_ui->param_info.tf_value->setTextQuiet(buf);
  434. }
  435. global_ui->param_info.last_param_widget = NULL;
  436. global_ui->param_info.placeholder_framecount = 1;
  437. }
  438. else if(0 != global_ui->param_info.placeholder_framecount)
  439. {
  440. if(++global_ui->param_info.placeholder_framecount > (30*30))
  441. {
  442. global_ui->param_info.tf_id->setTextQuiet("");
  443. global_ui->param_info.tf_value->setTextQuiet("");
  444. global_ui->param_info.placeholder_framecount = 0;
  445. }
  446. }
  447. #if 0
  448. // Set window title
  449. // (note) the VST plugin editor window title is set by the VST host
  450. std::string windowTitle;
  451. windowTitle = global_ui->app.gApplicationName;
  452. windowTitle += " ";
  453. windowTitle += global_ui->app.gApplicationVersion;
  454. if (!global_ui->app.gRackWidget->lastPath.empty()) {
  455. windowTitle += " - ";
  456. windowTitle += stringFilename(global_ui->app.gRackWidget->lastPath);
  457. }
  458. if (windowTitle != global_ui->window.lastWindowTitle) {
  459. // // glfwSetWindowTitle(global_ui->window.gWindow, windowTitle.c_str());
  460. global_ui->window.lastWindowTitle = windowTitle;
  461. }
  462. #endif
  463. // Get framebuffer/window ratio
  464. int width, height;
  465. lglw_window_size_get(global_ui->window.lglw, &width, &height);
  466. global_ui->window.gWindowRatio = 1.0f;
  467. global_ui->ui.gScene->box.size = Vec(width, height);
  468. // Step scene
  469. global_ui->ui.gScene->step();
  470. // Render
  471. renderGui();
  472. // Present
  473. glFlush();
  474. lglw_swap_buffers(global_ui->window.lglw);
  475. }
  476. void windowCursorLock() {
  477. if (global_ui->window.gAllowCursorLock) {
  478. lglw_mouse_grab(global_ui->window.lglw, LGLW_MOUSE_GRAB_WARP);
  479. }
  480. }
  481. void windowCursorUnlock() {
  482. if (global_ui->window.gAllowCursorLock) {
  483. lglw_mouse_ungrab(global_ui->window.lglw);
  484. }
  485. }
  486. bool windowIsModPressed() {
  487. return (0u != (lglw_keyboard_get_modifiers(global_ui->window.lglw) & (LGLW_KMOD_LCTRL | LGLW_KMOD_RCTRL)));
  488. }
  489. bool windowIsShiftPressed() {
  490. return (0u != (lglw_keyboard_get_modifiers(global_ui->window.lglw) & (LGLW_KMOD_LSHIFT | LGLW_KMOD_RSHIFT)));
  491. }
  492. Vec windowGetWindowSize() {
  493. int width, height;
  494. lglw_window_size_get(global_ui->window.lglw, &width, &height);
  495. return Vec(width, height);
  496. }
  497. void windowSetWindowSize(Vec size) {
  498. (void)size;
  499. // (note) not supported
  500. }
  501. Vec windowGetWindowPos() {
  502. int x, y;
  503. x = 0;
  504. y = 0;
  505. return Vec(x, y);
  506. }
  507. void windowSetWindowPos(Vec pos) {
  508. int x = pos.x;
  509. int y = pos.y;
  510. // (note) not supported
  511. }
  512. bool windowIsMaximized() {
  513. return true;
  514. }
  515. void windowSetTheme(NVGcolor bg, NVGcolor fg) {
  516. // Assume dark background and light foreground
  517. BNDwidgetTheme w;
  518. w.outlineColor = bg;
  519. w.itemColor = fg;
  520. w.innerColor = bg;
  521. w.innerSelectedColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30));
  522. w.textColor = fg;
  523. w.textSelectedColor = fg;
  524. w.shadeTop = 0;
  525. w.shadeDown = 0;
  526. BNDtheme t;
  527. t.backgroundColor = colorPlus(bg, nvgRGB(0x30, 0x30, 0x30));
  528. t.regularTheme = w;
  529. t.toolTheme = w;
  530. t.radioTheme = w;
  531. t.textFieldTheme = w;
  532. t.optionTheme = w;
  533. t.choiceTheme = w;
  534. t.numberFieldTheme = w;
  535. t.sliderTheme = w;
  536. t.scrollBarTheme = w;
  537. t.tooltipTheme = w;
  538. t.menuTheme = w;
  539. t.menuItemTheme = w;
  540. t.sliderTheme.itemColor = bg;
  541. t.sliderTheme.innerColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50));
  542. t.sliderTheme.innerSelectedColor = colorPlus(bg, nvgRGB(0x60, 0x60, 0x60));
  543. t.textFieldTheme = t.sliderTheme;
  544. t.textFieldTheme.textColor = colorMinus(bg, nvgRGB(0x20, 0x20, 0x20));
  545. t.textFieldTheme.textSelectedColor = t.textFieldTheme.textColor;
  546. t.scrollBarTheme.itemColor = colorPlus(bg, nvgRGB(0x50, 0x50, 0x50));
  547. t.scrollBarTheme.innerColor = bg;
  548. t.menuTheme.innerColor = colorMinus(bg, nvgRGB(0x10, 0x10, 0x10));
  549. t.menuTheme.textColor = colorMinus(fg, nvgRGB(0x50, 0x50, 0x50));
  550. t.menuTheme.textSelectedColor = t.menuTheme.textColor;
  551. bndSetTheme(t);
  552. }
  553. void windowSetFullScreen(bool fullScreen) {
  554. (void)fullScreen;
  555. // (note) not supported
  556. }
  557. bool windowGetFullScreen() {
  558. return false;
  559. }
  560. void windowClose(void) {
  561. // (note) not supported
  562. }
  563. ////////////////////
  564. // resources
  565. ////////////////////
  566. Font::Font(const std::string &filename) {
  567. printf("xxx vstrack_plugin: Font::Font\n");
  568. handle = nvgCreateFont(global_ui->window.gVg, filename.c_str(), filename.c_str());
  569. if (handle >= 0) {
  570. info("Loaded font %s", filename.c_str());
  571. }
  572. else {
  573. warn("Failed to load font %s", filename.c_str());
  574. }
  575. }
  576. Font::~Font() {
  577. // There is no NanoVG deleteFont() function yet, so do nothing
  578. }
  579. std::shared_ptr<Font> Font::load(const std::string &filename) {
  580. auto sp = global_ui->window.font_cache[filename].lock();
  581. if (!sp)
  582. global_ui->window.font_cache[filename] = sp = std::make_shared<Font>(filename);
  583. return sp;
  584. }
  585. ////////////////////
  586. // Image
  587. ////////////////////
  588. Image::Image(const std::string &filename) {
  589. printf("xxx vstrack_plugin: Image::Image\n");
  590. handle = nvgCreateImage(global_ui->window.gVg, filename.c_str(), NVG_IMAGE_REPEATX | NVG_IMAGE_REPEATY);
  591. if (handle > 0) {
  592. info("Loaded image %s", filename.c_str());
  593. }
  594. else {
  595. warn("Failed to load image %s", filename.c_str());
  596. }
  597. }
  598. Image::~Image() {
  599. // TODO What if handle is invalid?
  600. nvgDeleteImage(global_ui->window.gVg, handle);
  601. }
  602. std::shared_ptr<Image> Image::load(const std::string &filename) {
  603. auto sp = global_ui->window.image_cache[filename].lock();
  604. if (!sp)
  605. global_ui->window.image_cache[filename] = sp = std::make_shared<Image>(filename);
  606. return sp;
  607. }
  608. ////////////////////
  609. // SVG
  610. ////////////////////
  611. SVG::SVG(const std::string &filename) {
  612. printf("xxx vstrack: SVG::SVG\n");
  613. // printf("xxx SVG::SVG: ENTER\n");
  614. handle = nsvgParseFromFile(filename.c_str(), "px", SVG_DPI);
  615. // printf("xxx SVG::SVG: handle=%p\n");
  616. if (handle) {
  617. info("Loaded SVG %s", filename.c_str());
  618. }
  619. else {
  620. warn("Failed to load SVG %s", filename.c_str());
  621. }
  622. // printf("xxx SVG::SVG: LEAVE\n");
  623. }
  624. SVG::~SVG() {
  625. nsvgDelete(handle);
  626. }
  627. std::shared_ptr<SVG> SVG::load(const std::string &filename) {
  628. // printf("xxx SVG::load: ENTER\n");
  629. auto sp = global_ui->window.svg_cache[filename].lock();
  630. if (!sp)
  631. global_ui->window.svg_cache[filename] = sp = std::make_shared<SVG>(filename);
  632. // printf("xxx SVG::load: RETURN\n");
  633. return sp;
  634. }
  635. } // namespace rack