2023-01-05 20:25:55 +08:00
/**************************************************************************/
/* os.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 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. */
/**************************************************************************/
2018-01-05 07:50:27 +08:00
2014-02-10 09:10:30 +08:00
# include "os.h"
2017-01-14 01:25:43 +08:00
2020-11-08 06:33:38 +08:00
# include "core/config/project_settings.h"
2020-04-28 21:19:37 +08:00
# include "core/input/input.h"
2021-06-11 20:51:48 +08:00
# include "core/io/dir_access.h"
# include "core/io/file_access.h"
2023-01-14 03:24:12 +08:00
# include "core/io/json.h"
2018-09-12 00:13:45 +08:00
# include "core/os/midi_driver.h"
# include "core/version_generated.gen.h"
2017-01-14 01:25:43 +08:00
# include <stdarg.h>
2023-11-18 03:44:38 +08:00
# ifdef MINGW_ENABLED
# define MINGW_STDTHREAD_REDUNDANCY_WARNING
# include "thirdparty/mingw-std-threads/mingw.thread.h"
# define THREADING_NAMESPACE mingw_stdthread
# else
2022-08-24 15:50:33 +08:00
# include <thread>
2023-11-18 03:44:38 +08:00
# define THREADING_NAMESPACE std
# endif
2014-02-10 09:10:30 +08:00
2020-04-02 07:20:12 +08:00
OS * OS : : singleton = nullptr ;
2020-06-30 02:46:21 +08:00
uint64_t OS : : target_ticks = 0 ;
2014-02-10 09:10:30 +08:00
2017-03-05 23:44:50 +08:00
OS * OS : : get_singleton ( ) {
2014-02-10 09:10:30 +08:00
return singleton ;
}
2021-05-24 19:54:05 +08:00
uint64_t OS : : get_ticks_msec ( ) const {
return get_ticks_usec ( ) / 1000ULL ;
2019-03-29 21:53:11 +08:00
}
2020-05-31 20:19:31 +08:00
double OS : : get_unix_time ( ) const {
2018-12-20 05:18:52 +08:00
return 0 ;
}
2020-05-14 20:29:06 +08:00
2017-11-21 17:35:01 +08:00
void OS : : _set_logger ( CompositeLogger * p_logger ) {
2017-09-22 13:56:02 +08:00
if ( _logger ) {
memdelete ( _logger ) ;
2015-11-06 03:13:05 +08:00
}
2017-09-22 13:56:02 +08:00
_logger = p_logger ;
}
2017-11-21 17:35:01 +08:00
void OS : : add_logger ( Logger * p_logger ) {
if ( ! _logger ) {
Vector < Logger * > loggers ;
loggers . push_back ( p_logger ) ;
_logger = memnew ( CompositeLogger ( loggers ) ) ;
} else {
_logger - > add_logger ( p_logger ) ;
}
2017-09-22 13:56:02 +08:00
}
2023-04-28 19:15:36 +08:00
String OS : : get_identifier ( ) const {
return get_name ( ) . to_lower ( ) ;
}
2021-09-22 23:36:40 +08:00
void OS : : print_error ( const char * p_function , const char * p_file , int p_line , const char * p_code , const char * p_rationale , bool p_editor_notify , Logger : : ErrorType p_type ) {
2020-03-07 23:38:06 +08:00
if ( ! _stderr_enabled ) {
return ;
}
2022-03-23 17:08:58 +08:00
if ( _logger ) {
_logger - > log_error ( p_function , p_file , p_line , p_code , p_rationale , p_editor_notify , p_type ) ;
}
2014-02-10 09:10:30 +08:00
}
2017-03-05 23:44:50 +08:00
void OS : : print ( const char * p_format , . . . ) {
2020-03-07 23:38:06 +08:00
if ( ! _stdout_enabled ) {
return ;
}
2014-02-10 09:10:30 +08:00
va_list argp ;
va_start ( argp , p_format ) ;
2022-03-23 17:08:58 +08:00
if ( _logger ) {
_logger - > logv ( p_format , argp , false ) ;
}
2014-02-10 09:10:30 +08:00
va_end ( argp ) ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
2022-05-03 07:29:38 +08:00
void OS : : print_rich ( const char * p_format , . . . ) {
if ( ! _stdout_enabled ) {
return ;
}
va_list argp ;
va_start ( argp , p_format ) ;
if ( _logger ) {
_logger - > logv ( p_format , argp , false ) ;
}
va_end ( argp ) ;
}
2017-03-05 23:44:50 +08:00
void OS : : printerr ( const char * p_format , . . . ) {
2020-03-07 23:38:06 +08:00
if ( ! _stderr_enabled ) {
return ;
}
2014-02-10 09:10:30 +08:00
va_list argp ;
va_start ( argp , p_format ) ;
2022-03-23 17:08:58 +08:00
if ( _logger ) {
_logger - > logv ( p_format , argp , true ) ;
}
2014-02-10 09:10:30 +08:00
va_end ( argp ) ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
2021-07-23 00:23:48 +08:00
void OS : : alert ( const String & p_alert , const String & p_title ) {
fprintf ( stderr , " %s: %s \n " , p_title . utf8 ( ) . get_data ( ) , p_alert . utf8 ( ) . get_data ( ) ) ;
}
2014-02-10 09:10:30 +08:00
void OS : : set_low_processor_usage_mode ( bool p_enabled ) {
2017-03-05 23:44:50 +08:00
low_processor_usage_mode = p_enabled ;
2014-02-10 09:10:30 +08:00
}
bool OS : : is_in_low_processor_usage_mode ( ) const {
return low_processor_usage_mode ;
}
2017-11-23 01:40:43 +08:00
void OS : : set_low_processor_usage_mode_sleep_usec ( int p_usec ) {
low_processor_usage_mode_sleep_usec = p_usec ;
}
int OS : : get_low_processor_usage_mode_sleep_usec ( ) const {
return low_processor_usage_mode_sleep_usec ;
}
2021-09-01 22:47:12 +08:00
void OS : : set_delta_smoothing ( bool p_enabled ) {
_delta_smoothing_enabled = p_enabled ;
}
bool OS : : is_delta_smoothing_enabled ( ) const {
return _delta_smoothing_enabled ;
}
2014-02-10 09:10:30 +08:00
String OS : : get_executable_path ( ) const {
return _execpath ;
}
2017-08-07 18:17:31 +08:00
int OS : : get_process_id ( ) const {
2014-04-05 23:39:30 +08:00
return - 1 ;
2020-05-19 21:46:49 +08:00
}
2014-04-05 23:39:30 +08:00
2014-02-10 09:10:30 +08:00
bool OS : : is_stdout_verbose ( ) const {
return _verbose_stdout ;
}
2020-07-09 15:32:42 +08:00
bool OS : : is_stdout_debug_enabled ( ) const {
return _debug_stdout ;
}
2020-03-07 23:38:06 +08:00
bool OS : : is_stdout_enabled ( ) const {
return _stdout_enabled ;
}
bool OS : : is_stderr_enabled ( ) const {
return _stderr_enabled ;
}
void OS : : set_stdout_enabled ( bool p_enabled ) {
_stdout_enabled = p_enabled ;
}
void OS : : set_stderr_enabled ( bool p_enabled ) {
_stderr_enabled = p_enabled ;
}
2014-02-10 09:10:30 +08:00
int OS : : get_exit_code ( ) const {
return _exit_code ;
}
2020-05-14 20:29:06 +08:00
2014-02-10 09:10:30 +08:00
void OS : : set_exit_code ( int p_code ) {
2017-03-05 23:44:50 +08:00
_exit_code = p_code ;
2014-02-10 09:10:30 +08:00
}
String OS : : get_locale ( ) const {
return " en " ;
}
2021-09-16 15:27:56 +08:00
// Non-virtual helper to extract the 2 or 3-letter language code from
// `get_locale()` in a way that's consistent for all platforms.
String OS : : get_locale_language ( ) const {
return get_locale ( ) . left ( 3 ) . replace ( " _ " , " " ) ;
}
2021-12-20 17:28:54 +08:00
// Embedded PCK offset.
uint64_t OS : : get_embedded_pck_offset ( ) const {
return 0 ;
}
2017-11-27 02:00:53 +08:00
// Helper function to ensure that a dir name/path will be valid on the OS
2023-01-14 22:07:28 +08:00
String OS : : get_safe_dir_name ( const String & p_dir_name , bool p_allow_paths ) const {
String safe_dir_name = p_dir_name ;
2017-11-27 02:00:53 +08:00
Vector < String > invalid_chars = String ( " : * ? \" < > | " ) . split ( " " ) ;
2023-01-14 22:07:28 +08:00
if ( p_allow_paths ) {
2017-11-27 02:00:53 +08:00
// Dir separators are allowed, but disallow ".." to avoid going up the filesystem
invalid_chars . push_back ( " .. " ) ;
2023-01-14 22:07:28 +08:00
safe_dir_name = safe_dir_name . replace ( " \\ " , " / " ) . strip_edges ( ) ;
2017-11-27 02:00:53 +08:00
} else {
invalid_chars . push_back ( " / " ) ;
2023-01-14 22:07:28 +08:00
invalid_chars . push_back ( " \\ " ) ;
safe_dir_name = safe_dir_name . strip_edges ( ) ;
// These directory names are invalid.
if ( safe_dir_name = = " . " ) {
safe_dir_name = " dot " ;
} else if ( safe_dir_name = = " .. " ) {
safe_dir_name = " twodots " ;
}
2017-11-27 02:00:53 +08:00
}
for ( int i = 0 ; i < invalid_chars . size ( ) ; i + + ) {
safe_dir_name = safe_dir_name . replace ( invalid_chars [ i ] , " - " ) ;
2016-06-03 02:39:37 +08:00
}
2024-06-25 02:09:44 +08:00
// Trim trailing periods from the returned value as it's not valid for folder names on Windows.
// This check is still applied on non-Windows platforms so the returned value is consistent across platforms.
return safe_dir_name . rstrip ( " . " ) ;
2016-06-03 02:39:37 +08:00
}
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
// Path to data, config, cache, etc. OS-specific folders
// Get properly capitalized engine name for system paths
String OS : : get_godot_dir_name ( ) const {
// Default to lowercase, so only override when different case is needed
2017-11-20 04:18:01 +08:00
return String ( VERSION_SHORT_NAME ) . to_lower ( ) ;
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
}
// OS equivalent of XDG_DATA_HOME
String OS : : get_data_path ( ) const {
return " . " ;
}
// OS equivalent of XDG_CONFIG_HOME
String OS : : get_config_path ( ) const {
return " . " ;
}
// OS equivalent of XDG_CACHE_HOME
String OS : : get_cache_path ( ) const {
return " . " ;
}
2020-01-11 00:02:29 +08:00
// Path to macOS .app bundle resources
String OS : : get_bundle_resource_dir ( ) const {
return " . " ;
2020-05-19 21:46:49 +08:00
}
2020-01-11 00:02:29 +08:00
2021-05-13 14:25:09 +08:00
// Path to macOS .app bundle embedded icon
String OS : : get_bundle_icon_path ( ) const {
return String ( ) ;
}
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
// OS specific path for user://
2017-11-17 22:25:22 +08:00
String OS : : get_user_data_dir ( ) const {
2014-02-10 09:10:30 +08:00
return " . " ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
// Absolute path to res://
String OS : : get_resource_dir ( ) const {
return ProjectSettings : : get_singleton ( ) - > get_resource_path ( ) ;
}
// Access system-specific dirs like Documents, Downloads, etc.
2021-07-11 09:39:31 +08:00
String OS : : get_system_dir ( SystemDir p_dir , bool p_shared_storage ) const {
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
return " . " ;
}
2024-01-09 09:36:19 +08:00
Error OS : : shell_open ( const String & p_uri ) {
2014-02-10 09:10:30 +08:00
return ERR_UNAVAILABLE ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
2022-12-07 11:33:35 +08:00
Error OS : : shell_show_in_file_manager ( String p_path , bool p_open_folder ) {
2023-07-06 13:14:08 +08:00
p_path = p_path . trim_prefix ( " file:// " ) ;
if ( ! DirAccess : : dir_exists_absolute ( p_path ) ) {
2022-12-07 11:33:35 +08:00
p_path = p_path . get_base_dir ( ) ;
}
2023-07-06 13:14:08 +08:00
p_path = String ( " file:// " ) + p_path ;
2022-12-07 11:33:35 +08:00
return shell_open ( p_path ) ;
}
2014-02-10 09:10:30 +08:00
// implement these with the canvas?
2019-01-31 02:07:46 +08:00
uint64_t OS : : get_static_memory_usage ( ) const {
2017-01-06 21:15:44 +08:00
return Memory : : get_mem_usage ( ) ;
2014-02-10 09:10:30 +08:00
}
2019-01-31 02:07:46 +08:00
uint64_t OS : : get_static_memory_peak_usage ( ) const {
2017-01-06 21:15:44 +08:00
return Memory : : get_mem_max_usage ( ) ;
2014-02-10 09:10:30 +08:00
}
2017-03-05 23:44:50 +08:00
Error OS : : set_cwd ( const String & p_cwd ) {
2014-02-10 09:10:30 +08:00
return ERR_CANT_OPEN ;
}
2023-04-03 16:46:29 +08:00
Dictionary OS : : get_memory_info ( ) const {
Dictionary meminfo ;
meminfo [ " physical " ] = - 1 ;
meminfo [ " free " ] = - 1 ;
meminfo [ " available " ] = - 1 ;
meminfo [ " stack " ] = - 1 ;
return meminfo ;
2014-02-10 09:10:30 +08:00
}
void OS : : yield ( ) {
}
2020-03-03 21:36:29 +08:00
void OS : : ensure_user_data_dir ( ) {
2017-11-17 22:25:22 +08:00
String dd = get_user_data_dir ( ) ;
2022-03-10 22:27:09 +08:00
if ( DirAccess : : exists ( dd ) ) {
2014-02-10 09:10:30 +08:00
return ;
}
2022-03-23 17:08:58 +08:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_FILESYSTEM ) ;
2014-09-20 05:39:50 +08:00
Error err = da - > make_dir_recursive ( dd ) ;
2024-10-11 22:17:49 +08:00
ERR_FAIL_COND_MSG ( err ! = OK , vformat ( " Error attempting to create data dir: %s. " , dd ) ) ;
2014-02-10 09:10:30 +08:00
}
String OS : : get_model_name ( ) const {
2017-03-05 23:44:50 +08:00
return " GenericDevice " ;
2014-02-10 09:10:30 +08:00
}
2022-07-29 21:30:52 +08:00
void OS : : set_cmdline ( const char * p_execpath , const List < String > & p_args , const List < String > & p_user_args ) {
2022-01-06 17:34:10 +08:00
_execpath = String : : utf8 ( p_execpath ) ;
2014-02-10 09:10:30 +08:00
_cmdline = p_args ;
2022-07-29 21:30:52 +08:00
_user_args = p_user_args ;
2020-05-19 21:46:49 +08:00
}
2014-02-10 09:10:30 +08:00
2017-08-07 18:17:31 +08:00
String OS : : get_unique_id ( ) const {
2023-09-27 21:17:02 +08:00
return " " ;
2014-02-10 09:10:30 +08:00
}
int OS : : get_processor_count ( ) const {
2023-11-18 03:44:38 +08:00
return THREADING_NAMESPACE : : thread : : hardware_concurrency ( ) ;
2014-02-10 09:10:30 +08:00
}
2020-12-27 08:50:21 +08:00
String OS : : get_processor_name ( ) const {
return " " ;
}
2019-02-26 22:58:47 +08:00
void OS : : set_has_server_feature_callback ( HasServerFeatureCallback p_callback ) {
has_server_feature_callback = p_callback ;
}
2017-10-03 03:38:39 +08:00
bool OS : : has_feature ( const String & p_feature ) {
2021-03-26 22:33:36 +08:00
// Feature tags are always lowercase for consistency.
2023-04-28 19:15:36 +08:00
if ( p_feature = = get_identifier ( ) ) {
2021-03-26 22:33:36 +08:00
return true ;
}
Implement Running Godot as Movie Writer
* Allows running the game in "movie writer" mode.
* It ensures entirely stable framerate, so your run can be saved stable and with proper sound (which is impossible if your CPU/GPU can't sustain doing this in real-time).
* If disabling vsync, it can save movies faster than the game is run, but if you want to control the interaction it can get difficult.
* Implements a simple, default MJPEG writer.
This new features has two main use cases, which have high demand:
* Saving game videos in high quality and ensuring the frame rate is *completely* stable, always.
* Using Godot as a tool to make movies and animations (which is ideal if you want interaction, or creating them procedurally. No other software is as good for this).
**Note**: This feature **IS NOT** for capturing real-time footage. Use something like OBS, SimpleScreenRecorder or FRAPS to achieve that, as they do a much better job at intercepting the compositor than Godot can probably do using Vulkan or OpenGL natively. If your game runs near real-time when capturing, you can still use this feature but it will play no sound (sound will be saved directly).
Usage:
$ godot --write-movie movie.avi [scene_file.tscn]
Missing:
* Options for configuring video writing via GLOBAL_DEF
* UI Menu for launching with this mode from the editor.
* Add to list of command line options.
* Add a feature tag to override configurations when movie writing (fantastic for saving videos with highest quality settings).
2022-06-17 06:55:19 +08:00
if ( p_feature = = " movie " ) {
return _writing_movie ;
}
2017-07-20 04:00:46 +08:00
# ifdef DEBUG_ENABLED
2020-05-14 22:41:43 +08:00
if ( p_feature = = " debug " ) {
2017-07-20 04:00:46 +08:00
return true ;
2020-05-14 22:41:43 +08:00
}
2022-10-27 00:23:09 +08:00
# endif // DEBUG_ENABLED
# ifdef TOOLS_ENABLED
if ( p_feature = = " editor " ) {
return true ;
}
2023-12-03 01:55:27 +08:00
if ( p_feature = = " editor_hint " ) {
return _in_editor ;
} else if ( p_feature = = " editor_runtime " ) {
return ! _in_editor ;
}
2017-07-20 04:00:46 +08:00
# else
2022-10-27 00:23:09 +08:00
if ( p_feature = = " template " ) {
2017-07-20 04:00:46 +08:00
return true ;
2022-02-16 20:56:32 +08:00
}
2022-10-27 00:23:09 +08:00
# ifdef DEBUG_ENABLED
if ( p_feature = = " template_debug " ) {
2018-07-26 05:20:54 +08:00
return true ;
2020-05-14 22:41:43 +08:00
}
2018-07-26 05:20:54 +08:00
# else
2022-10-27 00:23:09 +08:00
if ( p_feature = = " template_release " | | p_feature = = " release " ) {
2018-07-26 05:20:54 +08:00
return true ;
2022-02-16 20:56:32 +08:00
}
2022-10-27 00:23:09 +08:00
# endif // DEBUG_ENABLED
# endif // TOOLS_ENABLED
2017-07-20 04:00:46 +08:00
2022-12-04 01:17:04 +08:00
# ifdef REAL_T_IS_DOUBLE
if ( p_feature = = " double " ) {
return true ;
}
# else
if ( p_feature = = " single " ) {
return true ;
}
# endif // REAL_T_IS_DOUBLE
2017-10-03 03:38:39 +08:00
if ( sizeof ( void * ) = = 8 & & p_feature = = " 64 " ) {
return true ;
}
if ( sizeof ( void * ) = = 4 & & p_feature = = " 32 " ) {
return true ;
}
2022-06-06 03:10:20 +08:00
# if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
# if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
2024-11-04 15:28:14 +08:00
# if defined(MACOS_ENABLED)
if ( p_feature = = " universal " ) {
return true ;
}
# endif
2017-10-02 23:01:43 +08:00
if ( p_feature = = " x86_64 " ) {
return true ;
}
2022-06-06 03:10:20 +08:00
# elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
if ( p_feature = = " x86_32 " ) {
return true ;
}
# endif
2017-10-02 23:01:43 +08:00
if ( p_feature = = " x86 " ) {
return true ;
}
2022-06-06 03:10:20 +08:00
# elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
# if defined(__aarch64__) || defined(_M_ARM64)
2024-11-04 15:28:14 +08:00
# if defined(MACOS_ENABLED)
if ( p_feature = = " universal " ) {
return true ;
}
# endif
2017-10-02 23:01:43 +08:00
if ( p_feature = = " arm64 " ) {
return true ;
}
2022-06-06 03:10:20 +08:00
# elif defined(__arm__) || defined(_M_ARM)
if ( p_feature = = " arm32 " ) {
return true ;
}
# endif
2017-10-02 23:01:43 +08:00
# if defined(__ARM_ARCH_7A__)
if ( p_feature = = " armv7a " | | p_feature = = " armv7 " ) {
return true ;
}
# endif
# if defined(__ARM_ARCH_7S__)
if ( p_feature = = " armv7s " | | p_feature = = " armv7 " ) {
return true ;
}
# endif
if ( p_feature = = " arm " ) {
return true ;
}
2021-08-29 06:40:32 +08:00
# elif defined(__riscv)
# if __riscv_xlen == 8
if ( p_feature = = " rv64 " ) {
return true ;
}
# endif
if ( p_feature = = " riscv " ) {
return true ;
}
2021-11-02 04:23:18 +08:00
# elif defined(__powerpc__)
# if defined(__powerpc64__)
if ( p_feature = = " ppc64 " ) {
return true ;
}
# endif
if ( p_feature = = " ppc " ) {
return true ;
}
2022-06-06 03:10:20 +08:00
# elif defined(__wasm__)
# if defined(__wasm64__)
if ( p_feature = = " wasm64 " ) {
return true ;
}
# elif defined(__wasm32__)
if ( p_feature = = " wasm32 " ) {
return true ;
}
# endif
if ( p_feature = = " wasm " ) {
return true ;
}
2017-10-02 23:01:43 +08:00
# endif
2017-10-03 03:38:39 +08:00
2023-11-06 04:59:36 +08:00
# if defined(IOS_SIMULATOR)
if ( p_feature = = " simulator " ) {
return true ;
}
# endif
2023-12-02 02:39:09 +08:00
# ifdef THREADS_ENABLED
if ( p_feature = = " threads " ) {
return true ;
}
2024-06-25 01:52:21 +08:00
# else
if ( p_feature = = " nothreads " ) {
return true ;
}
2023-12-02 02:39:09 +08:00
# endif
2020-05-14 22:41:43 +08:00
if ( _check_internal_feature_support ( p_feature ) ) {
2017-07-20 04:00:46 +08:00
return true ;
2020-05-14 22:41:43 +08:00
}
2017-07-20 04:00:46 +08:00
2024-11-06 03:21:44 +08:00
if ( has_server_feature_callback ) {
return has_server_feature_callback ( p_feature ) ;
}
# ifdef DEBUG_ENABLED
else if ( is_stdout_verbose ( ) ) {
WARN_PRINT_ONCE ( " Server features cannot be checked before RenderingServer has been created. If you are checking a server feature, consider moving your OS::has_feature call after INITIALIZATION_LEVEL_SERVERS. " ) ;
2019-02-26 22:58:47 +08:00
}
2024-11-06 03:21:44 +08:00
# endif
2019-02-26 22:58:47 +08:00
2020-05-14 22:41:43 +08:00
if ( ProjectSettings : : get_singleton ( ) - > has_custom_feature ( p_feature ) ) {
2018-07-18 16:22:59 +08:00
return true ;
2020-05-14 22:41:43 +08:00
}
2018-07-18 16:22:59 +08:00
2017-07-20 04:00:46 +08:00
return false ;
}
2021-03-30 20:42:50 +08:00
bool OS : : is_sandboxed ( ) const {
return false ;
}
2018-07-20 05:58:15 +08:00
void OS : : set_restart_on_exit ( bool p_restart , const List < String > & p_restart_arguments ) {
restart_on_exit = p_restart ;
restart_commandline = p_restart_arguments ;
}
bool OS : : is_restart_on_exit_set ( ) const {
return restart_on_exit ;
}
2018-07-20 14:37:10 +08:00
List < String > OS : : get_restart_on_exit_arguments ( ) const {
2018-07-20 05:58:15 +08:00
return restart_commandline ;
}
2020-02-18 05:06:54 +08:00
PackedStringArray OS : : get_connected_midi_inputs ( ) {
2020-05-14 22:41:43 +08:00
if ( MIDIDriver : : get_singleton ( ) ) {
2018-07-14 20:11:28 +08:00
return MIDIDriver : : get_singleton ( ) - > get_connected_inputs ( ) ;
2020-05-14 22:41:43 +08:00
}
2018-07-14 20:11:28 +08:00
2020-02-18 05:06:54 +08:00
PackedStringArray list ;
2020-06-01 01:10:27 +08:00
ERR_FAIL_V_MSG ( list , vformat ( " MIDI input isn't supported on %s. " , OS : : get_singleton ( ) - > get_name ( ) ) ) ;
2018-07-14 20:11:28 +08:00
}
2018-08-04 08:17:33 +08:00
void OS : : open_midi_inputs ( ) {
2020-05-14 22:41:43 +08:00
if ( MIDIDriver : : get_singleton ( ) ) {
2018-08-04 08:17:33 +08:00
MIDIDriver : : get_singleton ( ) - > open ( ) ;
2020-06-01 01:10:27 +08:00
} else {
ERR_PRINT ( vformat ( " MIDI input isn't supported on %s. " , OS : : get_singleton ( ) - > get_name ( ) ) ) ;
2020-05-14 22:41:43 +08:00
}
2018-08-04 08:17:33 +08:00
}
void OS : : close_midi_inputs ( ) {
2020-05-14 22:41:43 +08:00
if ( MIDIDriver : : get_singleton ( ) ) {
2018-08-04 08:17:33 +08:00
MIDIDriver : : get_singleton ( ) - > close ( ) ;
2020-06-01 01:10:27 +08:00
} else {
ERR_PRINT ( vformat ( " MIDI input isn't supported on %s. " , OS : : get_singleton ( ) - > get_name ( ) ) ) ;
2020-05-14 22:41:43 +08:00
}
2018-08-04 08:17:33 +08:00
}
2020-06-30 02:46:21 +08:00
void OS : : add_frame_delay ( bool p_can_draw ) {
const uint32_t frame_delay = Engine : : get_singleton ( ) - > get_frame_delay ( ) ;
if ( frame_delay ) {
// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
// the actual frame time into account.
// Due to the high fluctuation of the actual sleep duration, it's not recommended
// to use this as a FPS limiter.
delay_usec ( frame_delay * 1000 ) ;
}
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
// previous frame time into account for a smoother result.
uint64_t dynamic_delay = 0 ;
if ( is_in_low_processor_usage_mode ( ) | | ! p_can_draw ) {
dynamic_delay = get_low_processor_usage_mode_sleep_usec ( ) ;
}
2022-09-23 04:15:07 +08:00
const int max_fps = Engine : : get_singleton ( ) - > get_max_fps ( ) ;
if ( max_fps > 0 & & ! Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
2020-06-30 02:46:21 +08:00
// Override the low processor usage mode sleep delay if the target FPS is lower.
2022-09-23 04:15:07 +08:00
dynamic_delay = MAX ( dynamic_delay , ( uint64_t ) ( 1000000 / max_fps ) ) ;
2020-06-30 02:46:21 +08:00
}
if ( dynamic_delay > 0 ) {
target_ticks + = dynamic_delay ;
uint64_t current_ticks = get_ticks_usec ( ) ;
if ( current_ticks < target_ticks ) {
delay_usec ( target_ticks - current_ticks ) ;
}
current_ticks = get_ticks_usec ( ) ;
target_ticks = MIN ( MAX ( target_ticks , current_ticks - dynamic_delay ) , current_ticks + dynamic_delay ) ;
}
}
2023-04-28 19:15:36 +08:00
Error OS : : setup_remote_filesystem ( const String & p_server_host , int p_port , const String & p_password , String & r_project_path ) {
return default_rfs . synchronize_with_server ( p_server_host , p_port , p_password , r_project_path ) ;
}
2023-01-25 19:17:11 +08:00
OS : : PreferredTextureFormat OS : : get_preferred_texture_format ( ) const {
# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC ; // By rule, ARM hardware uses ETC texture compression.
# elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC ; // By rule, X86 hardware prefers S3TC and derivatives.
# else
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC ; // Override in platform if needed.
# endif
}
2023-01-14 03:24:12 +08:00
void OS : : set_use_benchmark ( bool p_use_benchmark ) {
use_benchmark = p_use_benchmark ;
}
bool OS : : is_use_benchmark_set ( ) {
return use_benchmark ;
}
void OS : : set_benchmark_file ( const String & p_benchmark_file ) {
benchmark_file = p_benchmark_file ;
}
String OS : : get_benchmark_file ( ) {
return benchmark_file ;
}
2023-12-08 19:52:49 +08:00
void OS : : benchmark_begin_measure ( const String & p_context , const String & p_what ) {
2023-01-14 03:24:12 +08:00
# ifdef TOOLS_ENABLED
2023-12-08 19:52:49 +08:00
Pair < String , String > mark_key ( p_context , p_what ) ;
ERR_FAIL_COND_MSG ( benchmark_marks_from . has ( mark_key ) , vformat ( " Benchmark key '%s:%s' already exists. " , p_context , p_what ) ) ;
benchmark_marks_from [ mark_key ] = OS : : get_singleton ( ) - > get_ticks_usec ( ) ;
2023-01-14 03:24:12 +08:00
# endif
}
2023-12-08 19:52:49 +08:00
void OS : : benchmark_end_measure ( const String & p_context , const String & p_what ) {
2023-01-14 03:24:12 +08:00
# ifdef TOOLS_ENABLED
2023-12-08 19:52:49 +08:00
Pair < String , String > mark_key ( p_context , p_what ) ;
ERR_FAIL_COND_MSG ( ! benchmark_marks_from . has ( mark_key ) , vformat ( " Benchmark key '%s:%s' doesn't exist. " , p_context , p_what ) ) ;
2023-01-14 03:24:12 +08:00
2023-12-08 19:52:49 +08:00
uint64_t total = OS : : get_singleton ( ) - > get_ticks_usec ( ) - benchmark_marks_from [ mark_key ] ;
double total_f = double ( total ) / double ( 1000000 ) ;
benchmark_marks_final [ mark_key ] = total_f ;
2023-01-14 03:24:12 +08:00
# endif
}
void OS : : benchmark_dump ( ) {
# ifdef TOOLS_ENABLED
if ( ! use_benchmark ) {
return ;
}
2023-12-08 19:52:49 +08:00
2023-01-14 03:24:12 +08:00
if ( ! benchmark_file . is_empty ( ) ) {
Ref < FileAccess > f = FileAccess : : open ( benchmark_file , FileAccess : : WRITE ) ;
if ( f . is_valid ( ) ) {
2023-12-08 19:52:49 +08:00
Dictionary benchmark_marks ;
for ( const KeyValue < Pair < String , String > , double > & E : benchmark_marks_final ) {
const String mark_key = vformat ( " [%s] %s " , E . key . first , E . key . second ) ;
benchmark_marks [ mark_key ] = E . value ;
}
2023-01-14 03:24:12 +08:00
Ref < JSON > json ;
json . instantiate ( ) ;
2023-12-08 19:52:49 +08:00
f - > store_string ( json - > stringify ( benchmark_marks , " \t " , false , true ) ) ;
2023-01-14 03:24:12 +08:00
}
} else {
2023-12-08 19:52:49 +08:00
HashMap < String , String > results ;
for ( const KeyValue < Pair < String , String > , double > & E : benchmark_marks_final ) {
if ( E . key . first = = " Startup " & & ! results . has ( E . key . first ) ) {
results . insert ( E . key . first , " " , true ) ; // Hack to make sure "Startup" always comes first.
}
results [ E . key . first ] + = vformat ( " \t \t - %s: %.3f msec. \n " , E . key . second , ( E . value * 1000 ) ) ;
}
2023-01-14 03:24:12 +08:00
print_line ( " BENCHMARK: " ) ;
2023-12-08 19:52:49 +08:00
for ( const KeyValue < String , String > & E : results ) {
print_line ( vformat ( " \t [%s] \n %s " , E . key , E . value ) ) ;
2023-01-14 03:24:12 +08:00
}
}
# endif
}
2014-02-10 09:10:30 +08:00
OS : : OS ( ) {
2017-03-05 23:44:50 +08:00
singleton = this ;
2017-11-21 17:35:01 +08:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( StdLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
2014-02-10 09:10:30 +08:00
}
OS : : ~ OS ( ) {
2022-03-23 17:08:58 +08:00
if ( _logger ) {
memdelete ( _logger ) ;
}
2020-04-02 07:20:12 +08:00
singleton = nullptr ;
2014-02-10 09:10:30 +08:00
}