mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-11-21 16:37:20 +01:00
472 lines
14 KiB
C++
472 lines
14 KiB
C++
/*************************************************************************/
|
|
/* frt_godot3.cc */
|
|
/*************************************************************************/
|
|
/* This file is part of: */
|
|
/* PANDEMONIUM ENGINE */
|
|
/* https://github.com/Relintai/pandemonium_engine */
|
|
/*************************************************************************/
|
|
/* Copyright (c) 2022-present Péter Magyar. */
|
|
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
|
|
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
|
|
/* */
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
/* a copy of this software and associated documentation files (the */
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
/* the following conditions: */
|
|
/* */
|
|
/* The above copyright notice and this permission notice shall be */
|
|
/* included in all copies or substantial portions of the Software. */
|
|
/* */
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
/*************************************************************************/
|
|
|
|
// 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"
|
|
|
|
#ifndef GLES3_DISABLED
|
|
#include "drivers/gles3/rasterizer_gles3.h"
|
|
|
|
#define FRT_DL_SKIP
|
|
#include "drivers/gles2/rasterizer_gles2.h"
|
|
#else
|
|
#include "drivers/gles2/rasterizer_gles2.h"
|
|
#endif
|
|
|
|
#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 "main/main.h"
|
|
#include "servers/rendering/rasterizer.h"
|
|
#include "servers/rendering/rendering_server_raster.h"
|
|
#include "servers/rendering/rendering_server_wrap_mt.h"
|
|
#include "servers/rendering_server.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,
|
|
#ifndef GLES3_DISABLED
|
|
VIDEO_DRIVER_GLES3
|
|
#endif
|
|
};
|
|
MainLoop *main_loop_;
|
|
VideoMode video_mode_;
|
|
bool quit_;
|
|
OS_FRT os_;
|
|
int video_driver_;
|
|
RenderingServer *rendering_server_;
|
|
void init_video() {
|
|
#ifndef GLES3_DISABLED
|
|
if (video_driver_ == VIDEO_DRIVER_GLES2) {
|
|
frt_resolve_symbols_gles2(get_proc_address);
|
|
RasterizerGLES2::register_config();
|
|
RasterizerGLES2::make_current();
|
|
} else {
|
|
frt_resolve_symbols_gles3(get_proc_address);
|
|
RasterizerGLES3::register_config();
|
|
RasterizerGLES3::make_current();
|
|
}
|
|
#else
|
|
frt_resolve_symbols_gles2(get_proc_address);
|
|
RasterizerGLES2::register_config();
|
|
RasterizerGLES2::make_current();
|
|
#endif
|
|
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<InputEventWithModifiers> 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 {
|
|
#ifndef GLES3_DISABLED
|
|
return driver == VIDEO_DRIVER_GLES3 ? "GLES3" : "GLES2";
|
|
#else
|
|
return "GLES2";
|
|
#endif
|
|
}
|
|
bool _check_internal_feature_support(const String &feature) FRT_OVERRIDE {
|
|
#ifndef GLES3_DISABLED
|
|
if (video_driver_ == VIDEO_DRIVER_GLES3 && feature == "etc2")
|
|
return true;
|
|
#endif
|
|
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<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);
|
|
}
|
|
void set_icon(const Ref<Image> &icon) FRT_OVERRIDE {
|
|
if (icon.is_null())
|
|
return;
|
|
Ref<Image> i = icon->duplicate();
|
|
i->convert(Image::FORMAT_RGBA8);
|
|
PoolVector<uint8_t>::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<InputEventKey> 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<InputEventMouseMotion> 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<InputEventMouseButton> 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.run();
|
|
Main::cleanup();
|
|
return os.get_exit_code();
|
|
}
|