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.

ysfx_api_gfx.cpp 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. #endif
  56. //------------------------------------------------------------------------------
  57. #if !defined(YSFX_NO_GFX)
  58. static bool eel_lice_get_filename_for_string(void *opaque, EEL_F idx, WDL_FastString *fs, int iswrite)
  59. {
  60. if (iswrite)
  61. return false; // this is neither supported nor used
  62. ysfx_t *fx = (ysfx_t *)opaque;
  63. std::string filepath;
  64. if (!ysfx_find_data_file(fx, &idx, filepath))
  65. return false;
  66. if (fs) fs->Set(filepath.data(), (uint32_t)filepath.size());
  67. return true;
  68. }
  69. #define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) \
  70. eel_lice_get_filename_for_string(opaque, (idx), (fs), (p))
  71. #endif
  72. //------------------------------------------------------------------------------
  73. #if !defined(YSFX_NO_GFX)
  74. # include "ysfx_api_gfx_lice.hpp"
  75. #else
  76. # include "ysfx_api_gfx_dummy.hpp"
  77. #endif
  78. //------------------------------------------------------------------------------
  79. #if !defined(YSFX_NO_GFX)
  80. static bool translate_special_key(uint32_t uni_key, uint32_t &jsfx_key)
  81. {
  82. auto key_c = [](uint8_t a, uint8_t b, uint8_t c, uint8_t d) -> uint32_t {
  83. return a | (b << 8) | (c << 16) | (d << 24);
  84. };
  85. switch (uni_key) {
  86. default: return false;
  87. case ysfx_key_delete: jsfx_key = key_c('d', 'e', 'l', 0); break;
  88. case ysfx_key_f1: jsfx_key = key_c('f', '1', 0, 0); break;
  89. case ysfx_key_f2: jsfx_key = key_c('f', '2', 0, 0); break;
  90. case ysfx_key_f3: jsfx_key = key_c('f', '3', 0, 0); break;
  91. case ysfx_key_f4: jsfx_key = key_c('f', '4', 0, 0); break;
  92. case ysfx_key_f5: jsfx_key = key_c('f', '5', 0, 0); break;
  93. case ysfx_key_f6: jsfx_key = key_c('f', '6', 0, 0); break;
  94. case ysfx_key_f7: jsfx_key = key_c('f', '7', 0, 0); break;
  95. case ysfx_key_f8: jsfx_key = key_c('f', '8', 0, 0); break;
  96. case ysfx_key_f9: jsfx_key = key_c('f', '9', 0, 0); break;
  97. case ysfx_key_f10: jsfx_key = key_c('f', '1', '0', 0); break;
  98. case ysfx_key_f11: jsfx_key = key_c('f', '1', '1', 0); break;
  99. case ysfx_key_f12: jsfx_key = key_c('f', '1', '2', 0); break;
  100. case ysfx_key_left: jsfx_key = key_c('l', 'e', 'f', 't'); break;
  101. case ysfx_key_up: jsfx_key = key_c('u', 'p', 0, 0); break;
  102. case ysfx_key_right: jsfx_key = key_c('r', 'g', 'h', 't'); break;
  103. case ysfx_key_down: jsfx_key = key_c('d', 'o', 'w', 'n'); break;
  104. case ysfx_key_page_up: jsfx_key = key_c('p', 'g', 'u', 'p'); break;
  105. case ysfx_key_page_down: jsfx_key = key_c('p', 'g', 'd', 'n'); break;
  106. case ysfx_key_home: jsfx_key = key_c('h', 'o', 'm', 'e'); break;
  107. case ysfx_key_end: jsfx_key = key_c('e', 'n', 'd', 0); break;
  108. case ysfx_key_insert: jsfx_key = key_c('i', 'n', 's', 0); break;
  109. }
  110. return true;
  111. }
  112. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getchar(void *opaque, EEL_F *p)
  113. {
  114. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  115. if (!state)
  116. return 0;
  117. if (*p >= 1/*2*/) { // NOTE(jpc) this is 2.0 originally, which seems wrong
  118. if (*p == 65536) {
  119. // TODO implement window flags
  120. return 0;
  121. }
  122. // current key down status
  123. uint32_t key = (uint32_t)*p;
  124. uint32_t key_id;
  125. if (translate_special_key(key, key))
  126. key_id = key;
  127. else if (key < 256)
  128. key_id = ysfx::latin1_tolower(key);
  129. else // support the Latin-1 character set only
  130. return 0;
  131. return (EEL_F)(state->keys_pressed.find(key_id) != state->keys_pressed.end());
  132. }
  133. if (!state->input_queue.empty()) {
  134. uint32_t key = state->input_queue.front();
  135. state->input_queue.pop();
  136. return (EEL_F)key;
  137. }
  138. return 0;
  139. }
  140. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_showmenu(void *opaque, INT_PTR nparms, EEL_F **parms)
  141. {
  142. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  143. if (!state || !state->show_menu)
  144. return 0;
  145. ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;
  146. std::string desc;
  147. if (!ysfx_string_get(fx, *parms[0], desc) || desc.empty())
  148. return 0;
  149. int32_t x = (int32_t)*fx->var.gfx_x;
  150. int32_t y = (int32_t)*fx->var.gfx_y;
  151. return state->show_menu(state->callback_data, desc.c_str(), x, y);
  152. }
  153. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setcursor(void *opaque, INT_PTR nparms, EEL_F **parms)
  154. {
  155. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  156. if (!state || !state->set_cursor)
  157. return 0;
  158. int32_t id = (int32_t)*parms[0];
  159. state->set_cursor(state->callback_data, id);
  160. return 0;
  161. }
  162. static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms)
  163. {
  164. ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque);
  165. if (!state || !state->get_drop_file)
  166. return 0;
  167. const int32_t idx = (int)*parms[0];
  168. if (idx < 0) {
  169. state->get_drop_file(state->callback_data, -1);
  170. return 0;
  171. }
  172. const char *file = state->get_drop_file(state->callback_data, idx);
  173. if (!file)
  174. return 0;
  175. if (np > 1) {
  176. ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx;
  177. ysfx_string_set(fx, *parms[1], file);
  178. }
  179. return 1;
  180. }
  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_blitext", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blitext);
  368. NSEEL_addfunc_varparm("gfx_blit", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_blit2);
  369. NSEEL_addfunc_varparm("gfx_setfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setfont);
  370. NSEEL_addfunc_varparm("gfx_getfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getfont);
  371. NSEEL_addfunc_varparm("gfx_set", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_set);
  372. NSEEL_addfunc_varparm("gfx_getdropfile", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getdropfile);
  373. NSEEL_addfunc_varparm("gfx_getsyscol", 0, NSEEL_PProc_THIS, &ysfx_api_gfx_getsyscol);
  374. NSEEL_addfunc_retval("gfx_getchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getchar);
  375. }
  376. //------------------------------------------------------------------------------
  377. // SWELL helpers
  378. #if !defined(YSFX_NO_GFX)
  379. // implement these GL functions on SWELL targets where they are missing
  380. #if !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)
  381. void SWELL_SetViewGL(HWND h, char wantGL)
  382. {
  383. }
  384. bool SWELL_GetViewGL(HWND h)
  385. {
  386. return false;
  387. }
  388. bool SWELL_SetGLContextToView(HWND h)
  389. {
  390. return false;
  391. }
  392. #endif // !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX)
  393. #endif