1 // Copyright 2021 Jean Pierre Cimalando
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // SPDX-License-Identifier: Apache-2.0
19 #include "ysfx_config.hpp"
20 #include "ysfx_eel_utils.hpp"
21 #include <type_traits>
31 static_assert(std::is_same
<EEL_F
, ysfx_real
>::value
,
32 "ysfx_real is incorrectly defined");
35 ysfx_max_file_handles
= 64, // change if it needs more
38 //------------------------------------------------------------------------------
39 static thread_local ysfx_thread_id_t ysfx_thread_id
;
41 ysfx_thread_id_t
ysfx_get_thread_id()
43 return ysfx_thread_id
;
46 void ysfx_set_thread_id(ysfx_thread_id_t id
)
51 //------------------------------------------------------------------------------
52 struct ysfx_api_initializer
{
54 ysfx_api_initializer();
55 ~ysfx_api_initializer();
57 static void init_once();
60 ysfx_api_initializer::ysfx_api_initializer()
62 if (NSEEL_init() != 0)
63 throw std::runtime_error("NSEEL_init");
66 ysfx_api_init_reaper();
71 ysfx_api_initializer::~ysfx_api_initializer()
76 void ysfx_api_initializer::init_once()
78 static ysfx_api_initializer init
;
81 //------------------------------------------------------------------------------
82 ysfx_t
*ysfx_new(ysfx_config_t
*config
)
84 ysfx_u fx
{new ysfx_t
};
86 ysfx_config_add_ref(config
);
87 fx
->config
.reset(config
);
89 fx
->string_ctx
.reset(ysfx_eel_string_context_new());
91 ysfx_api_initializer::init_once();
93 NSEEL_VMCTX vm
= NSEEL_VM_alloc();
95 throw std::bad_alloc();
98 NSEEL_VM_SetCustomFuncThis(vm
, fx
.get());
100 ysfx_eel_string_initvm(vm
);
102 #if !defined(YSFX_NO_GFX)
103 fx
->gfx
.state
.reset(ysfx_gfx_state_new(fx
.get()));
106 auto var_resolver
= [](void *userdata
, const char *name
) -> EEL_F
* {
107 ysfx_t
*fx
= (ysfx_t
*)userdata
;
108 auto it
= fx
->source
.slider_alias
.find(name
);
109 if (it
!= fx
->source
.slider_alias
.end())
110 return fx
->var
.slider
[it
->second
];
113 NSEEL_VM_set_var_resolver(vm
, var_resolver
, fx
.get());
115 for (uint32_t i
= 0; i
< ysfx_max_channels
; ++i
) {
116 std::string name
= "spl" + std::to_string(i
);
117 EEL_F
*var
= NSEEL_VM_regvar(vm
, name
.c_str());
118 *(fx
->var
.spl
[i
] = var
) = 0;
120 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
) {
121 std::string name
= "slider" + std::to_string(i
+ 1);
122 EEL_F
*var
= NSEEL_VM_regvar(vm
, name
.c_str());
123 *(fx
->var
.slider
[i
] = var
) = 0;
124 fx
->slider_of_var
[var
] = i
;
127 #define AUTOVAR(name, value) *(fx->var.name = NSEEL_VM_regvar(vm, #name)) = (value)
128 AUTOVAR(srate
, fx
->sample_rate
);
129 AUTOVAR(num_ch
, fx
->valid_input_channels
);
130 AUTOVAR(samplesblock
, fx
->block_size
);
133 AUTOVAR(play_state
, 1);
134 AUTOVAR(play_position
, 0);
135 AUTOVAR(beat_position
, 0);
137 AUTOVAR(ts_denom
, 4);
138 AUTOVAR(ext_noinit
, 0);
139 AUTOVAR(ext_nodenorm
, 0);
140 AUTOVAR(ext_midi_bus
, 0);
141 AUTOVAR(midi_bus
, 0);
142 AUTOVAR(pdc_delay
, 0);
143 AUTOVAR(pdc_bot_ch
, 0);
144 AUTOVAR(pdc_top_ch
, 0);
145 AUTOVAR(pdc_midi
, 0);
156 AUTOVAR(gfx_mode
, 0);
157 AUTOVAR(gfx_clear
, 0);
158 AUTOVAR(gfx_texth
, 0);
159 AUTOVAR(gfx_dest
, 0);
160 AUTOVAR(gfx_ext_retina
, 0);
163 AUTOVAR(mouse_cap
, 0);
164 AUTOVAR(mouse_wheel
, 0);
165 AUTOVAR(mouse_hwheel
, 0);
168 fx
->midi
.in
.reset(new ysfx_midi_buffer_t
);
169 fx
->midi
.out
.reset(new ysfx_midi_buffer_t
);
170 ysfx_set_midi_capacity(fx
.get(), 1024, true);
172 fx
->file
.list
.reserve(16);
173 fx
->file
.list
.emplace_back(new ysfx_serializer_t(fx
->vm
.get()));
178 void ysfx_free(ysfx_t
*fx
)
183 if (fx
->ref_count
.fetch_sub(1, std::memory_order_acq_rel
) == 1)
187 void ysfx_add_ref(ysfx_t
*fx
)
189 fx
->ref_count
.fetch_add(1, std::memory_order_relaxed
);
192 ysfx_config_t
*ysfx_get_config(ysfx_t
*fx
)
194 return fx
->config
.get();
197 bool ysfx_load_file(ysfx_t
*fx
, const char *filepath
, uint32_t loadopts
)
201 //--------------------------------------------------------------------------
204 auto fail_guard
= ysfx::defer([fx
]() { ysfx_unload_source(fx
); });
206 //--------------------------------------------------------------------------
207 // load the main file
209 ysfx::file_uid main_uid
;
212 ysfx_source_unit_u main
{new ysfx_source_unit_t
};
214 ysfx::FILE_u stream
{ysfx::fopen_utf8(filepath
, "rb")};
215 if (!stream
|| !ysfx::get_stream_file_uid(stream
.get(), main_uid
)) {
216 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s: cannot open file for reading", ysfx::path_file_name(filepath
).c_str());
220 ysfx::stdio_text_reader
reader(stream
.get());
222 ysfx_parse_error error
;
223 if (!ysfx_parse_toplevel(reader
, main
->toplevel
, &error
)) {
224 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s:%u: %s", ysfx::path_file_name(filepath
).c_str(), error
.line
+ 1, error
.message
.c_str());
227 ysfx_parse_header(main
->toplevel
.header
.get(), main
->header
);
230 if (main
->header
.desc
.empty()) {
231 ysfx_logf(*fx
->config
, ysfx_log_warning
, "%s: the required `desc` field is missing", ysfx::path_file_name(filepath
).c_str());
232 main
->header
.desc
= ysfx::path_file_name(filepath
);
235 if (loadopts
& ysfx_load_ignoring_imports
)
236 main
->header
.imports
.clear();
238 // if no pins are specified and we have @sample, the default is stereo
239 if (main
->toplevel
.sample
&& !main
->header
.explicit_pins
&&
240 main
->header
.in_pins
.empty() && main
->header
.out_pins
.empty())
242 main
->header
.in_pins
= {"JS input 1", "JS input 2"};
243 main
->header
.out_pins
= {"JS output 1", "JS output 2"};
246 // register variables aliased to sliders
247 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
) {
248 if (main
->header
.sliders
[i
].exists
) {
249 if (!main
->header
.sliders
[i
].var
.empty())
250 fx
->source
.slider_alias
.insert({main
->header
.sliders
[i
].var
, i
});
254 fx
->source
.main
= std::move(main
);
255 fx
->source
.main_file_path
.assign(filepath
);
257 // find the bank file, if present
259 ysfx::path_directory(filepath
).c_str(),
260 (ysfx::path_file_name(filepath
) + ".rpl").c_str(),
261 fx
->source
.bank_path
);
263 // fill the file enums with the contents of directories
264 ysfx_fill_file_enums(fx
);
266 // find incorrect enums and fix them
267 ysfx_fix_invalid_enums(fx
);
269 // set the initial mask of visible sliders
270 ysfx_update_slider_visibility_mask(fx
);
273 //--------------------------------------------------------------------------
276 // we load the imports recursively using post-order
278 static constexpr uint32_t max_import_level
= 32;
279 std::set
<ysfx::file_uid
> seen
;
281 std::function
<bool(const std::string
&, const std::string
&, uint32_t)> do_next_import
=
282 [fx
, &seen
, &do_next_import
]
283 (const std::string
&name
, const std::string
&origin
, uint32_t level
) -> bool
285 if (level
>= max_import_level
) {
286 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s: %s", ysfx::path_file_name(origin
.c_str()).c_str(), "too many import levels");
290 std::string imported_path
= ysfx_resolve_import_path(fx
, name
, origin
);
291 if (imported_path
.empty()) {
292 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s: cannot find import: %s", ysfx::path_file_name(origin
.c_str()).c_str(), name
.c_str());
296 ysfx::file_uid imported_uid
;
297 ysfx::FILE_u stream
{ysfx::fopen_utf8(imported_path
.c_str(), "rb")};
298 if (!stream
|| !ysfx::get_stream_file_uid(stream
.get(), imported_uid
)) {
299 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s: cannot open file for reading", ysfx::path_file_name(imported_path
.c_str()).c_str());
303 // this file was already visited, skip
304 if (!seen
.insert(imported_uid
).second
)
308 ysfx_source_unit_u unit
{new ysfx_source_unit_t
};
309 ysfx::stdio_text_reader
reader(stream
.get());
311 ysfx_parse_error error
;
312 if (!ysfx_parse_toplevel(reader
, unit
->toplevel
, &error
)) {
313 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s:%u: %s", ysfx::path_file_name(imported_path
.c_str()).c_str(), error
.line
+ 1, error
.message
.c_str());
316 ysfx_parse_header(unit
->toplevel
.header
.get(), unit
->header
);
318 // process the imported dependencies, *first*
319 for (const std::string
&name
: unit
->header
.imports
) {
320 if (!do_next_import(name
, imported_path
.c_str(), level
+ 1))
324 // add it to the import sources, *second*
325 fx
->source
.imports
.push_back(std::move(unit
));
330 for (const std::string
&name
: fx
->source
.main
->header
.imports
) {
331 if (!do_next_import(name
, filepath
, 0))
335 //--------------------------------------------------------------------------
336 // initialize the sliders to defaults
338 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
)
339 *fx
->var
.slider
[i
] = fx
->source
.main
->header
.sliders
[i
].def
;
341 //--------------------------------------------------------------------------
347 bool ysfx_compile(ysfx_t
*fx
, uint32_t compileopts
)
349 ysfx_unload_code(fx
);
351 if (!fx
->source
.main
) {
352 ysfx_logf(*fx
->config
, ysfx_log_error
, "???: no source is loaded, cannot compile");
356 //--------------------------------------------------------------------------
359 auto fail_guard
= ysfx::defer([fx
]() { ysfx_unload_code(fx
); });
361 //--------------------------------------------------------------------------
364 NSEEL_VMCTX vm
= fx
->vm
.get();
367 uint32_t maxmem
= fx
->source
.main
->header
.options
.maxmem
;
369 maxmem
= 8 * 1024 * 1024;
370 if (maxmem
> 32 * 1024 * 1024)
371 maxmem
= 32 * 1024 * 1024;
373 NSEEL_VM_setramsize(vm
, (int)maxmem
);
376 //--------------------------------------------------------------------------
379 auto compile_section
=
380 [fx
](ysfx_section_t
*section
, const char *name
, NSEEL_CODEHANDLE_u
&dest
) -> bool
382 NSEEL_VMCTX vm
= fx
->vm
.get();
383 if (section
->text
.empty()) {
384 // NOTE: check for empty source, which would return null code
388 NSEEL_CODEHANDLE_u code
{NSEEL_code_compile_ex(vm
, section
->text
.c_str(), section
->line_offset
, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS
)};
390 ysfx_logf(*fx
->config
, ysfx_log_error
, "%s: %s", name
, NSEEL_code_getcodeerror(vm
));
393 dest
= std::move(code
);
397 // compile the multiple @init sections, imports first
399 std::vector
<ysfx_section_t
*> secs
;
400 secs
.reserve(fx
->source
.imports
.size() + 1);
402 // collect init sections: imports first, main second
403 for (size_t i
= 0; i
< fx
->source
.imports
.size(); ++i
)
404 secs
.push_back(fx
->source
.imports
[i
]->toplevel
.init
.get());
405 secs
.push_back(fx
->source
.main
->toplevel
.init
.get());
407 for (ysfx_section_t
*sec
: secs
) {
408 NSEEL_CODEHANDLE_u code
;
409 if (sec
&& !compile_section(sec
, "@init", code
))
411 fx
->code
.init
.push_back(std::move(code
));
415 // compile the other sections, single
416 // a non-@init section is searched in the main file first;
417 // if not found, it's inherited from the first import which has it.
418 ysfx_section_t
*slider
= ysfx_search_section(fx
, ysfx_section_slider
);
419 ysfx_section_t
*block
= ysfx_search_section(fx
, ysfx_section_block
);
420 ysfx_section_t
*sample
= ysfx_search_section(fx
, ysfx_section_sample
);
421 ysfx_section_t
*gfx
= nullptr;
422 ysfx_section_t
*serialize
= nullptr;
423 if ((compileopts
& ysfx_compile_no_gfx
) == 0)
424 gfx
= ysfx_search_section(fx
, ysfx_section_gfx
);
425 if ((compileopts
& ysfx_compile_no_serialize
) == 0)
426 serialize
= ysfx_search_section(fx
, ysfx_section_serialize
);
428 if (slider
&& !compile_section(slider
, "@slider", fx
->code
.slider
))
430 if (block
&& !compile_section(block
, "@block", fx
->code
.block
))
432 if (sample
&& !compile_section(sample
, "@sample", fx
->code
.sample
))
434 if (gfx
&& !compile_section(gfx
, "@gfx", fx
->code
.gfx
))
436 if (serialize
&& !compile_section(serialize
, "@serialize", fx
->code
.serialize
))
439 fx
->code
.compiled
= true;
440 fx
->is_freshly_compiled
= true;
441 fx
->must_compute_init
= true;
444 ysfx_eel_string_context_update_named_vars(fx
->string_ctx
.get(), vm
);
450 bool ysfx_is_compiled(ysfx_t
*fx
)
452 return fx
->code
.compiled
;
455 void ysfx_unload_source(ysfx_t
*fx
)
460 void ysfx_unload_code(ysfx_t
*fx
)
462 #if !defined(YSFX_NO_GFX)
463 // get rid of gfx first, to prevent a UI thread from trying
464 // to access VM and invoke code
466 std::lock_guard
<ysfx::mutex
> lock
{fx
->gfx
.mutex
};
467 fx
->gfx
.ready
= false;
468 fx
->gfx
.wants_retina
= false;
469 fx
->gfx
.must_init
.store(false);
475 fx
->is_freshly_compiled
= false;
476 fx
->must_compute_init
= false;
477 fx
->must_compute_slider
= false;
479 NSEEL_VMCTX vm
= fx
->vm
.get();
480 NSEEL_code_compile_ex(vm
, nullptr, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET
);
481 NSEEL_VM_remove_unused_vars(vm
);
482 NSEEL_VM_remove_all_nonreg_vars(vm
);
483 NSEEL_VM_freeRAM(vm
);
486 void ysfx_unload(ysfx_t
*fx
)
488 ysfx_unload_code(fx
);
489 ysfx_unload_source(fx
);
492 bool ysfx_is_loaded(ysfx_t
*fx
)
494 return fx
->source
.main
!= nullptr;
497 void ysfx_fill_file_enums(ysfx_t
*fx
)
499 if (fx
->config
->data_root
.empty())
502 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
) {
503 ysfx_slider_t
&slider
= fx
->source
.main
->header
.sliders
[i
];
504 if (slider
.path
.empty())
507 std::string dirpath
= ysfx::path_ensure_final_separator((fx
->config
->data_root
+ slider
.path
).c_str());
508 ysfx::string_list entries
= ysfx::list_directory(dirpath
.c_str());
510 for (const std::string
&filename
: entries
) {
511 if (!filename
.empty() && ysfx::is_path_separator(filename
.back()))
514 std::string filepath
= dirpath
+ filename
;
516 ysfx_file_type_t ftype
= ysfx_detect_file_type(fx
, filepath
.c_str(), nullptr);
517 if (ftype
== ysfx_file_type_none
)
520 slider
.enum_names
.push_back(std::move(filename
));
523 if (!slider
.enum_names
.empty())
524 slider
.max
= (EEL_F
)(slider
.enum_names
.size() - 1);
528 void ysfx_fix_invalid_enums(ysfx_t
*fx
)
530 //NOTE: regardless of the range of enum sliders in source, it is <0,N-1,1>
531 // if there is a mismatch, correct and output a warning
533 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
) {
534 ysfx_slider_t
&slider
= fx
->source
.main
->header
.sliders
[i
];
538 uint32_t count
= (uint32_t)slider
.enum_names
.size();
540 bool is_file
= !slider
.path
.empty();
541 ysfx_logf(*fx
->config
, ysfx_log_warning
, "slider%u: the enumeration does not contain any %s", i
+ 1, is_file
? "files" : "items");
542 slider
.enum_names
.emplace_back();
547 else if (slider
.min
!= 0 || slider
.inc
!= 1 || slider
.max
!= (EEL_F
)(count
- 1)) {
548 ysfx_logf(*fx
->config
, ysfx_log_warning
, "slider%u: the enumeration has an invalid range", i
+ 1);
550 slider
.max
= (EEL_F
)(count
- 1);
556 const char *ysfx_get_name(ysfx_t
*fx
)
558 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
561 return main
->header
.desc
.c_str();
564 const char *ysfx_get_file_path(ysfx_t
*fx
)
566 return fx
->source
.main_file_path
.c_str();
569 const char *ysfx_get_author(ysfx_t
*fx
)
571 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
574 return main
->header
.author
.c_str();
577 uint32_t ysfx_get_tags(ysfx_t
*fx
, const char **dest
, uint32_t destsize
)
579 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
583 uint32_t count
= (uint32_t)main
->header
.tags
.size();
585 uint32_t copysize
= (destsize
< count
) ? destsize
: count
;
586 for (uint32_t i
= 0; i
< copysize
; ++i
)
587 dest
[i
] = main
->header
.tags
[i
].c_str();
592 const char *ysfx_get_tag(ysfx_t
*fx
, uint32_t index
)
594 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
595 if (!main
|| index
>= main
->header
.tags
.size())
597 return main
->header
.tags
[index
].c_str();
600 uint32_t ysfx_get_num_inputs(ysfx_t
*fx
)
602 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
605 return (uint32_t)main
->header
.in_pins
.size();
608 uint32_t ysfx_get_num_outputs(ysfx_t
*fx
)
610 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
613 return (uint32_t)main
->header
.out_pins
.size();
616 const char *ysfx_get_input_name(ysfx_t
*fx
, uint32_t index
)
618 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
619 if (!main
|| index
>= main
->header
.in_pins
.size())
621 return main
->header
.in_pins
[index
].c_str();
624 const char *ysfx_get_output_name(ysfx_t
*fx
, uint32_t index
)
626 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
627 if (!main
|| index
>= main
->header
.out_pins
.size())
629 return main
->header
.out_pins
[index
].c_str();
632 bool ysfx_wants_meters(ysfx_t
*fx
)
634 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
638 return !main
->header
.options
.no_meter
;
641 bool ysfx_get_gfx_dim(ysfx_t
*fx
, uint32_t dim
[2])
643 ysfx_toplevel_t
*origin
= nullptr;
644 ysfx_section_t
*sec
= ysfx_search_section(fx
, ysfx_section_gfx
, &origin
);
655 dim
[0] = origin
->gfx_w
;
656 dim
[1] = origin
->gfx_h
;
661 ysfx_section_t
*ysfx_search_section(ysfx_t
*fx
, uint32_t type
, ysfx_toplevel_t
**origin
)
663 if (!fx
->source
.main
)
667 [fx
](ysfx_section_t
*(*test
)(ysfx_toplevel_t
&tl
), ysfx_toplevel_t
**origin
) -> ysfx_section_t
*
669 ysfx_toplevel_t
*tl
= &fx
->source
.main
->toplevel
;
670 ysfx_section_t
*sec
= test(*tl
);
671 for (size_t i
= 0; !sec
&& i
< fx
->source
.imports
.size(); ++i
) {
672 tl
= &fx
->source
.imports
[i
]->toplevel
;
676 *origin
= sec
? tl
: nullptr;
681 case ysfx_section_init
:
682 return search([](ysfx_toplevel_t
&tl
) { return tl
.init
.get(); }, origin
);
683 case ysfx_section_slider
:
684 return search([](ysfx_toplevel_t
&tl
) { return tl
.slider
.get(); }, origin
);
685 case ysfx_section_block
:
686 return search([](ysfx_toplevel_t
&tl
) { return tl
.block
.get(); }, origin
);
687 case ysfx_section_sample
:
688 return search([](ysfx_toplevel_t
&tl
) { return tl
.sample
.get(); }, origin
);
689 case ysfx_section_gfx
:
690 return search([](ysfx_toplevel_t
&tl
) { return tl
.gfx
.get(); }, origin
);
691 case ysfx_section_serialize
:
692 return search([](ysfx_toplevel_t
&tl
) { return tl
.serialize
.get(); }, origin
);
698 bool ysfx_has_section(ysfx_t
*fx
, uint32_t type
)
700 return ysfx_search_section(fx
, type
) != nullptr;
703 bool ysfx_slider_exists(ysfx_t
*fx
, uint32_t index
)
705 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
706 if (index
>= ysfx_max_sliders
|| !main
)
709 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
710 return slider
.exists
;
713 const char *ysfx_slider_get_name(ysfx_t
*fx
, uint32_t index
)
715 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
716 if (index
>= ysfx_max_sliders
|| !main
)
719 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
720 return slider
.desc
.c_str();
723 bool ysfx_slider_get_range(ysfx_t
*fx
, uint32_t index
, ysfx_slider_range_t
*range
)
725 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
726 if (index
>= ysfx_max_sliders
|| !main
)
729 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
730 range
->def
= slider
.def
;
731 range
->min
= slider
.min
;
732 range
->max
= slider
.max
;
733 range
->inc
= slider
.inc
;
737 bool ysfx_slider_is_enum(ysfx_t
*fx
, uint32_t index
)
739 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
740 if (index
>= ysfx_max_sliders
|| !main
)
743 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
744 return slider
.is_enum
;
747 uint32_t ysfx_slider_get_enum_names(ysfx_t
*fx
, uint32_t index
, const char **dest
, uint32_t destsize
)
749 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
750 if (index
>= ysfx_max_sliders
|| !main
)
753 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
754 uint32_t count
= (uint32_t)slider
.enum_names
.size();
756 uint32_t copysize
= (destsize
< count
) ? destsize
: count
;
757 for (uint32_t i
= 0; i
< copysize
; ++i
)
758 dest
[i
] = slider
.enum_names
[i
].c_str();
763 const char *ysfx_slider_get_enum_name(ysfx_t
*fx
, uint32_t slider_index
, uint32_t enum_index
)
765 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
766 if (slider_index
>= ysfx_max_sliders
|| !main
)
769 ysfx_slider_t
&slider
= main
->header
.sliders
[slider_index
];
770 if (enum_index
>= slider
.enum_names
.size())
773 return slider
.enum_names
[enum_index
].c_str();
776 bool ysfx_slider_is_path(ysfx_t
*fx
, uint32_t index
)
778 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
779 if (index
>= ysfx_max_sliders
|| !main
)
782 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
783 return !slider
.path
.empty();
786 bool ysfx_slider_is_initially_visible(ysfx_t
*fx
, uint32_t index
)
788 ysfx_source_unit_t
*main
= fx
->source
.main
.get();
789 if (index
>= ysfx_max_sliders
|| !main
)
792 ysfx_slider_t
&slider
= main
->header
.sliders
[index
];
793 return slider
.initially_visible
;
796 ysfx_real
ysfx_slider_get_value(ysfx_t
*fx
, uint32_t index
)
798 if (index
>= ysfx_max_sliders
)
800 return *fx
->var
.slider
[index
];
803 void ysfx_slider_set_value(ysfx_t
*fx
, uint32_t index
, ysfx_real value
)
805 if (index
>= ysfx_max_sliders
)
807 if (*fx
->var
.slider
[index
] != value
) {
808 *fx
->var
.slider
[index
] = value
;
809 fx
->must_compute_slider
= true;
813 std::string
ysfx_resolve_import_path(ysfx_t
*fx
, const std::string
&name
, const std::string
&origin
)
815 std::vector
<std::string
> dirs
;
817 // create the list of search directories
822 dirs
.push_back(ysfx::path_directory(origin
.c_str()));
824 const std::string
&import_root
= fx
->config
->import_root
;
825 if (!import_root
.empty() && dirs
[0] != import_root
)
826 dirs
.push_back(import_root
);
829 // the search should be case-insensitive
830 static constexpr bool nocase
= true;
832 static auto *check_existence
= +[](const std::string
&dir
, const std::string
&file
, std::string
&result_path
) -> int {
834 return ysfx::case_resolve(dir
.c_str(), file
.c_str(), result_path
);
836 result_path
= dir
+ file
;
837 return ysfx::exists(result_path
.c_str());
841 // search for the file in these directories directly
842 for (const std::string
&dir
: dirs
) {
843 std::string resolved
;
844 if (check_existence(dir
, name
, resolved
))
848 // search for the file recursively
849 for (const std::string
&dir
: dirs
) {
851 const std::string
*name
= nullptr;
852 std::string resolved
;
856 auto visit
= [](const std::string
&dir
, void *data
) -> bool {
857 visit_data
&vd
= *(visit_data
*)data
;
858 std::string resolved
;
859 if (check_existence(dir
, *vd
.name
, resolved
)) {
860 vd
.resolved
= std::move(resolved
);
865 ysfx::visit_directories(dir
.c_str(), +visit
, &vd
);
866 if (!vd
.resolved
.empty())
870 return std::string
{};
874 uint32_t ysfx_get_block_size(ysfx_t
*fx
)
876 return fx
->block_size
;
879 ysfx_real
ysfx_get_sample_rate(ysfx_t
*fx
)
881 return fx
->sample_rate
;
884 void ysfx_set_block_size(ysfx_t
*fx
, uint32_t blocksize
)
886 if (fx
->block_size
!= blocksize
) {
887 fx
->block_size
= blocksize
;
888 fx
->must_compute_init
= true;
892 void ysfx_set_sample_rate(ysfx_t
*fx
, ysfx_real samplerate
)
894 if (fx
->sample_rate
!= samplerate
) {
895 fx
->sample_rate
= samplerate
;
896 fx
->must_compute_init
= true;
900 void ysfx_set_midi_capacity(ysfx_t
*fx
, uint32_t capacity
, bool extensible
)
902 ysfx_midi_reserve(fx
->midi
.in
.get(), capacity
, extensible
);
903 ysfx_midi_reserve(fx
->midi
.out
.get(), capacity
, extensible
);
906 void ysfx_init(ysfx_t
*fx
)
908 if (!fx
->code
.compiled
)
911 *fx
->var
.samplesblock
= (EEL_F
)fx
->block_size
;
912 *fx
->var
.srate
= fx
->sample_rate
;
914 *fx
->var
.pdc_delay
= 0;
915 *fx
->var
.pdc_bot_ch
= 0;
916 *fx
->var
.pdc_top_ch
= 0;
917 *fx
->var
.pdc_midi
= 0;
919 if (fx
->is_freshly_compiled
) {
921 fx
->is_freshly_compiled
= false;
924 ysfx_clear_files(fx
);
926 for (size_t i
= 0; i
< fx
->code
.init
.size(); ++i
)
927 NSEEL_code_execute(fx
->code
.init
[i
].get());
929 fx
->must_compute_init
= false;
930 fx
->must_compute_slider
= true;
932 #if !defined(YSFX_NO_GFX)
933 // do initializations on next @gfx, on the gfx thread
934 // release-acquire order is for VM `gfx_*` variables and `wants_retina`
935 fx
->gfx
.wants_retina
= *fx
->var
.gfx_ext_retina
> 0;
936 fx
->gfx
.must_init
.store(true, std::memory_order_release
);
940 void ysfx_first_init(ysfx_t
*fx
)
942 assert(fx
->code
.compiled
);
943 assert(fx
->is_freshly_compiled
);
945 fx
->slider
.automate_mask
.store(0);
946 fx
->slider
.change_mask
.store(0);
947 ysfx_update_slider_visibility_mask(fx
);
950 ysfx_real
ysfx_get_pdc_delay(ysfx_t
*fx
)
952 ysfx_real value
= *fx
->var
.pdc_delay
;
953 return (value
> 0) ? value
: 0;
956 void ysfx_get_pdc_channels(ysfx_t
*fx
, uint32_t channels
[2])
961 int64_t bot
= (int64_t)*fx
->var
.pdc_bot_ch
;
962 bot
= (bot
> 0) ? bot
: 0;
963 bot
= (bot
< ysfx_max_channels
) ? bot
: ysfx_max_channels
;
964 channels
[0] = (uint32_t)bot
;
966 int64_t top
= (int64_t)*fx
->var
.pdc_top_ch
;
967 top
= (top
> bot
) ? top
: bot
;
968 top
= (top
< ysfx_max_channels
) ? top
: ysfx_max_channels
;
969 channels
[1] = (uint32_t)top
;
972 bool ysfx_get_pdc_midi(ysfx_t
*fx
)
974 return (bool)*fx
->var
.pdc_midi
;
977 void ysfx_update_slider_visibility_mask(ysfx_t
*fx
)
979 uint64_t visible
= 0;
980 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
) {
981 ysfx_slider_t
&slider
= fx
->source
.main
->header
.sliders
[i
];
982 visible
|= (uint64_t)slider
.initially_visible
<< i
;
984 fx
->slider
.visible_mask
.store(visible
);
987 void ysfx_set_time_info(ysfx_t
*fx
, const ysfx_time_info_t
*info
)
989 uint32_t prev_state
= (uint32_t)*fx
->var
.play_state
;
990 uint32_t new_state
= info
->playback_state
;
992 // unless `ext_noinit`, we should call @init every transport restart
993 if (!*fx
->var
.ext_noinit
) {
994 auto is_running
= [](uint32_t state
) {
995 return state
== ysfx_playback_playing
||
996 state
== ysfx_playback_recording
;
998 if (!is_running(prev_state
) && is_running(new_state
))
999 fx
->must_compute_init
= true;
1002 *fx
->var
.tempo
= info
->tempo
;
1003 *fx
->var
.play_state
= (EEL_F
)new_state
;
1004 *fx
->var
.play_position
= info
->time_position
;
1005 *fx
->var
.beat_position
= info
->beat_position
;
1006 *fx
->var
.ts_num
= (EEL_F
)info
->time_signature
[0];
1007 *fx
->var
.ts_denom
= (EEL_F
)info
->time_signature
[1];
1010 bool ysfx_send_midi(ysfx_t
*fx
, const ysfx_midi_event_t
*event
)
1012 return ysfx_midi_push(fx
->midi
.in
.get(), event
);
1015 bool ysfx_receive_midi(ysfx_t
*fx
, ysfx_midi_event_t
*event
)
1017 return ysfx_midi_get_next(fx
->midi
.out
.get(), event
);
1020 bool ysfx_receive_midi_from_bus(ysfx_t
*fx
, uint32_t bus
, ysfx_midi_event_t
*event
)
1022 return ysfx_midi_get_next_from_bus(fx
->midi
.out
.get(), 0, event
);
1025 uint32_t ysfx_current_midi_bus(ysfx_t
*fx
)
1028 if (*fx
->var
.ext_midi_bus
)
1029 bus
= (int32_t)*fx
->var
.midi_bus
;
1033 bool ysfx_send_trigger(ysfx_t
*fx
, uint32_t index
)
1035 if (index
>= ysfx_max_triggers
)
1038 fx
->triggers
|= 1u << index
;
1042 uint64_t ysfx_fetch_slider_changes(ysfx_t
*fx
)
1044 return fx
->slider
.change_mask
.exchange(0);
1047 uint64_t ysfx_fetch_slider_automations(ysfx_t
*fx
)
1049 return fx
->slider
.automate_mask
.exchange(0);
1052 uint64_t ysfx_get_slider_visibility(ysfx_t
*fx
)
1054 return fx
->slider
.visible_mask
.load();
1057 template <class Real
>
1058 void ysfx_process_generic(ysfx_t
*fx
, const Real
*const *ins
, Real
*const *outs
, uint32_t num_ins
, uint32_t num_outs
, uint32_t num_frames
)
1060 ysfx_set_thread_id(ysfx_thread_id_dsp
);
1062 // prepare MIDI input for reading, output for writing
1063 assert(fx
->midi
.in
->read_pos
== 0);
1064 ysfx_midi_clear(fx
->midi
.out
.get());
1067 *fx
->var
.trigger
= (EEL_F
)fx
->triggers
;
1070 if (!fx
->code
.compiled
) {
1071 for (uint32_t ch
= 0; ch
< num_outs
; ++ch
)
1072 memset(outs
[ch
], 0, num_frames
* sizeof(Real
));
1075 // compute @init if needed
1076 if (fx
->must_compute_init
)
1079 const uint32_t orig_num_outs
= num_outs
;
1080 const uint32_t num_code_ins
= (uint32_t)fx
->source
.main
->header
.in_pins
.size();
1081 const uint32_t num_code_outs
= (uint32_t)fx
->source
.main
->header
.out_pins
.size();
1082 if (num_ins
> num_code_ins
)
1083 num_ins
= num_code_ins
;
1084 if (num_outs
> num_code_outs
)
1085 num_outs
= num_code_outs
;
1087 fx
->valid_input_channels
= num_ins
;
1089 *fx
->var
.samplesblock
= (EEL_F
)num_frames
;
1090 *fx
->var
.num_ch
= (EEL_F
)num_ins
;
1092 // compute @slider if needed
1093 if (fx
->must_compute_slider
) {
1094 NSEEL_code_execute(fx
->code
.slider
.get());
1095 fx
->must_compute_slider
= false;
1099 NSEEL_code_execute(fx
->code
.block
.get());
1101 // compute @sample, once per frame
1102 if (fx
->code
.sample
) {
1103 EEL_F
**spl
= fx
->var
.spl
;
1104 for (uint32_t i
= 0; i
< num_frames
; ++i
) {
1105 for (uint32_t ch
= 0; ch
< num_ins
; ++ch
)
1106 *spl
[ch
] = (EEL_F
)ins
[ch
][i
];
1107 for (uint32_t ch
= num_ins
; ch
< num_code_ins
; ++ch
)
1109 NSEEL_code_execute(fx
->code
.sample
.get());
1110 for (uint32_t ch
= 0; ch
< num_outs
; ++ch
)
1111 outs
[ch
][i
] = (Real
)*spl
[ch
];
1115 // clear any output channels above the maximum count
1116 for (uint32_t ch
= num_outs
; ch
< orig_num_outs
; ++ch
)
1117 memset(outs
[ch
], 0, num_frames
* sizeof(Real
));
1120 // prepare MIDI input for writing, output for reading
1121 assert(fx
->midi
.out
->read_pos
== 0);
1122 ysfx_midi_clear(fx
->midi
.in
.get());
1124 ysfx_set_thread_id(ysfx_thread_id_none
);
1127 void ysfx_process_float(ysfx_t
*fx
, const float *const *ins
, float *const *outs
, uint32_t num_ins
, uint32_t num_outs
, uint32_t num_frames
)
1129 ysfx_process_generic
<float>(fx
, ins
, outs
, num_ins
, num_outs
, num_frames
);
1132 void ysfx_process_double(ysfx_t
*fx
, const double *const *ins
, double *const *outs
, uint32_t num_ins
, uint32_t num_outs
, uint32_t num_frames
)
1134 ysfx_process_generic
<double>(fx
, ins
, outs
, num_ins
, num_outs
, num_frames
);
1137 void ysfx_clear_files(ysfx_t
*fx
)
1139 std::lock_guard
<ysfx::mutex
> list_lock(fx
->file
.list_mutex
);
1141 // delete all except the serializer
1142 while (fx
->file
.list
.size() > 1) {
1143 ysfx_file_t
*file
= fx
->file
.list
.back().get();
1144 std::unique_ptr
<ysfx::mutex
> file_mutex
;
1145 std::unique_lock
<ysfx::mutex
> file_lock
;
1147 file_lock
= std::unique_lock
<ysfx::mutex
>{*fx
->file
.list
.back()->m_mutex
};
1148 file_mutex
= std::move(fx
->file
.list
.back()->m_mutex
);
1150 fx
->file
.list
.pop_back();
1154 ysfx_file_t
*ysfx_get_file(ysfx_t
*fx
, uint32_t handle
, std::unique_lock
<ysfx::mutex
> &lock
, std::unique_lock
<ysfx::mutex
> *list_lock
)
1156 std::unique_lock
<ysfx::mutex
> local_list_lock
;
1158 *list_lock
= std::unique_lock
<ysfx::mutex
>(fx
->file
.list_mutex
);
1160 local_list_lock
= std::unique_lock
<ysfx::mutex
>(fx
->file
.list_mutex
);
1161 if (handle
>= fx
->file
.list
.size())
1163 ysfx_file_t
*file
= fx
->file
.list
[handle
].get();
1166 lock
= std::unique_lock
<ysfx::mutex
>{*file
->m_mutex
};
1170 int32_t ysfx_insert_file(ysfx_t
*fx
, ysfx_file_t
*file
)
1172 std::lock_guard
<ysfx::mutex
> lock(fx
->file
.list_mutex
);
1175 size_t noneidx
= ~(size_t)0;
1176 size_t freeidx
= noneidx
;
1178 for (size_t i
= 0, n
= fx
->file
.list
.size(); i
< n
&& freeidx
== noneidx
; ++i
) {
1179 if (!fx
->file
.list
[i
])
1182 if (freeidx
!= noneidx
) {
1183 fx
->file
.list
[freeidx
].reset(file
);
1184 return (uint32_t)freeidx
;
1187 enum { max_file_handles
= 64 };
1189 size_t pos
= fx
->file
.list
.size();
1190 if (pos
>= max_file_handles
)
1193 fx
->file
.list
.emplace_back(file
);
1194 return (uint32_t)pos
;
1197 bool ysfx_load_state(ysfx_t
*fx
, ysfx_state_t
*state
)
1199 if (!fx
->code
.compiled
)
1202 // restore the serialization
1203 std::string
buffer((char *)state
->data
, state
->data_size
);
1205 // restore the sliders
1206 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
)
1207 *fx
->var
.slider
[i
] = fx
->source
.main
->header
.sliders
[i
].def
;
1209 for (uint32_t i
= 0, n
= state
->slider_count
; i
< n
; ++i
) {
1210 uint32_t j
= state
->sliders
[i
].index
;
1211 if (j
< ysfx_max_sliders
&& fx
->source
.main
->header
.sliders
[j
].exists
)
1212 *fx
->var
.slider
[j
] = state
->sliders
[i
].value
;
1214 fx
->must_compute_slider
= true;
1216 // invoke @serialize
1218 std::unique_lock
<ysfx::mutex
> lock
;
1219 ysfx_serializer_t
*serializer
= static_cast<ysfx_serializer_t
*>(ysfx_get_file(fx
, 0, lock
));
1221 serializer
->begin(false, buffer
);
1231 ysfx_state_t
*ysfx_save_state(ysfx_t
*fx
)
1233 if (!fx
->code
.compiled
)
1238 // invoke @serialize
1240 std::unique_lock
<ysfx::mutex
> lock
;
1241 ysfx_serializer_t
*serializer
= static_cast<ysfx_serializer_t
*>(ysfx_get_file(fx
, 0, lock
));
1243 serializer
->begin(true, buffer
);
1251 ysfx_state_u state
{new ysfx_state_t
};
1252 uint32_t slider_count
= 0;
1253 for (uint32_t i
= 0; i
< ysfx_max_sliders
; ++i
)
1254 slider_count
+= fx
->source
.main
->header
.sliders
[i
].exists
;
1256 state
->sliders
= new ysfx_state_slider_t
[slider_count
]{};
1257 state
->slider_count
= slider_count
;
1259 for (uint32_t i
= 0, j
= 0; i
< slider_count
; ++i
) {
1260 if (fx
->source
.main
->header
.sliders
[i
].exists
) {
1261 state
->sliders
[j
].index
= i
;
1262 state
->sliders
[j
].value
= *fx
->var
.slider
[i
];
1267 // save the serialization
1268 state
->data_size
= buffer
.size();
1269 state
->data
= new uint8_t[state
->data_size
];
1270 memcpy(state
->data
, buffer
.data(), state
->data_size
);
1273 return state
.release();
1276 void ysfx_state_free(ysfx_state_t
*state
)
1281 delete[] state
->sliders
;
1282 delete[] state
->data
;
1286 ysfx_state_t
*ysfx_state_dup(ysfx_state_t
*state_in
)
1291 ysfx_state_u state_out
{new ysfx_state_t
};
1293 uint32_t slider_count
= state_out
->slider_count
= state_in
->slider_count
;
1294 size_t data_size
= state_out
->data_size
= state_in
->data_size
;
1296 state_out
->sliders
= new ysfx_state_slider_t
[slider_count
];
1297 memcpy(state_out
->sliders
, state_in
->sliders
, slider_count
* sizeof(ysfx_state_slider_t
));
1299 state_out
->data
= new uint8_t[data_size
];
1300 memcpy(state_out
->data
, state_in
->data
, data_size
);
1302 return state_out
.release();
1305 void ysfx_serialize(ysfx_t
*fx
)
1307 if (fx
->code
.serialize
) {
1308 if (fx
->must_compute_init
)
1310 NSEEL_code_execute(fx
->code
.serialize
.get());
1314 uint32_t ysfx_get_slider_of_var(ysfx_t
*fx
, EEL_F
*var
)
1316 auto it
= fx
->slider_of_var
.find(var
);
1317 if (it
== fx
->slider_of_var
.end())
1318 return ~(uint32_t)0;
1322 const char *ysfx_get_bank_path(ysfx_t
*fx
)
1324 return fx
->source
.bank_path
.c_str();
1327 void ysfx_enum_vars(ysfx_t
*fx
, ysfx_enum_vars_callback_t
*callback
, void *userdata
)
1329 NSEEL_VM_enumallvars(fx
->vm
.get(), callback
, userdata
);
1332 ysfx_real
*ysfx_find_var(ysfx_t
*fx
, const char *name
)
1335 ysfx_real
*var
= nullptr;
1336 const char *name
= nullptr;
1340 auto callback
= [](const char *name
, EEL_F
*var
, void *userdata
) -> int {
1341 find_data
*fd
= (find_data
*)userdata
;
1342 if (strcmp(name
, fd
->name
) != 0)
1347 NSEEL_VM_enumallvars(fx
->vm
.get(), +callback
, &fd
);
1351 void ysfx_read_vmem(ysfx_t
*fx
, uint32_t addr
, ysfx_real
*dest
, uint32_t count
)
1353 ysfx_eel_ram_reader
reader(fx
->vm
.get(), addr
);
1354 for (uint32_t i
= 0; i
< count
; ++i
)
1355 dest
[i
] = reader
.read_next();
1358 bool ysfx_find_data_file(ysfx_t
*fx
, EEL_F
*file
, std::string
&result
)
1360 // 3 possibilities for file
1362 // - index of filename
1365 std::string filepart
;
1367 bool accept_absolute
= false;
1368 bool accept_relative
= false;
1370 int32_t index
= ysfx_eel_round
<int32_t>(*file
);
1371 uint32_t slideridx
= ysfx_get_slider_of_var(fx
, file
);
1372 ysfx_slider_t
*slider
= nullptr;
1374 if (slideridx
!= ~(uint32_t)0)
1375 slider
= &fx
->source
.main
->header
.sliders
[slideridx
];
1377 if (slider
&& !slider
->path
.empty()) {
1378 int32_t value
= ysfx_eel_round
<int32_t>(*fx
->var
.slider
[slideridx
]);
1379 if (value
< 0 || (uint32_t)value
>= slider
->enum_names
.size())
1382 filepart
= slider
->path
+ '/' + slider
->enum_names
[(uint32_t)value
];
1383 accept_relative
= true;
1385 else if (index
>= 0 && (uint32_t)index
< fx
->source
.main
->header
.filenames
.size()) {
1386 filepart
= fx
->source
.main
->header
.filenames
[(uint32_t)index
];
1387 accept_relative
= true;
1389 else if (ysfx_string_get(fx
, *file
, filepart
)) {
1390 accept_absolute
= true;
1391 accept_relative
= true;
1396 std::vector
<std::string
> filecandidates
;
1397 filecandidates
.reserve(2);
1399 if (accept_absolute
&& !ysfx::path_is_relative(filepart
.c_str()))
1400 filecandidates
.push_back(filepart
);
1401 else if (accept_relative
) {
1402 filecandidates
.push_back(ysfx::path_directory(fx
->source
.main_file_path
.c_str()) + filepart
);
1403 if (!fx
->config
->data_root
.empty())
1404 filecandidates
.push_back(fx
->config
->data_root
+ filepart
);
1407 for (const std::string
&filepath
: filecandidates
) {
1408 if (ysfx::exists(filepath
.c_str())) {
1409 result
.assign(filepath
);
1417 ysfx_file_type_t
ysfx_detect_file_type(ysfx_t
*fx
, const char *path
, void **fmtobj
)
1419 if (ysfx::path_has_suffix(path
, "txt"))
1420 return ysfx_file_type_txt
;
1421 if (ysfx::path_has_suffix(path
, "raw"))
1422 return ysfx_file_type_raw
;
1423 for (ysfx_audio_format_t
&fmt
: fx
->config
->audio_formats
) {
1424 if (fmt
.can_handle(path
)) {
1427 return ysfx_file_type_audio
;
1430 return ysfx_file_type_none
;
1433 void ysfx_gfx_setup(ysfx_t
*fx
, ysfx_gfx_config_t
*gc
)
1435 #if !defined(YSFX_NO_GFX)
1436 bool doinit
= false;
1437 ysfx_scoped_gfx_t scope
{fx
, doinit
};
1439 ysfx_gfx_state_set_bitmap(fx
->gfx
.state
.get(), gc
->pixels
, gc
->pixel_width
, gc
->pixel_height
, gc
->pixel_stride
);
1440 ysfx_real scale
= fx
->gfx
.wants_retina
? gc
->scale_factor
: 1;
1441 ysfx_gfx_state_set_scale_factor(fx
->gfx
.state
.get(), scale
);
1443 ysfx_gfx_state_set_callback_data(fx
->gfx
.state
.get(), gc
->user_data
);
1444 ysfx_gfx_state_set_show_menu_callback(fx
->gfx
.state
.get(), gc
->show_menu
);
1445 ysfx_gfx_state_set_set_cursor_callback(fx
->gfx
.state
.get(), gc
->set_cursor
);
1446 ysfx_gfx_state_set_get_drop_file_callback(fx
->gfx
.state
.get(), gc
->get_drop_file
);
1453 bool ysfx_gfx_wants_retina(ysfx_t
*fx
)
1455 #if !defined(YSFX_NO_GFX)
1456 return fx
->gfx
.wants_retina
;
1463 void ysfx_gfx_add_key(ysfx_t
*fx
, uint32_t mods
, uint32_t key
, bool press
)
1465 #if !defined(YSFX_NO_GFX)
1467 ysfx_scoped_gfx_t scope
{fx
, doinit
};
1472 ysfx_gfx_state_add_key(fx
->gfx
.state
.get(), mods
, key
, press
);
1480 void ysfx_gfx_update_mouse(ysfx_t
*fx
, uint32_t mods
, int32_t xpos
, int32_t ypos
, uint32_t buttons
, ysfx_real wheel
, ysfx_real hwheel
)
1482 #if !defined(YSFX_NO_GFX)
1484 ysfx_scoped_gfx_t scope
{fx
, doinit
};
1489 *fx
->var
.mouse_x
= (EEL_F
)xpos
;
1490 *fx
->var
.mouse_y
= (EEL_F
)ypos
;
1491 *fx
->var
.mouse_wheel
+= 120 * wheel
;
1492 *fx
->var
.mouse_hwheel
+= 120 * hwheel
;
1494 uint32_t mouse_cap
= 0;
1495 if (mods
& ysfx_mod_shift
)
1497 if (mods
& ysfx_mod_ctrl
)
1499 if (mods
& ysfx_mod_alt
)
1501 if (mods
& ysfx_mod_super
)
1503 if (buttons
& ysfx_button_left
)
1505 if (buttons
& ysfx_button_middle
)
1507 if (buttons
& ysfx_button_right
)
1509 *fx
->var
.mouse_cap
= (EEL_F
)mouse_cap
;
1522 bool ysfx_gfx_run(ysfx_t
*fx
)
1524 #if !defined(YSFX_NO_GFX)
1526 ysfx_scoped_gfx_t scope
{fx
, doinit
};
1531 ysfx_gfx_prepare(fx
);
1532 NSEEL_code_execute(fx
->code
.gfx
.get());
1534 return ysfx_gfx_state_is_dirty(fx
->gfx
.state
.get());