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.

772 lines
24KB

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