Audio plugin host https://kx.studio/carla
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.

471 lines
16KB

  1. // Copyright 2021 Jean Pierre Cimalando
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // SPDX-License-Identifier: Apache-2.0
  16. //
  17. #include "ysfx.hpp"
  18. #include "ysfx_config.hpp"
  19. #include "ysfx_api_gfx.hpp"
  20. #include "ysfx_eel_utils.hpp"
  21. #if !defined(YSFX_NO_GFX)
  22. # include "lice_stb/lice_stb_loaders.hpp"
  23. # define WDL_NO_DEFINE_MINMAX
  24. # include "WDL/swell/swell.h"
  25. # include "WDL/lice/lice.h"
  26. # include "WDL/lice/lice_text.h"
  27. # include "WDL/wdlstring.h"
  28. #endif
  29. #include <vector>
  30. #include <queue>
  31. #include <unordered_set>
  32. #include <memory>
  33. #include <atomic>
  34. #include <cassert>
  35. #if !defined(YSFX_NO_GFX)
  36. #define GFX_GET_CONTEXT(opaque) (((opaque)) ? ysfx_gfx_get_context((ysfx_t *)(opaque)) : nullptr)
  37. enum {
  38. ysfx_gfx_max_images = 1024,
  39. ysfx_gfx_max_fonts = 128,
  40. ysfx_gfx_max_input = 1024,
  41. };
  42. class eel_lice_state;
  43. struct ysfx_gfx_state_t {
  44. ysfx_gfx_state_t(ysfx_t *fx);
  45. ~ysfx_gfx_state_t();
  46. std::unique_ptr<eel_lice_state> lice;
  47. std::queue<uint32_t> input_queue;
  48. std::unordered_set<uint32_t> keys_pressed;
  49. ysfx_real scale = 0.0;
  50. void *callback_data = nullptr;
  51. int (*show_menu)(void *, const char *, int32_t, int32_t) = nullptr;
  52. void (*set_cursor)(void *, int32_t) = nullptr;
  53. const char *(*get_drop_file)(void *user_data, int32_t index) = nullptr;
  54. };
  55. //------------------------------------------------------------------------------
  56. #if !defined(YSFX_NO_GFX)
  57. static bool eel_lice_get_filename_for_string(void *opaque, EEL_F idx, WDL_FastString *fs, int iswrite)
  58. {
  59. if (iswrite)
  60. return false; // this is neither supported nor used
  61. ysfx_t *fx = (ysfx_t *)opaque;
  62. std::string filepath;
  63. if (!ysfx_find_data_file(fx, &idx, filepath))
  64. return false;
  65. if (fs) fs->Set(filepath.data(), (uint32_t)filepath.size());
  66. return true;
  67. }
  68. #define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) \
  69. eel_lice_get_filename_for_string(opaque, (idx), (fs), (p))
  70. #endif
  71. //------------------------------------------------------------------------------
  72. #if !defined(YSFX_NO_GFX)
  73. # include "ysfx_api_gfx_lice.hpp"
  74. #else
  75. # include "ysfx_api_gfx_dummy.hpp"
  76. #endif
  77. //------------------------------------------------------------------------------
  78. #if !defined(YSFX_NO_GFX)
  79. static bool translate_special_key(uint32_t uni_key, uint32_t &jsfx_key)
  80. {
  81. auto key_c = [](uint8_t a, uint8_t b, uint8_t c, uint8_t d) -> uint32_t {
  82. return a | (b << 8) | (c << 16) | (d << 24);
  83. };
  84. switch (uni_key) {
  85. default: return false;
  86. case ysfx_key_delete: jsfx_key = key_c('d', 'e', 'l', 0); break;
  87. case ysfx_key_f1: jsfx_key = key_c('f', '1', 0, 0); break;
  88. case ysfx_key_f2: jsfx_key = key_c('f', '2', 0, 0); break;
  89. case ysfx_key_f3: jsfx_key = key_c('f', '3', 0, 0); break;
  90. case ysfx_key_f4: jsfx_key = key_c('f', '4', 0, 0); break;
  91. case ysfx_key_f5: jsfx_key = key_c('f', '5', 0, 0); break;
  92. case ysfx_key_f6: jsfx_key = key_c('f', '6', 0, 0); break;
  93. case ysfx_key_f7: jsfx_key = key_c('f', '7', 0, 0); break;
  94. case ysfx_key_f8: jsfx_key = key_c('f', '8', 0, 0); break;
  95. case ysfx_key_f9: jsfx_key = key_c('f', '9', 0, 0); break;
  96. case ysfx_key_f10: jsfx_key = key_c('f', '1', '0', 0); break;
  97. case ysfx_key_f11: jsfx_key = key_c('f', '1', '1', 0); break;
  98. case ysfx_key_f12: jsfx_key = key_c('f', '1', '2', 0); break;
  99. case ysfx_key_left: jsfx_key = key_c('l', 'e', 'f', 't'); break;
  100. case ysfx_key_up: jsfx_key = key_c('u', 'p', 0, 0); break;
  101. case ysfx_key_right: jsfx_key = key_c('r', 'g', 'h', 't'); break;
  102. case ysfx_key_down: jsfx_key = key_c('d', 'o', 'w', 'n'); break;
  103. case ysfx_key_page_up: jsfx_key = key_c('p', 'g', 'u', 'p'); break;
  104. case ysfx_key_page_down: jsfx_key = key_c('p', 'g', 'd', 'n'); break;
  105. case ysfx_key_home: jsfx_key = key_c('h', 'o', 'm', 'e'); break;
  106. case ysfx_key_end: jsfx_key = key_c('e', 'n', 'd', 0); break;
  107. case ysfx_key_insert: jsfx_key = key_c('i', 'n', 's', 0); break;
  108. }
  109. return true;
  110. }
  111. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getchar(void *opaque, EEL_F *p)
  112. {
  113. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  114. if (!state)
  115. return 0;
  116. if (*p >= 1/*2*/) { // NOTE(jpc) this is 2.0 originally, which seems wrong
  117. if (*p == 65536) {
  118. // TODO implement window flags
  119. return 0;
  120. }
  121. // current key down status
  122. uint32_t key = (uint32_t)*p;
  123. uint32_t key_id;
  124. if (translate_special_key(key, key))
  125. key_id = key;
  126. else if (key < 256)
  127. key_id = ysfx::latin1_tolower(key);
  128. else // support the Latin-1 character set only
  129. return 0;
  130. return (EEL_F)(state->keys_pressed.find(key_id) != state->keys_pressed.end());
  131. }
  132. if (!state->input_queue.empty()) {
  133. uint32_t key = state->input_queue.front();
  134. state->input_queue.pop();
  135. return (EEL_F)key;
  136. }
  137. return 0;
  138. }
  139. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_showmenu(void *opaque, INT_PTR nparms, EEL_F **parms)
  140. {
  141. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  142. if (!state || !state->show_menu)
  143. return 0;
  144. ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;
  145. std::string desc;
  146. if (!ysfx_string_get(fx, *parms[0], desc) || desc.empty())
  147. return 0;
  148. int32_t x = (int32_t)*fx->var.gfx_x;
  149. int32_t y = (int32_t)*fx->var.gfx_y;
  150. return state->show_menu(state->callback_data, desc.c_str(), x, y);
  151. }
  152. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setcursor(void *opaque, INT_PTR nparms, EEL_F **parms)
  153. {
  154. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  155. if (!state || !state->set_cursor)
  156. return 0;
  157. int32_t id = (int32_t)*parms[0];
  158. state->set_cursor(state->callback_data, id);
  159. return 0;
  160. }
  161. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms)
  162. {
  163. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  164. if (!state || !state->get_drop_file)
  165. return 0;
  166. const int32_t idx = (int)*parms[0];
  167. if (idx < 0) {
  168. state->get_drop_file(state->callback_data, -1);
  169. return 0;
  170. }
  171. const char *file = state->get_drop_file(state->callback_data, idx);
  172. if (!file)
  173. return 0;
  174. if (np > 1) {
  175. ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;
  176. ysfx_string_set(fx, *parms[1], file);
  177. }
  178. return 1;
  179. }
  180. #endif
  181. //------------------------------------------------------------------------------
  182. #if !defined(YSFX_NO_GFX)
  183. ysfx_gfx_state_t::ysfx_gfx_state_t(ysfx_t *fx)
  184. : lice{new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}}
  185. {
  186. lice->m_framebuffer = new LICE_WrapperBitmap{nullptr, 0, 0, 0, false};
  187. }
  188. ysfx_gfx_state_t::~ysfx_gfx_state_t()
  189. {
  190. }
  191. #endif
  192. ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx)
  193. {
  194. return new ysfx_gfx_state_t{fx};
  195. }
  196. void ysfx_gfx_state_free(ysfx_gfx_state_t *state)
  197. {
  198. delete state;
  199. }
  200. void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride)
  201. {
  202. if (stride == 0)
  203. stride = 4 * w;
  204. eel_lice_state *lice = state->lice.get();
  205. assert(stride % 4 == 0);
  206. *static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer) = LICE_WrapperBitmap{(LICE_pixel *)data, (int)w, (int)h, (int)(stride / 4), false};
  207. }
  208. void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale)
  209. {
  210. state->scale = scale;
  211. }
  212. void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data)
  213. {
  214. state->callback_data = callback_data;
  215. }
  216. void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t))
  217. {
  218. state->show_menu = callback;
  219. }
  220. void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t))
  221. {
  222. state->set_cursor = callback;
  223. }
  224. void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t))
  225. {
  226. state->get_drop_file = callback;
  227. }
  228. bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state)
  229. {
  230. return state->lice->m_framebuffer_dirty;
  231. }
  232. void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press)
  233. {
  234. if (key < 1)
  235. return;
  236. uint32_t key_id;
  237. if (translate_special_key(key, key))
  238. key_id = key;
  239. else if (key < 256)
  240. key_id = ysfx::latin1_tolower(key);
  241. else // support the Latin-1 character set only
  242. return;
  243. uint32_t key_with_mod = key;
  244. if (key_id >= 'a' && key_id <= 'z') {
  245. uint32_t off = (uint32_t)(key_id - 'a');
  246. if (mods & (ysfx_mod_ctrl|ysfx_mod_alt))
  247. key_with_mod = off + 257;
  248. else if (mods & ysfx_mod_ctrl)
  249. key_with_mod = off + 1;
  250. else if (mods & ysfx_mod_alt)
  251. key_with_mod = off + 321;
  252. }
  253. if (press && key_with_mod > 0) {
  254. while (state->input_queue.size() >= ysfx_gfx_max_input)
  255. state->input_queue.pop();
  256. state->input_queue.push(key_with_mod);
  257. }
  258. if (press)
  259. state->keys_pressed.insert(key_id);
  260. else
  261. state->keys_pressed.erase(key_id);
  262. }
  263. //------------------------------------------------------------------------------
  264. void ysfx_gfx_enter(ysfx_t *fx, bool doinit)
  265. {
  266. fx->gfx.mutex.lock();
  267. ysfx_gfx_state_t *state = fx->gfx.state.get();
  268. if (doinit) {
  269. if (fx->gfx.must_init.exchange(false, std::memory_order_acquire)) {
  270. *fx->var.gfx_r = 1.0;
  271. *fx->var.gfx_g = 1.0;
  272. *fx->var.gfx_b = 1.0;
  273. *fx->var.gfx_a = 1.0;
  274. *fx->var.gfx_a2 = 1.0;
  275. *fx->var.gfx_dest = -1.0;
  276. *fx->var.mouse_wheel = 0.0;
  277. *fx->var.mouse_hwheel = 0.0;
  278. // NOTE the above are according to eel_lice.h `resetVarsToStock`
  279. // it helps to reset a few more, especially for clearing
  280. *fx->var.gfx_mode = 0;
  281. *fx->var.gfx_clear = 0;
  282. *fx->var.gfx_texth = 0;
  283. *fx->var.mouse_cap = 0;
  284. // reset key state
  285. state->input_queue = {};
  286. state->keys_pressed = {};
  287. // reset lice
  288. eel_lice_state *lice = state->lice.get();
  289. LICE_WrapperBitmap framebuffer = *static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer);
  290. state->lice.reset();
  291. lice = new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts};
  292. state->lice.reset(lice);
  293. lice->m_framebuffer = new LICE_WrapperBitmap(framebuffer);
  294. // load images from filenames
  295. uint32_t numfiles = (uint32_t)fx->source.main->header.filenames.size();
  296. for (uint32_t i = 0; i < numfiles; ++i)
  297. lice->gfx_loadimg(fx, (int32_t)i, (EEL_F)i);
  298. fx->gfx.ready = true;
  299. }
  300. }
  301. ysfx_set_thread_id(ysfx_thread_id_gfx);
  302. }
  303. void ysfx_gfx_leave(ysfx_t *fx)
  304. {
  305. ysfx_set_thread_id(ysfx_thread_id_none);
  306. fx->gfx.mutex.unlock();
  307. }
  308. ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx)
  309. {
  310. if (!fx)
  311. return nullptr;
  312. // NOTE: make sure that this will be used from the @gfx thread only
  313. if (ysfx_get_thread_id() != ysfx_thread_id_gfx)
  314. return nullptr;
  315. return fx->gfx.state.get();
  316. }
  317. void ysfx_gfx_prepare(ysfx_t *fx)
  318. {
  319. ysfx_gfx_state_t *state = ysfx_gfx_get_context(fx);
  320. eel_lice_state *lice = state->lice.get();
  321. lice->m_framebuffer_dirty = false;
  322. // set variables `gfx_w` and `gfx_h`
  323. ysfx_real gfx_w = (ysfx_real)lice->m_framebuffer->getWidth();
  324. ysfx_real gfx_h = (ysfx_real)lice->m_framebuffer->getHeight();
  325. if (state->scale > 1.0) {
  326. gfx_w *= state->scale;
  327. gfx_h *= state->scale;
  328. *fx->var.gfx_ext_retina = state->scale;
  329. }
  330. *fx->var.gfx_w = gfx_w;
  331. *fx->var.gfx_h = gfx_h;
  332. }
  333. #endif
  334. //------------------------------------------------------------------------------
  335. void ysfx_api_init_gfx()
  336. {
  337. #if !defined(YSFX_NO_GFX)
  338. lice_stb_install_loaders();
  339. #endif
  340. NSEEL_addfunc_retptr("gfx_lineto", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto);
  341. NSEEL_addfunc_retptr("gfx_lineto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto2);
  342. NSEEL_addfunc_retptr("gfx_rectto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_rectto);
  343. NSEEL_addfunc_varparm("gfx_rect", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_rect);
  344. NSEEL_addfunc_varparm("gfx_line", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_line);
  345. NSEEL_addfunc_varparm("gfx_gradrect", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_gradrect);
  346. NSEEL_addfunc_varparm("gfx_muladdrect", 7, NSEEL_PProc_THIS, &ysfx_api_gfx_muladdrect);
  347. NSEEL_addfunc_varparm("gfx_deltablit", 9, NSEEL_PProc_THIS, &ysfx_api_gfx_deltablit);
  348. NSEEL_addfunc_exparms("gfx_transformblit", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_transformblit);
  349. NSEEL_addfunc_varparm("gfx_circle", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_circle);
  350. NSEEL_addfunc_varparm("gfx_triangle", 6, NSEEL_PProc_THIS, &ysfx_api_gfx_triangle);
  351. NSEEL_addfunc_varparm("gfx_roundrect", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_roundrect);
  352. NSEEL_addfunc_varparm("gfx_arc", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_arc);
  353. NSEEL_addfunc_retptr("gfx_blurto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_blurto);
  354. NSEEL_addfunc_exparms("gfx_showmenu", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_showmenu);
  355. NSEEL_addfunc_varparm("gfx_setcursor", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setcursor);
  356. NSEEL_addfunc_retptr("gfx_drawnumber", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_drawnumber);
  357. NSEEL_addfunc_retptr("gfx_drawchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawchar);
  358. NSEEL_addfunc_varparm("gfx_drawstr", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawstr);
  359. NSEEL_addfunc_retptr("gfx_measurestr", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurestr);
  360. NSEEL_addfunc_retptr("gfx_measurechar", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurechar);
  361. NSEEL_addfunc_varparm("gfx_printf", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_printf);
  362. NSEEL_addfunc_retptr("gfx_setpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setpixel);
  363. NSEEL_addfunc_retptr("gfx_getpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getpixel);
  364. NSEEL_addfunc_retptr("gfx_getimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getimgdim);
  365. NSEEL_addfunc_retval("gfx_setimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setimgdim);
  366. NSEEL_addfunc_retval("gfx_loadimg", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_loadimg);
  367. NSEEL_addfunc_retptr("gfx_blit", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blit);
  368. NSEEL_addfunc_retptr("gfx_blitext", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blitext);
  369. NSEEL_addfunc_varparm("gfx_blit", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_blit2);
  370. NSEEL_addfunc_varparm("gfx_setfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setfont);
  371. NSEEL_addfunc_varparm("gfx_getfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getfont);
  372. NSEEL_addfunc_varparm("gfx_set", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_set);
  373. NSEEL_addfunc_varparm("gfx_getdropfile", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getdropfile);
  374. NSEEL_addfunc_varparm("gfx_getsyscol", 0, NSEEL_PProc_THIS, &ysfx_api_gfx_getsyscol);
  375. NSEEL_addfunc_retval("gfx_getchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getchar);
  376. }
  377. //------------------------------------------------------------------------------
  378. // SWELL helpers
  379. #if !defined(YSFX_NO_GFX)
  380. // implement these GL functions on SWELL targets where they are missing
  381. #if !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)
  382. void SWELL_SetViewGL(HWND h, char wantGL)
  383. {
  384. }
  385. bool SWELL_GetViewGL(HWND h)
  386. {
  387. return false;
  388. }
  389. bool SWELL_SetGLContextToView(HWND h)
  390. {
  391. return false;
  392. }
  393. #endif // !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)
  394. #endif