diff --git a/src/audio.cpp b/src/audio.cpp index 1448736ce56..fd6e720ec90 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -70,6 +70,21 @@ AudioInterface::AudioInterface(const Game_ConfigAudio& cfg) : cfg(cfg) { Game_ConfigAudio AudioInterface::GetConfig() const { auto acfg = cfg; acfg.Hide(); + +#if !defined(HAVE_FLUIDSYNTH) && !defined(HAVE_FLUIDLITE) + acfg.fluidsynth_midi.SetOptionVisible(false); + acfg.soundfont.SetOptionVisible(false); +#endif +#ifndef HAVE_LIBWILDMIDI + acfg.wildmidi_midi.SetOptionVisible(false); +#endif +#ifndef HAVE_NATIVE_MIDI + acfg.native_midi.SetOptionVisible(false); +#endif +#ifndef WANT_FMMIDI + acfg.fmmidi_midi.SetOptionVisible(false); +#endif + vGetConfig(acfg); return acfg; } diff --git a/src/baseui.h b/src/baseui.h index 8e7f8577c54..172af7d9517 100644 --- a/src/baseui.h +++ b/src/baseui.h @@ -199,9 +199,15 @@ class BaseUi { /** Toggle whether we should show fps */ void ToggleShowFps(); - /** Toggle wheter we should show fps on the titlebar */ + /** Toggle whether we should show fps on the titlebar */ void ToggleShowFpsOnTitle(); + /** + * Set whether the program pauses the execution when the program focus is lost. + * @param value + */ + void SetPauseWhenFocusLost(bool value); + /** * @return the minimum amount of time each physical frame should take. * If the UI manages time (i.e.) vsync, will return a 0 duration. @@ -228,6 +234,15 @@ class BaseUi { */ void SetGameResolution(GameResolution resolution); + /** + * Opens the specified URL through the operating system. + * Opens a file browser when file:// is provided. + * + * @param url URL to open + * @return true when successful + */ + virtual bool OpenURL(StringView path) { (void)path; return false; } + /** Toggles "stretch to screen width" on or off */ virtual void ToggleStretch() {}; @@ -385,6 +400,10 @@ inline void BaseUi::ToggleShowFpsOnTitle() { vcfg.fps_render_window.Toggle(); } +inline void BaseUi::SetPauseWhenFocusLost(bool value) { + vcfg.pause_when_focus_lost.Set(value); +} + inline Game_Clock::duration BaseUi::GetFrameLimit() const { return IsFrameRateSynchronized() ? Game_Clock::duration(0) : frame_limit; } diff --git a/src/bitmap.cpp b/src/bitmap.cpp index 9cc54864e04..8ee75f18afb 100644 --- a/src/bitmap.cpp +++ b/src/bitmap.cpp @@ -334,20 +334,20 @@ void Bitmap::HueChangeBlit(int x, int y, Bitmap const& src, Rect const& src_rect } Point Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align) { - FontRef font = Font::Default(); - switch (align) { case Text::AlignLeft: return TextDraw(rect.x, rect.y, color, text); break; case Text::AlignCenter: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + (rect.width - text_rect.width) / 2; return TextDraw(dx, rect.y, color, text); break; } case Text::AlignRight: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + rect.width - text_rect.width; return TextDraw(dx, rect.y, color, text); break; @@ -355,30 +355,30 @@ Point Bitmap::TextDraw(Rect const& rect, int color, StringView text, Text::Align default: assert(false); } - return Point(); + return {}; } Point Bitmap::TextDraw(int x, int y, int color, StringView text, Text::Alignment align) { - auto font = Font::Default(); + auto f = font ? font : Font::Default(); auto system = Cache::SystemOrBlack(); - return Text::Draw(*this, x, y, *font, *system, color, text, align); + return Text::Draw(*this, x, y, *f, *system, color, text, align); } Point Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Alignment align) { - FontRef font = Font::Default(); - switch (align) { case Text::AlignLeft: return TextDraw(rect.x, rect.y, color, text); break; case Text::AlignCenter: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + (rect.width - text_rect.width) / 2; return TextDraw(dx, rect.y, color, text); break; } case Text::AlignRight: { - Rect text_rect = Text::GetSize(*font, text); + auto f = font ? font : Font::Default(); + Rect text_rect = Text::GetSize(*f, text); int dx = rect.x + rect.width - text_rect.width; return TextDraw(dx, rect.y, color, text); break; @@ -386,12 +386,12 @@ Point Bitmap::TextDraw(Rect const& rect, Color color, StringView text, Text::Ali default: assert(false); } - return Point(); + return {}; } Point Bitmap::TextDraw(int x, int y, Color color, StringView text) { - auto font = Font::Default(); - return Text::Draw(*this, x, y, *font, color, text); + auto f = font ? font : Font::Default(); + return Text::Draw(*this, x, y, *f, color, text); } Rect Bitmap::TransformRectangle(const Transform& xform, const Rect& rect) { diff --git a/src/bitmap.h b/src/bitmap.h index ee7a77fddc5..b7512162c54 100644 --- a/src/bitmap.h +++ b/src/bitmap.h @@ -234,7 +234,7 @@ class Bitmap { Color GetColorAt(int x, int y) const; /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param x x coordinate where text rendering starts. * @param y y coordinate where text rendering starts. @@ -246,7 +246,7 @@ class Bitmap { Point TextDraw(int x, int y, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param rect bounding rectangle. * @param color system color index. @@ -257,7 +257,7 @@ class Bitmap { Point TextDraw(Rect const& rect, int color, StringView text, Text::Alignment align = Text::AlignLeft); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param x x coordinate where text rendering starts. * @param y y coordinate where text rendering starts. @@ -268,7 +268,7 @@ class Bitmap { Point TextDraw(int x, int y, Color color, StringView text); /** - * Draws text to bitmap using the Font::Default() font. + * Draws text to bitmap using the configured Font or the Font::Default() font. * * @param rect bounding rectangle. * @param color text color. @@ -587,6 +587,9 @@ class Bitmap { int bpp() const; int pitch() const; + FontRef GetFont() const; + void SetFont(FontRef font); + ImageOpacity ComputeImageOpacity() const; ImageOpacity ComputeImageOpacity(Rect rect) const; @@ -596,6 +599,7 @@ class Bitmap { ImageOpacity image_opacity = ImageOpacity::Alpha_8Bit; TileOpacity tile_opacity; Color bg_color, sh_color; + FontRef font; std::string filename; @@ -672,4 +676,12 @@ inline StringView Bitmap::GetFilename() const { return filename; } +inline FontRef Bitmap::GetFont() const { + return font; +} + +inline void Bitmap::SetFont(FontRef font) { + this->font = font; +} + #endif diff --git a/src/config_param.h b/src/config_param.h index 5cc7158ebcb..cdbe2ecd502 100644 --- a/src/config_param.h +++ b/src/config_param.h @@ -19,6 +19,7 @@ #define EP_CONFIG_PARAM_H #include "string_view.h" +#include "filefinder.h" #include #include #include @@ -431,4 +432,14 @@ class EnumConfigParam : public ConfigParamBase { } }; +class PathConfigParam : public StringConfigParam { +public: + PathConfigParam(StringView name, StringView description, StringView config_section, StringView config_key, std::string value) : + StringConfigParam(name, description, config_section, config_key, value) {} + + std::string ValueToString() const override { + return std::get<1>(FileFinder::GetPathAndFilename(Get())); + } +}; + #endif diff --git a/src/filefinder.cpp b/src/filefinder.cpp index e87679d8c90..465eaf07d2d 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -57,18 +57,9 @@ namespace { auto MOVIE_TYPES = { ".avi", ".mpg" }; #endif - std::string fonts_path; std::shared_ptr root_fs; FilesystemView game_fs; FilesystemView save_fs; - - constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); - constexpr const auto MUSIC_TYPES = Utils::MakeSvArray( - ".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma"); - constexpr const auto SOUND_TYPES = Utils::MakeSvArray( - ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); - constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); - constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ""); // "" = Complete Filename (incl. extension) provided by the user } FilesystemView FileFinder::Game() { diff --git a/src/filefinder.h b/src/filefinder.h index 125a305bb6c..51bd5258b25 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -37,6 +37,14 @@ * insensitive files paths. */ namespace FileFinder { + constexpr const auto IMG_TYPES = Utils::MakeSvArray(".bmp", ".png", ".xyz"); + constexpr const auto MUSIC_TYPES = Utils::MakeSvArray( + ".opus", ".oga", ".ogg", ".wav", ".mid", ".midi", ".mp3", ".wma"); + constexpr const auto SOUND_TYPES = Utils::MakeSvArray( + ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); + constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); + constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ""); // "" = Complete Filename (incl. extension) provided by the user + /** * Quits FileFinder. */ diff --git a/src/font.cpp b/src/font.cpp index 78668354243..1ce989dcad1 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -567,7 +567,7 @@ FontRef Font::CreateFtFont(Filesystem_Stream::InputStream is, int size, bool bol FreeFontMemory(); - std::string key = ToString(is.GetName()) + ":" + (bold ? "T" : "F") + (italic ? "T" : "F"); + std::string key = ToString(is.GetName()) + ":" + std::to_string(size) + ":" + (bold ? "T" : "F") + (italic ? "T" : "F"); auto it = ft_cache.find(key); @@ -594,10 +594,24 @@ FontRef Font::CreateFtFont(Filesystem_Stream::InputStream is, int size, bool bol void Font::ResetDefault() { SetDefault(nullptr, true); SetDefault(nullptr, false); + +#ifdef HAVE_FREETYPE + const auto& cfg = Player::player_config; + if (!cfg.font1.Get().empty()) { + auto is = FileFinder::Root().OpenInputStream(cfg.font1.Get()); + SetDefault(CreateFtFont(std::move(is), cfg.font1_size.Get(), false, false), false); + } + + if (!cfg.font2.Get().empty()) { + auto is = FileFinder::Root().OpenInputStream(cfg.font2.Get()); + SetDefault(CreateFtFont(std::move(is), cfg.font2_size.Get(), false, false), true); + } +#endif } void Font::Dispose() { - ResetDefault(); + SetDefault(nullptr, true); + SetDefault(nullptr, false); #ifdef HAVE_FREETYPE if (library) { diff --git a/src/game_config.cpp b/src/game_config.cpp index e1084baa91a..8e339ba45a1 100644 --- a/src/game_config.cpp +++ b/src/game_config.cpp @@ -35,11 +35,18 @@ namespace { std::string config_path; + std::string soundfont_path; + std::string font_path; StringView config_name = "config.ini"; } void Game_ConfigPlayer::Hide() { - // Game specific settings unsupported +#ifndef HAVE_FREETYPE + font1.SetOptionVisible(false); + font1_size.SetOptionVisible(false); + font2.SetOptionVisible(false); + font2_size.SetOptionVisible(false); +#endif } void Game_ConfigVideo::Hide() { @@ -58,6 +65,7 @@ void Game_ConfigVideo::Hide() { scaling_mode.SetOptionVisible(false); stretch.SetOptionVisible(false); touch_ui.SetOptionVisible(false); + pause_when_focus_lost.SetOptionVisible(false); game_resolution.SetOptionVisible(false); } @@ -199,6 +207,35 @@ Filesystem_Stream::InputStream Game_Config::GetGlobalConfigFileInput() { return Filesystem_Stream::InputStream(); } +FilesystemView Game_Config::GetSoundfontFilesystem() { + std::string path = soundfont_path; + if (path.empty()) { + path = FileFinder::MakePath(GetGlobalConfigFilesystem().GetFullPath(), "Soundfont"); + } + + if (!FileFinder::Root().MakeDirectory(path, true)) { + Output::Warning("Could not create soundfont path {}", path); + return {}; + } + + return FileFinder::Root().Create(path); +} + + +FilesystemView Game_Config::GetFontFilesystem() { + std::string path = font_path; + if (path.empty()) { + path = FileFinder::MakePath(GetGlobalConfigFilesystem().GetFullPath(), "Font"); + } + + if (!FileFinder::Root().MakeDirectory(path, true)) { + Output::Warning("Could not create fount path {}", path); + return {}; + } + + return FileFinder::Root().Create(path); +} + Filesystem_Stream::OutputStream Game_Config::GetGlobalConfigFileOutput() { auto fs = GetGlobalConfigFilesystem(); @@ -341,6 +378,18 @@ void Game_Config::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 1, "--soundfont-path")) { + if (arg.NumValues() > 0) { + soundfont_path = FileFinder::MakeCanonical(arg.Value(0), 0); + } + continue; + } + if (cp.ParseNext(arg, 1, "--font-path")) { + if (arg.NumValues() > 0) { + font_path = FileFinder::MakeCanonical(arg.Value(0), 0); + } + continue; + } cp.SkipNext(); } @@ -364,6 +413,7 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { video.scaling_mode.FromIni(ini); video.stretch.FromIni(ini); video.touch_ui.FromIni(ini); + video.pause_when_focus_lost.FromIni(ini); video.game_resolution.FromIni(ini); if (ini.HasValue("Video", "WindowX") && ini.HasValue("Video", "WindowY") && ini.HasValue("Video", "WindowWidth") && ini.HasValue("Video", "WindowHeight")) { @@ -436,6 +486,10 @@ void Game_Config::LoadFromStream(Filesystem_Stream::InputStream& is) { player.settings_in_title.FromIni(ini); player.settings_in_menu.FromIni(ini); player.show_startup_logos.FromIni(ini); + player.font1.FromIni(ini); + player.font1_size.FromIni(ini); + player.font2.FromIni(ini); + player.font2_size.FromIni(ini); } void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { @@ -451,6 +505,7 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { video.scaling_mode.ToIni(os); video.stretch.ToIni(os); video.touch_ui.ToIni(os); + video.pause_when_focus_lost.ToIni(os); video.game_resolution.ToIni(os); // only preserve when toggling between window and fullscreen is supported @@ -518,6 +573,10 @@ void Game_Config::WriteToStream(Filesystem_Stream::OutputStream& os) const { player.settings_in_title.ToIni(os); player.settings_in_menu.ToIni(os); player.show_startup_logos.ToIni(os); + player.font1.ToIni(os); + player.font1_size.ToIni(os); + player.font2.ToIni(os); + player.font2_size.ToIni(os); os << "\n"; } diff --git a/src/game_config.h b/src/game_config.h index 0462a1a452d..c221514485a 100644 --- a/src/game_config.h +++ b/src/game_config.h @@ -68,6 +68,10 @@ struct Game_ConfigPlayer { Utils::MakeSvArray("None", "Custom", "All"), Utils::MakeSvArray("none", "custom", "all"), Utils::MakeSvArray("Do not show any additional logos", "Show custom logos bundled with the game", "Show all logos, including the original from RPG Maker")}; + PathConfigParam font1 { "Font 1", "The game chooses whether it wants font 1 or 2", "Player", "Font1", "" }; + RangeConfigParam font1_size { "Font 1 Size", "", "Player", "Font1Size", 12, 6, 16}; + PathConfigParam font2 { "Font 2", "The game chooses whether it wants font 1 or 2", "Player", "Font2", "" }; + RangeConfigParam font2_size { "Font 2 Size", "", "Player", "Font2Size", 12, 6, 16}; void Hide(); }; @@ -85,6 +89,7 @@ struct Game_ConfigVideo { Utils::MakeSvArray("nearest", "integer", "bilinear"), Utils::MakeSvArray("Scale to screen size (Causes scaling artifacts)", "Scale to multiple of the game resolution", "Like Nearest, but output is blurred to avoid artifacts")}; BoolConfigParam stretch{ "Stretch", "Stretch to the width of the window/screen", "Video", "Stretch", false }; + BoolConfigParam pause_when_focus_lost{ "Pause when focus lost", "Pause the execution when the program is in the background", "Video", "PauseWhenFocusLost", true }; BoolConfigParam touch_ui{ "Touch Ui", "Display the touch ui", "Video", "TouchUi", true }; EnumConfigParam game_resolution{ "Resolution", "Game resolution. Changes require a restart.", "Video", "GameResolution", GameResolution::Original, Utils::MakeSvArray("Original (Recommended)", "Widescreen (Experimental)", "Ultrawide (Experimental)"), @@ -107,7 +112,7 @@ struct Game_ConfigAudio { BoolConfigParam wildmidi_midi { "WildMidi (GUS)", "Play MIDI using GUS patches", "Audio", "WildMidi", true }; BoolConfigParam native_midi { "Native MIDI", "Play MIDI through the operating system ", "Audio", "NativeMidi", true }; BoolConfigParam fmmidi_midi { "FmMidi", "Play MIDI using the built-in MIDI synthesizer", "Audio", "FmMidi", true }; - StringConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; + PathConfigParam soundfont { "Soundfont", "Soundfont to use for Fluidsynth", "Audio", "Soundfont", "" }; void Hide(); }; @@ -150,6 +155,19 @@ struct Game_Config { */ static FilesystemView GetGlobalConfigFilesystem(); + /** + * Returns the filesystem view to the soundfont directory + * By default this is config/Soundfont + */ + static FilesystemView GetSoundfontFilesystem(); + + /** + * Returns the filesystem view to the font directory + * By default this is config/Font + */ + static FilesystemView GetFontFilesystem(); + + /** * Returns a handle to the global config file for reading. * The file is created if it does not exist. diff --git a/src/options.h b/src/options.h index 5f673d83aad..fc05038da06 100644 --- a/src/options.h +++ b/src/options.h @@ -52,18 +52,6 @@ /** Targeted screen bits per pixel. */ #define SCREEN_TARGET_BPP 32 -/** - * Pause the game process when the player window - * looses its focus. - */ -#define PAUSE_GAME_WHEN_FOCUS_LOST 1 - -/** - * Pause the audio process when the player window - * looses its focus. - */ -#define PAUSE_AUDIO_WHEN_FOCUS_LOST 1 - /** INI configuration filename. */ #define INI_NAME "RPG_RT.ini" #define EASYRPG_INI_NAME "EasyRPG.ini" diff --git a/src/platform/sdl/sdl2_ui.cpp b/src/platform/sdl/sdl2_ui.cpp index 7b7900cb8c6..3f57d73c211 100644 --- a/src/platform/sdl/sdl2_ui.cpp +++ b/src/platform/sdl/sdl2_ui.cpp @@ -705,16 +705,17 @@ void Sdl2Ui::ProcessEvent(SDL_Event &evnt) { void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { int state = evnt.window.event; -#if PAUSE_GAME_WHEN_FOCUS_LOST + if (state == SDL_WINDOWEVENT_FOCUS_LOST) { + if (!vcfg.pause_when_focus_lost.Get()) { + return; + } Player::Pause(); bool last = ShowCursor(true); -#ifndef EMSCRIPTEN // Filter SDL events until focus is regained - SDL_Event wait_event; while (SDL_WaitEvent(&wait_event)) { @@ -722,7 +723,6 @@ void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { break; } } -#endif ShowCursor(last); @@ -731,7 +731,7 @@ void Sdl2Ui::ProcessWindowEvent(SDL_Event &evnt) { return; } -#endif + #if defined(USE_MOUSE_OR_TOUCH) && defined(SUPPORT_MOUSE_OR_TOUCH) if (state == SDL_WINDOWEVENT_ENTER) { mouse_focus = true; @@ -1206,6 +1206,7 @@ void Sdl2Ui::vGetConfig(Game_ConfigVideo& cfg) const { cfg.scaling_mode.SetOptionVisible(true); cfg.stretch.SetOptionVisible(true); cfg.game_resolution.SetOptionVisible(true); + cfg.pause_when_focus_lost.SetOptionVisible(true); cfg.vsync.Set(current_display_mode.vsync); cfg.window_zoom.Set(current_display_mode.zoom); @@ -1219,6 +1220,8 @@ void Sdl2Ui::vGetConfig(Game_ConfigVideo& cfg) const { cfg.window_zoom.SetOptionVisible(false); // Toggling this freezes the web player cfg.vsync.SetOptionVisible(false); + cfg.pause_when_focus_lost.Lock(false); + cfg.pause_when_focus_lost.SetOptionVisible(false); #endif } @@ -1232,3 +1235,21 @@ Rect Sdl2Ui::GetWindowMetrics() const { return window_mode_metrics; } } + +bool Sdl2Ui::OpenURL(StringView url) { +#if SDL_VERSION_ATLEAST(2, 0, 14) + if (IsFullscreen()) { + ToggleFullscreen(); + } + + if (SDL_OpenURL(ToString(url).c_str()) < 0) { + Output::Warning("Open URL {} failed: {}", url, SDL_GetError()); + return false; + } + + return true; +#else + Output::Warning("Cannot Open URL: SDL2 version too old (must be 2.0.14)"); + return false; +#endif +} diff --git a/src/platform/sdl/sdl2_ui.h b/src/platform/sdl/sdl2_ui.h index c9543f2aa96..cfa28769c8d 100644 --- a/src/platform/sdl/sdl2_ui.h +++ b/src/platform/sdl/sdl2_ui.h @@ -70,6 +70,7 @@ class Sdl2Ui final : public BaseUi { void ToggleStretch() override; void ToggleVsync() override; void vGetConfig(Game_ConfigVideo& cfg) const override; + bool OpenURL(StringView url) override; Rect GetWindowMetrics() const override; #ifdef SUPPORT_AUDIO diff --git a/src/platform/sdl/sdl_ui.cpp b/src/platform/sdl/sdl_ui.cpp index 7d7f96ef65b..f2cff79c471 100644 --- a/src/platform/sdl/sdl_ui.cpp +++ b/src/platform/sdl/sdl_ui.cpp @@ -488,8 +488,11 @@ void SdlUi::ProcessActiveEvent(SDL_Event &evnt) { int state; state = evnt.active.state; -#if PAUSE_GAME_WHEN_FOCUS_LOST if (state == SDL_APPINPUTFOCUS && !evnt.active.gain) { + if (!vcfg.pause_when_focus_lost.Get()) { + return; + } + Player::Pause(); bool last = ShowCursor(true); @@ -510,7 +513,6 @@ void SdlUi::ProcessActiveEvent(SDL_Event &evnt) { return; } -#endif } void SdlUi::ProcessKeyDownEvent(SDL_Event &evnt) { @@ -757,6 +759,8 @@ void SdlUi::vGetConfig(Game_ConfigVideo& cfg) const { #endif cfg.fullscreen.SetOptionVisible(toggle_fs_available); + cfg.pause_when_focus_lost.SetOptionVisible(toggle_fs_available); + #ifdef SUPPORT_ZOOM cfg.window_zoom.SetOptionVisible(true); cfg.window_zoom.Set(current_display_mode.zoom); diff --git a/src/scene_logo.cpp b/src/scene_logo.cpp index 145181c3ac4..b805f312bc0 100644 --- a/src/scene_logo.cpp +++ b/src/scene_logo.cpp @@ -63,6 +63,8 @@ void Scene_Logo::Start() { void Scene_Logo::vUpdate() { if (current_logo_index == 0 && frame_counter == 0) { + Font::ResetDefault(); + if (!DetectGame()) { // async delay for emscripten return; diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index 4b7bc7cc31d..ea8aa9415c9 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -91,7 +91,7 @@ void Scene_Settings::CreateOptionsWindow() { options_window = std::make_unique(Player::menu_offset_x + 32, 32, MENU_WIDTH - 64, Player::screen_height - 32 * 2); options_window->SetHelpWindow(help_window.get()); - help_window2 = std::make_unique(Player::menu_offset_x, Player::screen_height - 32, MENU_WIDTH, 32); + help_window2 = std::make_unique(Player::menu_offset_x, options_window->GetBottomY(), MENU_WIDTH, 32); options_window->help_window2 = help_window2.get(); input_window = std::make_unique(Player::menu_offset_x, 32, MENU_WIDTH, Player::screen_height - 32 * 3); @@ -249,6 +249,7 @@ void Scene_Settings::vUpdate() { case Window_Settings::eVideo: case Window_Settings::eAudio: case Window_Settings::eAudioMidi: + case Window_Settings::eAudioSoundfont: case Window_Settings::eLicense: case Window_Settings::eEngine: case Window_Settings::eInputButtonCategory: @@ -257,6 +258,12 @@ void Scene_Settings::vUpdate() { case Window_Settings::eInputListButtonsDeveloper: UpdateOptions(); break; + case Window_Settings::eEngineFont1: + UpdateFont(false); + break; + case Window_Settings::eEngineFont2: + UpdateFont(true); + break; case Window_Settings::eInputButtonOption: UpdateButtonOption(); break; @@ -441,6 +448,31 @@ void Scene_Settings::UpdateOptions() { } } +void Scene_Settings::UpdateFont(bool mincho) { + auto fs = Game_Config::GetFontFilesystem(); + + help_window2->SetText("The quick brown fox jumps over the lazy dog 1234567890."); + + int index = options_window->GetIndex(); + if (index == 0) { + help_window2->SetFont(Font::DefaultBitmapFont(mincho)); + help_window2->SetVisible(true); + } else if (index >= options_window->GetRowMax() - 2) { + // Size or browse + } else { + auto is = fs.OpenInputStream(options_window->GetCurrentOption().text); + if (is) { + auto font = Font::CreateFtFont(std::move(is), options_window->font_size.Get(), false, false); + if (font) { + help_window2->SetFont(font); + help_window2->SetVisible(true); + } + } + } + + UpdateOptions(); +} + void Scene_Settings::UpdateButtonOption() { if (Input::IsTriggered(Input::DECISION)) { switch (input_mode_window->GetIndex()) { diff --git a/src/scene_settings.h b/src/scene_settings.h index 0bd902ec0bc..f62704ea190 100644 --- a/src/scene_settings.h +++ b/src/scene_settings.h @@ -63,6 +63,7 @@ class Scene_Settings : public Scene { void UpdateMain(); void UpdateOptions(); + void UpdateFont(bool mincho); void UpdateButtonOption(); void UpdateButtonAdd(); void UpdateButtonRemove(); diff --git a/src/window.h b/src/window.h index 0968b493173..abefad8f58a 100644 --- a/src/window.h +++ b/src/window.h @@ -20,6 +20,7 @@ // Headers #include "system.h" +#include "bitmap.h" #include "drawable.h" #include "rect.h" @@ -61,6 +62,8 @@ class Window : public Drawable { void SetWidth(int nwidth); int GetHeight() const; void SetHeight(int nheight); + int GetRightX() const; + int GetBottomY() const; int GetOx() const; void SetOx(int nox); int GetOy() const; @@ -80,6 +83,9 @@ class Window : public Drawable { void SetOpenAnimation(int frames); void SetCloseAnimation(int frames); + FontRef GetFont() const; + void SetFont(FontRef font); + bool IsOpening() const; bool IsClosing() const; bool IsOpeningOrClosing() const; @@ -88,6 +94,7 @@ class Window : public Drawable { virtual bool IsSystemGraphicUpdateAllowed() const; unsigned long ID; + FontRef font; BitmapRef windowskin, contents; bool stretch = true; Rect cursor_rect; @@ -153,6 +160,7 @@ inline BitmapRef Window::GetContents() const { inline void Window::SetContents(BitmapRef const& ncontents) { contents = ncontents; + contents->SetFont(font); } inline bool Window::GetStretch() const { @@ -236,6 +244,14 @@ inline int Window::GetHeight() const { return height; } +inline int Window::GetRightX() const { + return x + width; +} + +inline int Window::GetBottomY() const { + return y + height; +} + inline int Window::GetOx() const { return ox; } @@ -304,4 +320,15 @@ inline bool Window::IsSystemGraphicUpdateAllowed() const { return !IsClosing(); } +inline FontRef Window::GetFont() const { + return font; +} + +inline void Window::SetFont(FontRef font) { + this->font = font; + if (contents) { + contents->SetFont(font); + } +} + #endif diff --git a/src/window_help.cpp b/src/window_help.cpp index 3641a747687..1417a6bc741 100644 --- a/src/window_help.cpp +++ b/src/window_help.cpp @@ -61,7 +61,7 @@ void Window_Help::AddText(std::string text, int color, Text::Alignment align, bo // Special handling for proportional fonts: If the "normal" space is already small do not half it again if (nextpos != decltype(text)::npos) { - int space_width = Text::GetSize(*Font::Default(), " ").width; + int space_width = Text::GetSize(*(font ? font : Font::Default()), " ").width; if (halfwidthspace && space_width >= 6) { text_x_offset += space_width / 2; diff --git a/src/window_settings.cpp b/src/window_settings.cpp index 0cb1003d490..0a71950a38c 100644 --- a/src/window_settings.cpp +++ b/src/window_settings.cpp @@ -23,6 +23,7 @@ #include "text.h" #include "window_settings.h" #include "game_config.h" +#include "game_system.h" #include "input_buttons.h" #include "keys.h" #include "output.h" @@ -132,9 +133,18 @@ void Window_Settings::Refresh() { case eAudioMidi: RefreshAudioMidi(); break; + case eAudioSoundfont: + RefreshAudioSoundfont(); + break; case eEngine: RefreshEngine(); break; + case eEngineFont1: + RefreshEngineFont(false); + break; + case eEngineFont2: + RefreshEngineFont(true); + break; case eLicense: RefreshLicense(); break; @@ -268,6 +278,7 @@ void Window_Settings::RefreshVideo() { AddOption(cfg.fps_render_window, [](){ DisplayUi->ToggleShowFpsOnTitle(); }); AddOption(cfg.stretch, []() { DisplayUi->ToggleStretch(); }); AddOption(cfg.scaling_mode, [this](){ DisplayUi->SetScalingMode(static_cast(GetCurrentOption().current_value)); }); + AddOption(cfg.pause_when_focus_lost, [cfg]() mutable { DisplayUi->SetPauseWhenFocusLost(cfg.pause_when_focus_lost.Toggle()); }); AddOption(cfg.touch_ui, [](){ DisplayUi->ToggleTouchUi(); }); AddOption(cfg.game_resolution, [this]() { DisplayUi->SetGameResolution(static_cast(GetCurrentOption().current_value)); }); } @@ -277,8 +288,10 @@ void Window_Settings::RefreshAudio() { AddOption(cfg.music_volume, [this](){ Audio().BGM_SetGlobalVolume(GetCurrentOption().current_value); }); AddOption(cfg.sound_volume, [this](){ Audio().SE_SetGlobalVolume(GetCurrentOption().current_value); }); - AddOption(MenuItem("MIDI drivers", "Configure MIDI playback", ""), [this](){ Push(eAudioMidi); }); - AddOption(cfg.soundfont, [this](){ }); + if (cfg.fluidsynth_midi.IsOptionVisible() || cfg.wildmidi_midi.IsOptionVisible() || cfg.native_midi.IsOptionVisible() || cfg.fmmidi_midi.IsOptionVisible()) { + AddOption(MenuItem("MIDI drivers", "Configure MIDI playback", ""), [this]() { Push(eAudioMidi); }); + } + AddOption(cfg.soundfont, [this](){ Push(eAudioSoundfont); }); } void Window_Settings::RefreshAudioMidi() { @@ -289,64 +302,157 @@ void Window_Settings::RefreshAudioMidi() { AddOption(MenuItem("> Information <", "The first active and working option is used for MIDI", ""), [](){}); options.back().help2 = "Changes take effect when a new MIDI file is played"; -#if defined(HAVE_FLUIDSYNTH) || defined(HAVE_FLUIDLITE) - AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); - if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.fluidsynth_midi.Get()) { - options.back().text += " [In use]"; - used = true; + if (cfg.fluidsynth_midi.IsOptionVisible()) { + AddOption(cfg.fluidsynth_midi, []() { Audio().SetFluidsynthEnabled(Audio().GetConfig().fluidsynth_midi.Toggle()); }); + if (!MidiDecoder::CheckFluidsynth(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.fluidsynth_midi.Get()) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef HAVE_LIBWILDMIDI - AddOption(cfg.wildmidi_midi, [](){ Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); - if (!MidiDecoder::CheckWildMidi(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.wildmidi_midi.Get() && !used) { - options.back().text += " [In use]"; - used = true; + + if (cfg.wildmidi_midi.IsOptionVisible()) { + AddOption(cfg.wildmidi_midi, []() { Audio().SetWildMidiEnabled(Audio().GetConfig().wildmidi_midi.Toggle()); }); + if (!MidiDecoder::CheckWildMidi(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.wildmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef HAVE_NATIVE_MIDI - AddOption(cfg.native_midi, [](){ Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); - if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { - options.back().text += " [Not working]"; - options.back().color = Font::ColorKnockout; - } else if (cfg.native_midi.Get() && !used) { - options.back().text += " [In use]"; - used = true; + + if (cfg.native_midi.IsOptionVisible()) { + AddOption(cfg.native_midi, []() { Audio().SetNativeMidiEnabled(Audio().GetConfig().native_midi.Toggle()); }); + if (!GenericAudioMidiOut().IsInitialized(options.back().help2)) { + options.back().text += " [Not working]"; + options.back().color = Font::ColorKnockout; + } else if (cfg.native_midi.Get() && !used) { + options.back().text += " [In use]"; + used = true; + } } -#endif -#ifdef WANT_FMMIDI - AddOption(cfg.fmmidi_midi, [](){ Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); - if (cfg.fmmidi_midi.Get() && !used) { - options.back().text += " [In use]"; + + if (cfg.fmmidi_midi.IsOptionVisible()) { + AddOption(cfg.fmmidi_midi, []() { Audio().SetFmMidiEnabled(Audio().GetConfig().fmmidi_midi.Toggle()); }); + if (cfg.fmmidi_midi.Get() && !used) { + options.back().text += " [In use]"; + } } -#endif +} + +void Window_Settings::RefreshAudioSoundfont() { + auto fs = Game_Config::GetSoundfontFilesystem(); - if (options.size() == 1) { - options.pop_back(); - AddOption(MenuItem("MIDI is unavailable", "EasyRPG Player was not compiled with MIDI support", ""), [](){}); + if (!fs) { + Pop(); } + + fs.ClearCache(); + + auto acfg = Audio().GetConfig(); + AddOption(MenuItem("", "Attempt to find a suitable soundfont automatically", acfg.soundfont.Get().empty() ? "[x]" : ""), [this]() { + Audio().SetFluidsynthSoundfont({}); + Pop(); + }); + + auto list = fs.ListDirectory(); + assert(list); + + std::string sf_lower = Utils::LowerCase(Audio().GetFluidsynthSoundfont()); + for (const auto& item: *list) { + if (item.second.type == DirectoryTree::FileType::Regular && StringView(item.first).ends_with(".sf2")) { + AddOption(MenuItem(item.second.name, "Use this custom soundfont", StringView(sf_lower).ends_with(item.first) ? "[x]" : ""), [this, fs, item]() { + Audio().SetFluidsynthSoundfont(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + Pop(); + }); + } + } + + AddOption(MenuItem("", "Open the soundfont directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); } void Window_Settings::RefreshEngine() { auto& cfg = Player::player_config; // FIXME: Binding &cfg is not needed and generates a warning but MSVC requires it -#ifdef _MSC_VER + AddOption(cfg.font1, [this, &cfg]() { + font_size.Set(cfg.font1_size.Get()); + Push(eEngineFont1); + }); + if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_gothic) { + options.back().text += " [In use]"; + } + + AddOption(cfg.font2, [this, &cfg]() { + font_size.Set(cfg.font2_size.Get()); + Push(eEngineFont2); + }); + if (Main_Data::game_system->GetFontId() == lcf::rpg::System::Font_mincho) { + options.back().text += " [In use]"; + } + + AddOption(cfg.show_startup_logos, [this, &cfg](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); AddOption(cfg.settings_autosave, [&cfg](){ cfg.settings_autosave.Toggle(); }); AddOption(cfg.settings_in_title, [&cfg](){ cfg.settings_in_title.Toggle(); }); AddOption(cfg.settings_in_menu, [&cfg](){ cfg.settings_in_menu.Toggle(); }); - AddOption(cfg.show_startup_logos, [this, &cfg](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); -#else - AddOption(cfg.settings_autosave, [](){ cfg.settings_autosave.Toggle(); }); - AddOption(cfg.settings_in_title, [](){ cfg.settings_in_title.Toggle(); }); - AddOption(cfg.settings_in_menu, [](){ cfg.settings_in_menu.Toggle(); }); - AddOption(cfg.show_startup_logos, [this](){ cfg.show_startup_logos.Set(static_cast(GetCurrentOption().current_value)); }); -#endif +} + +void Window_Settings::RefreshEngineFont(bool mincho) { + auto fs = Game_Config::GetFontFilesystem(); + + if (!fs) { + Pop(); + } + + fs.ClearCache(); + + auto& cfg = Player::player_config; + + auto& setting = mincho ? cfg.font2 : cfg.font1; + + AddOption(MenuItem("", "Use the built-in pixel font", setting.Get().empty() ? "[x]" : ""), [this, mincho]() { + Font::SetDefault(nullptr, mincho); + Pop(); + }); + + //std::string font_lower = Utils::LowerCase(Audio().GetFluidsynthSoundfont()); + + auto list = fs.ListDirectory(); + assert(list); + for (const auto& item: *list) { + bool is_font = std::any_of(FileFinder::FONTS_TYPES.begin(), FileFinder::FONTS_TYPES.end(), [&item](const auto& ext) { + return StringView(item.first).ends_with(ext); + }); + + if (item.second.type == DirectoryTree::FileType::Regular && is_font) { + /*AddOption(MenuItem(item.second.name, "Use this custom soundfont", StringView(sf_lower).ends_with(item.first) ? "[x]" : ""), [fs, item]() { + Audio().SetFluidsynthSoundfont(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + });*/ + AddOption(MenuItem(item.second.name, "Use this font", ""), [=, &cfg, &setting]() mutable { + auto is = fs.OpenInputStream(item.second.name); + if (is) { + auto font = Font::CreateFtFont(std::move(is), font_size.Get(), false, false); + if (font) { + setting.Set(FileFinder::MakePath(fs.GetFullPath(), item.second.name)); + auto& setting_size = mincho ? cfg.font2_size : cfg.font1_size; + setting_size.Set(font->GetCurrentStyle().size); + Font::SetDefault(font, mincho); + Pop(); + } + } + }); + } + } + + AddOption(font_size, [this]() mutable { + font_size.Set(GetCurrentOption().current_value); + }); + + AddOption(MenuItem("", "Open the font directory in a file browser", ""), [fs]() { DisplayUi->OpenURL(fs.GetFullPath()); }); + } void Window_Settings::RefreshLicense() { diff --git a/src/window_settings.h b/src/window_settings.h index 80d1d34efee..7270ea2fe3e 100644 --- a/src/window_settings.h +++ b/src/window_settings.h @@ -43,8 +43,11 @@ class Window_Settings : public Window_Selectable { eVideo, eAudio, eAudioMidi, + eAudioSoundfont, eLicense, eEngine, + eEngineFont1, + eEngineFont2, eSave, eEnd, eAbout, @@ -110,6 +113,7 @@ class Window_Settings : public Window_Selectable { void Refresh(); Window_Help* help_window2 = nullptr; + RangeConfigParam font_size { "", "Font size to use. Not supported for the built-in font.", "", "", 12, 6, 16}; private: /** @@ -139,7 +143,9 @@ class Window_Settings : public Window_Selectable { void RefreshVideo(); void RefreshAudio(); void RefreshAudioMidi(); + void RefreshAudioSoundfont(); void RefreshEngine(); + void RefreshEngineFont(bool mincho); void RefreshLicense(); void UpdateHelp() override;