mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-24 12:47:12 +01:00
394 lines
12 KiB
C++
394 lines
12 KiB
C++
|
// frt_godot2.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 "dl/gles2.gen.h"
|
||
|
|
||
|
#include "core/print_string.h"
|
||
|
#include "drivers/unix/os_unix.h"
|
||
|
#include "servers/visual_server.h"
|
||
|
#include "servers/visual/visual_server_wrap_mt.h"
|
||
|
#include "servers/visual/rasterizer.h"
|
||
|
#include "servers/physics_server.h"
|
||
|
#include "servers/audio/audio_server_sw.h"
|
||
|
#include "servers/audio/sample_manager_sw.h"
|
||
|
#include "servers/spatial_sound/spatial_sound_server_sw.h"
|
||
|
#include "servers/spatial_sound_2d/spatial_sound_2d_server_sw.h"
|
||
|
#include "servers/physics/physics_server_sw.h"
|
||
|
#include "servers/physics_2d/physics_2d_server_sw.h"
|
||
|
#include "servers/physics_2d/physics_2d_server_wrap_mt.h"
|
||
|
#include "servers/visual/visual_server_raster.h"
|
||
|
#include "drivers/gles2/rasterizer_gles2.h"
|
||
|
#include "main/main.h"
|
||
|
|
||
|
namespace frt {
|
||
|
|
||
|
class AudioDriverSDL2 : public AudioDriverSW, public SampleProducer {
|
||
|
private:
|
||
|
Audio audio_;
|
||
|
int mix_rate_;
|
||
|
OutputFormat output_format_;
|
||
|
public:
|
||
|
AudioDriverSDL2() : audio_(this) {
|
||
|
}
|
||
|
public: // AudioDriverSW
|
||
|
const char *get_name() const FRT_OVERRIDE {
|
||
|
return "SDL2";
|
||
|
}
|
||
|
Error init() FRT_OVERRIDE {
|
||
|
mix_rate_ = GLOBAL_DEF("audio/mix_rate", 44100);
|
||
|
output_format_ = OUTPUT_STEREO;
|
||
|
const int latency = GLOBAL_DEF("audio/output_latency", 25);
|
||
|
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_;
|
||
|
}
|
||
|
OutputFormat get_output_format() const FRT_OVERRIDE {
|
||
|
return output_format_;
|
||
|
}
|
||
|
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 Godot2_OS : public OS_Unix, public EventHandler {
|
||
|
private:
|
||
|
MainLoop *main_loop_;
|
||
|
VideoMode video_mode_;
|
||
|
bool quit_;
|
||
|
OS_FRT os_;
|
||
|
RasterizerGLES2 *rasterizer_;
|
||
|
VisualServer *visual_server_;
|
||
|
int event_id_;
|
||
|
void init_video() {
|
||
|
rasterizer_ = memnew(RasterizerGLES2);
|
||
|
visual_server_ = memnew(VisualServerRaster(rasterizer_));
|
||
|
visual_server_->init();
|
||
|
}
|
||
|
void cleanup_video() {
|
||
|
visual_server_->finish();
|
||
|
memdelete(visual_server_);
|
||
|
memdelete(rasterizer_);
|
||
|
}
|
||
|
AudioDriverSDL2 audio_driver_;
|
||
|
AudioServerSW *audio_server_;
|
||
|
SampleManagerMallocSW *sample_manager_;
|
||
|
SpatialSoundServerSW *spatial_sound_server_;
|
||
|
SpatialSound2DServerSW *spatial_sound_2d_server_;
|
||
|
void init_audio() {
|
||
|
const int driver_id = 0;
|
||
|
AudioDriverManagerSW::add_driver(&audio_driver_);
|
||
|
AudioDriverManagerSW::get_driver(driver_id)->set_singleton();
|
||
|
if (AudioDriverManagerSW::get_driver(driver_id)->init() != OK)
|
||
|
fatal("audio driver failed to initialize.");
|
||
|
sample_manager_ = memnew(SampleManagerMallocSW);
|
||
|
audio_server_ = memnew(AudioServerSW(sample_manager_));
|
||
|
audio_server_->init();
|
||
|
spatial_sound_server_ = memnew(SpatialSoundServerSW);
|
||
|
spatial_sound_server_->init();
|
||
|
spatial_sound_2d_server_ = memnew(SpatialSound2DServerSW);
|
||
|
spatial_sound_2d_server_->init();
|
||
|
}
|
||
|
void cleanup_audio() {
|
||
|
spatial_sound_server_->finish();
|
||
|
memdelete(spatial_sound_server_);
|
||
|
spatial_sound_2d_server_->finish();
|
||
|
memdelete(spatial_sound_2d_server_);
|
||
|
memdelete(sample_manager_);
|
||
|
audio_server_->finish();
|
||
|
memdelete(audio_server_);
|
||
|
}
|
||
|
PhysicsServer *physics_server_;
|
||
|
Physics2DServer *physics_2d_server_;
|
||
|
void init_physics() {
|
||
|
physics_server_ = memnew(PhysicsServerSW);
|
||
|
physics_server_->init();
|
||
|
physics_2d_server_ = memnew(Physics2DServerSW);
|
||
|
physics_2d_server_->init();
|
||
|
}
|
||
|
void cleanup_physics() {
|
||
|
physics_2d_server_->finish();
|
||
|
memdelete(physics_2d_server_);
|
||
|
physics_server_->finish();
|
||
|
memdelete(physics_server_);
|
||
|
}
|
||
|
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(::InputModifierState &st) {
|
||
|
const InputModifierState *os_st = os_.get_modifier_state();
|
||
|
st.shift = os_st->shift;
|
||
|
st.alt = os_st->alt;
|
||
|
st.control = os_st->control;
|
||
|
st.meta = os_st->meta;
|
||
|
}
|
||
|
public:
|
||
|
Godot2_OS() : os_(this) {
|
||
|
main_loop_ = 0;
|
||
|
quit_ = false;
|
||
|
event_id_ = 0;
|
||
|
}
|
||
|
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 1;
|
||
|
}
|
||
|
const char *get_video_driver_name(int driver) const FRT_OVERRIDE {
|
||
|
return "GLES2";
|
||
|
}
|
||
|
VideoMode get_default_video_mode() const FRT_OVERRIDE {
|
||
|
return OS::VideoMode(960, 540, false);
|
||
|
}
|
||
|
void initialize(const VideoMode &desired, int video_driver, int audio_driver) FRT_OVERRIDE {
|
||
|
video_mode_ = desired;
|
||
|
os_.init(API_OpenGL_ES2, video_mode_.width, video_mode_.height, video_mode_.resizable, video_mode_.borderless_window, video_mode_.always_on_top);
|
||
|
frt_resolve_symbols_gles2(get_proc_address);
|
||
|
init_video();
|
||
|
init_audio();
|
||
|
init_physics();
|
||
|
init_input();
|
||
|
_ensure_data_dir();
|
||
|
}
|
||
|
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_physics();
|
||
|
cleanup_audio();
|
||
|
cleanup_video();
|
||
|
os_.cleanup();
|
||
|
}
|
||
|
Point2 get_mouse_pos() 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<VideoMode> *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);
|
||
|
}
|
||
|
bool is_vsync_enabled() const FRT_OVERRIDE {
|
||
|
return os_.is_vsync_enabled();
|
||
|
}
|
||
|
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);
|
||
|
InputEvent event;
|
||
|
event.ID = ++event_id_;
|
||
|
event.type = InputEvent::KEY;
|
||
|
event.device = 0;
|
||
|
fill_modifier_state(event.key.mod);
|
||
|
event.key.pressed = pressed;
|
||
|
event.key.scancode = code;
|
||
|
event.key.unicode = unicode;
|
||
|
event.key.echo = 0;
|
||
|
input_->parse_input_event(event);
|
||
|
}
|
||
|
void handle_mouse_motion_event(ivec2 pos, ivec2 dpos) FRT_OVERRIDE {
|
||
|
mouse_pos_.x = pos.x;
|
||
|
mouse_pos_.y = pos.y;
|
||
|
InputEvent event;
|
||
|
event.ID = ++event_id_;
|
||
|
event.type = InputEvent::MOUSE_MOTION;
|
||
|
event.device = 0;
|
||
|
fill_modifier_state(event.mouse_motion.mod);
|
||
|
event.mouse_motion.button_mask = mouse_state_;
|
||
|
event.mouse_motion.x = mouse_pos_.x;
|
||
|
event.mouse_motion.y = mouse_pos_.y;
|
||
|
input_->set_mouse_pos(mouse_pos_);
|
||
|
event.mouse_motion.global_x = mouse_pos_.x;
|
||
|
event.mouse_motion.global_y = mouse_pos_.y;
|
||
|
event.mouse_motion.speed_x = input_->get_mouse_speed().x;
|
||
|
event.mouse_motion.speed_y = input_->get_mouse_speed().y;
|
||
|
event.mouse_motion.relative_x = dpos.x;
|
||
|
event.mouse_motion.relative_y = dpos.y;
|
||
|
input_->parse_input_event(event);
|
||
|
}
|
||
|
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;
|
||
|
InputEvent event;
|
||
|
event.ID = ++event_id_;
|
||
|
event.type = InputEvent::MOUSE_BUTTON;
|
||
|
event.device = 0;
|
||
|
fill_modifier_state(event.mouse_button.mod);
|
||
|
event.mouse_button.button_mask = mouse_state_;
|
||
|
event.mouse_button.x = mouse_pos_.x;
|
||
|
event.mouse_button.y = mouse_pos_.y;
|
||
|
event.mouse_button.global_x = mouse_pos_.x;
|
||
|
event.mouse_button.global_y = mouse_pos_.y;
|
||
|
event.mouse_button.button_index = button;
|
||
|
event.mouse_button.doubleclick = doubleclick;
|
||
|
event.mouse_button.pressed = pressed;
|
||
|
input_->parse_input_event(event);
|
||
|
}
|
||
|
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 {
|
||
|
event_id_ = input_->joy_button(event_id_, id, button, pressed ? 1 : 0);
|
||
|
}
|
||
|
void handle_js_axis_event(int id, int axis, float value) FRT_OVERRIDE {
|
||
|
InputDefault::JoyAxis v = { -1, value };
|
||
|
event_id_ = input_->joy_axis(event_id_, id, axis, v);
|
||
|
}
|
||
|
void handle_js_hat_event(int id, int os_mask) FRT_OVERRIDE {
|
||
|
int mask = map_hat_os_mask(os_mask);
|
||
|
event_id_ = input_->joy_hat(event_id_, id, mask);
|
||
|
}
|
||
|
void handle_quit_event() FRT_OVERRIDE {
|
||
|
quit_ = true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
} // namespace frt
|
||
|
|
||
|
#include "frt_lib.h"
|
||
|
|
||
|
extern "C" int frt_godot_main(int argc, char *argv[]) {
|
||
|
frt::Godot2_OS os;
|
||
|
Error err = Main::setup(argv[0], argc - 1, &argv[1]);
|
||
|
if (err != OK)
|
||
|
return 255;
|
||
|
if (Main::start())
|
||
|
os.run();
|
||
|
Main::cleanup();
|
||
|
return os.get_exit_code();
|
||
|
}
|