// frt_godot3.cc /* FRT - A Godot platform targeting single board computers Copyright (c) 2017-2022 Emanuele Fornara SPDX-License-Identifier: MIT */ #include "frt.h" #include "sdl2_adapter.h" #include "sdl2_godot_mapping.h" #include "drivers/gles2/rasterizer_gles2.h" //#define FRT_DL_SKIP #include "core/string/print_string.h" #include "drivers/unix/os_unix.h" #pragma GCC diagnostic ignored "-Wvolatile" #include "servers/audio_server.h" #pragma GCC diagnostic pop #include "servers/rendering_server.h" #include "servers/rendering/rendering_server_wrap_mt.h" #include "servers/rendering/rasterizer.h" #include "servers/rendering/rendering_server_raster.h" #include "main/main.h" namespace frt { static const char *default_audio_device = "default"; // TODO class AudioDriverSDL2 : public AudioDriver, public SampleProducer { private: Audio audio_; int mix_rate_; SpeakerMode speaker_mode_; public: AudioDriverSDL2() : audio_(this) { } public: // AudioDriverSW const char *get_name() const FRT_OVERRIDE { return "SDL2"; } Error init() FRT_OVERRIDE { mix_rate_ = GLOBAL_GET("audio/mix_rate"); speaker_mode_ = SPEAKER_MODE_STEREO; const int latency = GLOBAL_GET("audio/output_latency"); const int samples = closest_power_of_2(latency * mix_rate_ / 1000); return audio_.init(mix_rate_, samples) ? OK : ERR_CANT_OPEN; } int get_mix_rate() const FRT_OVERRIDE { return mix_rate_; } SpeakerMode get_speaker_mode() const FRT_OVERRIDE { return speaker_mode_; } Array get_device_list() FRT_OVERRIDE { Array list; list.push_back(default_audio_device); return list; } String get_device() FRT_OVERRIDE { return default_audio_device; } void set_device(String device) FRT_OVERRIDE { } void start() FRT_OVERRIDE { audio_.start(); } void lock() FRT_OVERRIDE { audio_.lock(); } void unlock() FRT_OVERRIDE { audio_.unlock(); } void finish() FRT_OVERRIDE { audio_.finish(); } public: // SampleProducer void produce_samples(int n_of_frames, int32_t *frames) FRT_OVERRIDE { audio_server_process(n_of_frames, frames); } }; class Godot3_OS : public OS_Unix, public EventHandler { private: enum { VIDEO_DRIVER_GLES2, }; MainLoop *main_loop_; VideoMode video_mode_; bool quit_; OS_FRT os_; int video_driver_; RenderingServer *rendering_server_; void init_video() { //if (video_driver_ == VIDEO_DRIVER_GLES2) { frt_resolve_symbols_gles2(get_proc_address); RasterizerGLES2::register_config(); RasterizerGLES2::make_current(); //} rendering_server_ = memnew(RenderingServerRaster); rendering_server_->init(); } void cleanup_video() { rendering_server_->finish(); memdelete(rendering_server_); } AudioDriverSDL2 audio_driver_; void init_audio(int id) { AudioDriverManager::initialize(id); } void cleanup_audio() { } InputDefault *input_; Point2 mouse_pos_; int mouse_state_; void init_input() { input_ = memnew(InputDefault); mouse_pos_ = Point2(-1, -1); mouse_state_ = 0; } void cleanup_input() { memdelete(input_); } void fill_modifier_state(Ref st) { const InputModifierState *os_st = os_.get_modifier_state(); st->set_shift(os_st->shift); st->set_alt(os_st->alt); st->set_control(os_st->control); st->set_metakey(os_st->meta); } public: Godot3_OS() : os_(this) { AudioDriverManager::add_driver(&audio_driver_); main_loop_ = 0; quit_ = false; } void run() { if (main_loop_) { main_loop_->init(); while (!quit_ && !Main::iteration()) os_.dispatch_events(); main_loop_->finish(); } } public: // OS int get_video_driver_count() const FRT_OVERRIDE { return 2; } #if FRT_GODOT_VERSION >= 30100 int get_current_video_driver() const FRT_OVERRIDE { return video_driver_; } #endif const char *get_video_driver_name(int driver) const FRT_OVERRIDE { return "GLES2"; } bool _check_internal_feature_support(const String &feature) FRT_OVERRIDE { return feature == "mobile" || feature == "etc"; } String get_config_path() const FRT_OVERRIDE { if (has_environment("XDG_CONFIG_HOME")) return get_environment("XDG_CONFIG_HOME"); if (has_environment("HOME")) return get_environment("HOME").plus_file(".config"); return "."; } String get_data_path() const FRT_OVERRIDE { if (has_environment("XDG_DATA_HOME")) return get_environment("XDG_DATA_HOME"); if (has_environment("HOME")) return get_environment("HOME").plus_file(".local/share"); return get_config_path(); } String get_cache_path() const FRT_OVERRIDE { if (has_environment("XDG_CACHE_HOME")) return get_environment("XDG_CACHE_HOME"); if (has_environment("HOME")) return get_environment("HOME").plus_file(".cache"); return get_config_path(); } Error initialize(const VideoMode &desired, int video_driver, int audio_driver) FRT_OVERRIDE { video_mode_ = desired; video_driver_ = video_driver; const GraphicsAPI api = video_driver == VIDEO_DRIVER_GLES2 ? API_OpenGL_ES2 : API_OpenGL_ES3; os_.init(api, video_mode_.width, video_mode_.height, video_mode_.resizable, video_mode_.borderless_window, video_mode_.always_on_top); _set_use_vsync(video_mode_.use_vsync); init_video(); init_audio(audio_driver); init_input(); #if FRT_GODOT_VERSION < 30300 _ensure_user_data_dir(); #endif return OK; } void set_main_loop(MainLoop *main_loop) FRT_OVERRIDE { main_loop_ = main_loop; input_->set_main_loop(main_loop); } void delete_main_loop() FRT_OVERRIDE { if (main_loop_) memdelete(main_loop_); main_loop_ = 0; } void finalize() FRT_OVERRIDE { delete_main_loop(); cleanup_input(); cleanup_audio(); cleanup_video(); os_.cleanup(); } Point2 get_mouse_position() const FRT_OVERRIDE { return mouse_pos_; } int get_mouse_button_state() const FRT_OVERRIDE { return mouse_state_; } void set_mouse_mode(OS::MouseMode mode) FRT_OVERRIDE { os_.set_mouse_mode(map_mouse_mode(mode)); } OS::MouseMode get_mouse_mode() const FRT_OVERRIDE { return map_mouse_os_mode(os_.get_mouse_mode()); } void set_window_title(const String &title) FRT_OVERRIDE { os_.set_title(title.utf8().get_data()); } void set_video_mode(const VideoMode &video_mode, int screen) FRT_OVERRIDE { } VideoMode get_video_mode(int screen = 0) const FRT_OVERRIDE { return video_mode_; } void get_fullscreen_mode_list(List *list, int screen) const FRT_OVERRIDE { } Size2 get_window_size() const FRT_OVERRIDE { return Size2(video_mode_.width, video_mode_.height); } void set_window_size(const Size2 size) FRT_OVERRIDE { ivec2 os_size = { (int)size.width, (int)size.height }; os_.set_size(os_size); video_mode_.width = os_size.x; video_mode_.height = os_size.y; } Point2 get_window_position() const FRT_OVERRIDE { ivec2 pos = os_.get_pos(); return Point2(pos.x, pos.y); } void set_window_position(const Point2 &pos) FRT_OVERRIDE { ivec2 os_pos = { (int)pos.width, (int)pos.height }; os_.set_pos(os_pos); } void set_window_fullscreen(bool enable) FRT_OVERRIDE { os_.set_fullscreen(enable); video_mode_.fullscreen = enable; } bool is_window_fullscreen() const FRT_OVERRIDE { return os_.is_fullscreen(); } void set_window_always_on_top(bool enable) FRT_OVERRIDE { os_.set_always_on_top(enable); video_mode_.always_on_top = enable; } bool is_window_always_on_top() const FRT_OVERRIDE { return os_.is_always_on_top(); } void set_window_resizable(bool enable) FRT_OVERRIDE { os_.set_resizable(enable); } bool is_window_resizable() const FRT_OVERRIDE { return os_.is_resizable(); } void set_window_maximized(bool enable) FRT_OVERRIDE { os_.set_maximized(enable); } bool is_window_maximized() const FRT_OVERRIDE { return os_.is_maximized(); } void set_window_minimized(bool enable) FRT_OVERRIDE { os_.set_minimized(enable); } bool is_window_minimized() const FRT_OVERRIDE { return os_.is_minimized(); } MainLoop *get_main_loop() const FRT_OVERRIDE { return main_loop_; } bool can_draw() const FRT_OVERRIDE { return true; } void set_cursor_shape(CursorShape shape) FRT_OVERRIDE { } void set_custom_mouse_cursor(const RES &cursor, CursorShape shape, const Vector2 &hotspot) FRT_OVERRIDE { } void make_rendering_thread() FRT_OVERRIDE { os_.make_current(); } void release_rendering_thread() FRT_OVERRIDE { os_.release_current(); } void swap_buffers() FRT_OVERRIDE { os_.swap_buffers(); } void _set_use_vsync(bool enable) FRT_OVERRIDE { os_.set_use_vsync(enable); } void set_icon(const Ref &icon) FRT_OVERRIDE { if (icon.is_null()) return; Ref i = icon->duplicate(); i->convert(Image::FORMAT_RGBA8); PoolVector::Read r = i->get_data().read(); os_.set_icon(i->get_width(), i->get_height(), r.ptr()); } public: // EventHandler void handle_resize_event(ivec2 size) FRT_OVERRIDE { video_mode_.width = size.x; video_mode_.height = size.y; } void handle_key_event(int sdl2_code, int unicode, bool pressed) FRT_OVERRIDE { int code = map_key_sdl2_code(sdl2_code); Ref key; key.instance(); fill_modifier_state(key); key->set_pressed(pressed); key->set_scancode(code); #if FRT_GODOT_VERSION >= 30400 key->set_physical_scancode(code); // TODO #endif key->set_unicode(unicode); key->set_echo(false); input_->parse_input_event(key); } void handle_mouse_motion_event(ivec2 pos, ivec2 dpos) FRT_OVERRIDE { mouse_pos_.x = pos.x; mouse_pos_.y = pos.y; Ref mouse_motion; mouse_motion.instance(); fill_modifier_state(mouse_motion); Point2i posi(pos.x, pos.y); mouse_motion->set_button_mask(mouse_state_); mouse_motion->set_position(posi); mouse_motion->set_global_position(posi); input_->set_mouse_position(posi); mouse_motion->set_speed(input_->get_last_mouse_speed()); Point2i reli(dpos.x, dpos.y); mouse_motion->set_relative(reli); input_->parse_input_event(mouse_motion); } void handle_mouse_button_event(int os_button, bool pressed, bool doubleclick) FRT_OVERRIDE { int button = map_mouse_os_button(os_button); int bit = (1 << (button - 1)); if (pressed) mouse_state_ |= bit; else mouse_state_ &= ~bit; Ref mouse_button; mouse_button.instance(); fill_modifier_state(mouse_button); Point2i posi(mouse_pos_.x, mouse_pos_.y); mouse_button->set_position(posi); mouse_button->set_global_position(posi); mouse_button->set_button_index(button); mouse_button->set_button_mask(mouse_state_); mouse_button->set_doubleclick(doubleclick); mouse_button->set_pressed(pressed); input_->parse_input_event(mouse_button); } void handle_js_status_event(int id, bool connected, const char *name, const char *guid) FRT_OVERRIDE { input_->joy_connection_changed(id, connected, name, guid); } void handle_js_button_event(int id, int button, bool pressed) FRT_OVERRIDE { input_->joy_button(id, button, pressed ? 1 : 0); } void handle_js_axis_event(int id, int axis, float value) FRT_OVERRIDE { #if FRT_GODOT_VERSION >= 30500 input_->set_joy_axis(id, axis, value); #else InputDefault::JoyAxis v = { -1, value }; input_->joy_axis(id, axis, v); #endif } void handle_js_hat_event(int id, int os_mask) FRT_OVERRIDE { int mask = map_hat_os_mask(os_mask); input_->joy_hat(id, mask); } void handle_quit_event() FRT_OVERRIDE { quit_ = true; } void handle_flush_events() FRT_OVERRIDE { input_->flush_buffered_events(); } }; } // namespace frt #include "frt_lib.h" extern "C" int frt_godot_main(int argc, char *argv[]) { frt::Godot3_OS os; Error err = Main::setup(argv[0], argc - 1, &argv[1]); if (err != OK) return 255; if (Main::start()) os.main_run(); Main::cleanup(); return os.get_exit_code(); }