2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* os_windows.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. */
/**************************************************************************/
2015-01-10 21:35:26 +01:00
2014-02-10 02:10:30 +01:00
# include "os_windows.h"
2016-12-21 06:29:58 +01:00
2023-06-08 14:51:32 +02:00
# include "display_server_windows.h"
# include "joypad_windows.h"
# include "lang_table.h"
# include "windows_terminal_logger.h"
2024-01-11 13:33:35 +01:00
# include "windows_utils.h"
2023-06-08 14:51:32 +02:00
2020-02-27 03:30:20 +01:00
# include "core/debugger/engine_debugger.h"
# include "core/debugger/script_debugger.h"
2018-09-11 18:13:45 +02:00
# include "core/io/marshalls.h"
# include "core/version_generated.gen.h"
2021-09-23 08:56:12 +02:00
# include "drivers/unix/net_socket_posix.h"
2014-02-10 02:10:30 +01:00
# include "drivers/windows/dir_access_windows.h"
# include "drivers/windows/file_access_windows.h"
2023-10-19 13:35:10 +02:00
# include "drivers/windows/file_access_windows_pipe.h"
2017-01-16 19:19:45 +01:00
# include "main/main.h"
2017-06-10 15:15:33 +02:00
# include "servers/audio_server.h"
2020-12-03 22:09:47 +01:00
# include "servers/rendering/rendering_server_default.h"
2022-11-21 14:04:01 +01:00
# include "servers/text_server.h"
2014-12-02 18:02:41 +01:00
2019-02-12 15:43:54 +01:00
# include <avrt.h>
2022-02-07 17:33:55 +01:00
# include <bcrypt.h>
2019-02-20 13:00:19 +01:00
# include <direct.h>
2019-04-22 18:42:11 +02:00
# include <knownfolders.h>
2016-04-29 18:57:57 +02:00
# include <process.h>
2023-04-03 10:46:29 +02:00
# include <psapi.h>
2015-02-12 04:17:29 +01:00
# include <regstr.h>
2017-01-16 19:19:45 +01:00
# include <shlobj.h>
2022-10-11 12:39:41 +02:00
# include <wbemcli.h>
2023-04-13 21:17:55 +02:00
# include <wincrypt.h>
2015-02-12 04:17:29 +01:00
2023-03-27 10:49:05 +02:00
# ifdef DEBUG_ENABLED
# pragma pack(push, before_imagehlp, 8)
# include <imagehlp.h>
# pragma pack(pop, before_imagehlp)
# endif
2015-01-17 02:48:35 +01:00
extern " C " {
2018-11-02 23:10:44 +01:00
__declspec ( dllexport ) DWORD NvOptimusEnablement = 1 ;
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
2015-01-17 02:48:35 +01:00
}
2014-12-02 18:02:41 +01:00
2017-08-18 20:46:13 +02:00
// Workaround mingw-w64 < 4.0 bug
# ifndef WM_TOUCH
# define WM_TOUCH 576
2016-02-04 17:16:22 +01:00
# endif
2018-12-13 21:32:11 +01:00
# ifndef WM_POINTERUPDATE
# define WM_POINTERUPDATE 0x0245
# endif
2023-03-05 18:16:06 +01:00
// Missing in MinGW headers before 8.0.
# ifndef DWRITE_FONT_WEIGHT_SEMI_LIGHT
# define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT)350
# endif
2020-01-16 12:07:58 +01:00
# if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
# define GetProcAddress (void *)GetProcAddress
# endif
2017-12-01 12:42:57 +01:00
static String format_error_message ( DWORD id ) {
2020-04-02 01:20:12 +02:00
LPWSTR messageBuffer = nullptr ;
2017-12-01 12:42:57 +01:00
size_t size = FormatMessageW ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2020-04-02 01:20:12 +02:00
nullptr , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , nullptr ) ;
2017-12-01 12:42:57 +01:00
2020-07-27 12:43:20 +02:00
String msg = " Error " + itos ( id ) + " : " + String : : utf16 ( ( const char16_t * ) messageBuffer , size ) ;
2017-12-01 12:42:57 +01:00
LocalFree ( messageBuffer ) ;
2023-03-27 10:49:05 +02:00
return msg . replace ( " \r " , " " ) . replace ( " \n " , " " ) ;
2017-12-01 12:42:57 +01:00
}
2022-01-28 07:30:01 +01:00
void RedirectStream ( const char * p_file_name , const char * p_mode , FILE * p_cpp_stream , const DWORD p_std_handle ) {
const HANDLE h_existing = GetStdHandle ( p_std_handle ) ;
if ( h_existing ! = INVALID_HANDLE_VALUE ) { // Redirect only if attached console have a valid handle.
const HANDLE h_cpp = reinterpret_cast < HANDLE > ( _get_osfhandle ( _fileno ( p_cpp_stream ) ) ) ;
if ( h_cpp = = INVALID_HANDLE_VALUE ) { // Redirect only if it's not already redirected to the pipe or file.
FILE * fp = p_cpp_stream ;
freopen_s ( & fp , p_file_name , p_mode , p_cpp_stream ) ; // Redirect stream.
setvbuf ( p_cpp_stream , nullptr , _IONBF , 0 ) ; // Disable stream buffering.
}
}
}
2014-02-10 02:10:30 +01:00
void RedirectIOToConsole ( ) {
2024-04-25 11:56:07 +02:00
// Save current handles.
HANDLE h_stdin = GetStdHandle ( STD_INPUT_HANDLE ) ;
HANDLE h_stdout = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
HANDLE h_stderr = GetStdHandle ( STD_ERROR_HANDLE ) ;
2021-12-16 14:00:55 +01:00
if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) {
2024-04-25 11:56:07 +02:00
// Restore redirection (Note: if not redirected it's NULL handles not INVALID_HANDLE_VALUE).
if ( h_stdin ! = 0 ) {
SetStdHandle ( STD_INPUT_HANDLE , h_stdin ) ;
}
if ( h_stdout ! = 0 ) {
SetStdHandle ( STD_OUTPUT_HANDLE , h_stdout ) ;
}
if ( h_stderr ! = 0 ) {
SetStdHandle ( STD_ERROR_HANDLE , h_stderr ) ;
}
// Update file handles.
2022-01-28 07:30:01 +01:00
RedirectStream ( " CONIN$ " , " r " , stdin , STD_INPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stdout , STD_OUTPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stderr , STD_ERROR_HANDLE ) ;
2021-12-16 14:00:55 +01:00
}
2019-11-28 13:41:07 +01:00
}
2020-03-09 16:56:48 +01:00
BOOL WINAPI HandlerRoutine ( _In_ DWORD dwCtrlType ) {
2022-02-16 13:56:32 +01:00
if ( ! EngineDebugger : : is_active ( ) ) {
2020-03-09 16:56:48 +01:00
return FALSE ;
2022-02-16 13:56:32 +01:00
}
2018-12-18 14:17:43 +01:00
2020-03-09 16:56:48 +01:00
switch ( dwCtrlType ) {
case CTRL_C_EVENT :
EngineDebugger : : get_script_debugger ( ) - > set_depth ( - 1 ) ;
EngineDebugger : : get_script_debugger ( ) - > set_lines_left ( 1 ) ;
return TRUE ;
default :
return FALSE ;
}
2018-12-18 14:17:43 +01:00
}
2021-07-22 18:23:48 +02:00
void OS_Windows : : alert ( const String & p_alert , const String & p_title ) {
MessageBoxW ( nullptr , ( LPCWSTR ) ( p_alert . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) ( p_title . utf16 ( ) . get_data ( ) ) , MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL ) ;
}
2020-03-09 16:56:48 +01:00
void OS_Windows : : initialize_debugging ( ) {
SetConsoleCtrlHandler ( HandlerRoutine , TRUE ) ;
2017-12-10 19:38:26 +01:00
}
2022-05-09 01:25:49 +02:00
# ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
static void _error_handler ( void * p_self , const char * p_func , const char * p_file , int p_line , const char * p_error , const char * p_errorexp , bool p_editor_notify , ErrorHandlerType p_type ) {
String err_str ;
if ( p_errorexp & & p_errorexp [ 0 ] ) {
err_str = String : : utf8 ( p_errorexp ) ;
} else {
err_str = String : : utf8 ( p_file ) + " : " + itos ( p_line ) + " - " + String : : utf8 ( p_error ) ;
}
if ( p_editor_notify ) {
err_str + = " (User) \n " ;
} else {
err_str + = " \n " ;
}
OutputDebugStringW ( ( LPCWSTR ) err_str . utf16 ( ) . ptr ( ) ) ;
}
# endif
2020-03-09 16:56:48 +01:00
void OS_Windows : : initialize ( ) {
crash_handler . initialize ( ) ;
2017-12-10 19:38:26 +01:00
2022-05-09 01:25:49 +02:00
# ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
error_handlers . errfunc = _error_handler ;
error_handlers . userdata = this ;
add_error_handler ( & error_handlers ) ;
# endif
2020-03-09 16:56:48 +01:00
FileAccess : : make_default < FileAccessWindows > ( FileAccess : : ACCESS_RESOURCES ) ;
FileAccess : : make_default < FileAccessWindows > ( FileAccess : : ACCESS_USERDATA ) ;
FileAccess : : make_default < FileAccessWindows > ( FileAccess : : ACCESS_FILESYSTEM ) ;
2023-10-19 13:35:10 +02:00
FileAccess : : make_default < FileAccessWindowsPipe > ( FileAccess : : ACCESS_PIPE ) ;
2020-03-09 16:56:48 +01:00
DirAccess : : make_default < DirAccessWindows > ( DirAccess : : ACCESS_RESOURCES ) ;
DirAccess : : make_default < DirAccessWindows > ( DirAccess : : ACCESS_USERDATA ) ;
DirAccess : : make_default < DirAccessWindows > ( DirAccess : : ACCESS_FILESYSTEM ) ;
2017-12-10 19:38:26 +01:00
2020-03-09 16:56:48 +01:00
NetSocketPosix : : make_default ( ) ;
2017-12-10 19:38:26 +01:00
2020-03-09 16:56:48 +01:00
// We need to know how often the clock is updated
2022-01-04 22:38:44 +01:00
QueryPerformanceFrequency ( ( LARGE_INTEGER * ) & ticks_per_second ) ;
QueryPerformanceCounter ( ( LARGE_INTEGER * ) & ticks_start ) ;
2017-12-10 19:38:26 +01:00
2020-03-09 16:56:48 +01:00
// set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as
// long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)
timeBeginPeriod ( 1 ) ;
2017-12-10 19:38:26 +01:00
2022-05-13 15:04:37 +02:00
process_map = memnew ( ( HashMap < ProcessID , ProcessInfo > ) ) ;
2017-12-10 19:38:26 +01:00
2020-05-11 16:26:10 +02:00
// Add current Godot PID to the list of known PIDs
ProcessInfo current_pi = { } ;
PROCESS_INFORMATION current_pi_pi = { } ;
current_pi . pi = current_pi_pi ;
current_pi . pi . hProcess = GetCurrentProcess ( ) ;
process_map - > insert ( GetCurrentProcessId ( ) , current_pi ) ;
2021-05-06 02:48:18 +02:00
IPUnix : : make_default ( ) ;
2020-04-02 01:20:12 +02:00
main_loop = nullptr ;
2022-11-21 14:04:01 +01:00
HRESULT hr = DWriteCreateFactory ( DWRITE_FACTORY_TYPE_SHARED , __uuidof ( IDWriteFactory ) , reinterpret_cast < IUnknown * * > ( & dwrite_factory ) ) ;
if ( SUCCEEDED ( hr ) ) {
hr = dwrite_factory - > GetSystemFontCollection ( & font_collection , false ) ;
if ( SUCCEEDED ( hr ) ) {
dwrite_init = true ;
hr = dwrite_factory - > QueryInterface ( & dwrite_factory2 ) ;
if ( SUCCEEDED ( hr ) ) {
hr = dwrite_factory2 - > GetSystemFontFallback ( & system_font_fallback ) ;
if ( SUCCEEDED ( hr ) ) {
dwrite2_init = true ;
}
}
}
}
if ( ! dwrite_init ) {
print_verbose ( " Unable to load IDWriteFactory, system font support is disabled. " ) ;
} else if ( ! dwrite2_init ) {
print_verbose ( " Unable to load IDWriteFactory2, automatic system font fallback is disabled. " ) ;
}
2023-01-11 13:47:40 +01:00
FileAccessWindows : : initialize ( ) ;
2017-12-10 19:38:26 +01:00
}
2020-03-09 16:56:48 +01:00
void OS_Windows : : delete_main_loop ( ) {
2022-02-16 13:56:32 +01:00
if ( main_loop ) {
2020-03-09 16:56:48 +01:00
memdelete ( main_loop ) ;
2022-02-16 13:56:32 +01:00
}
2020-04-02 01:20:12 +02:00
main_loop = nullptr ;
2017-12-10 19:38:26 +01:00
}
2020-03-09 16:56:48 +01:00
void OS_Windows : : set_main_loop ( MainLoop * p_main_loop ) {
main_loop = p_main_loop ;
2017-12-10 19:38:26 +01:00
}
2020-03-09 16:56:48 +01:00
void OS_Windows : : finalize ( ) {
2022-11-21 14:04:01 +01:00
if ( dwrite_factory2 ) {
dwrite_factory2 - > Release ( ) ;
dwrite_factory2 = nullptr ;
}
if ( font_collection ) {
font_collection - > Release ( ) ;
font_collection = nullptr ;
}
if ( system_font_fallback ) {
system_font_fallback - > Release ( ) ;
system_font_fallback = nullptr ;
}
if ( dwrite_factory ) {
dwrite_factory - > Release ( ) ;
dwrite_factory = nullptr ;
}
2020-03-09 16:56:48 +01:00
# ifdef WINMIDI_ENABLED
driver_midi . close ( ) ;
# endif
2017-07-10 02:48:22 +02:00
2022-02-16 13:56:32 +01:00
if ( main_loop ) {
2020-03-09 16:56:48 +01:00
memdelete ( main_loop ) ;
2022-02-16 13:56:32 +01:00
}
2016-01-03 05:18:28 +01:00
2020-04-02 01:20:12 +02:00
main_loop = nullptr ;
2016-01-03 05:18:28 +01:00
}
2020-03-09 16:56:48 +01:00
void OS_Windows : : finalize_core ( ) {
2024-01-11 13:33:35 +01:00
while ( ! temp_libraries . is_empty ( ) ) {
_remove_temp_library ( temp_libraries . last ( ) - > key ) ;
}
2023-01-11 13:47:40 +01:00
FileAccessWindows : : finalize ( ) ;
2020-03-09 16:56:48 +01:00
timeEndPeriod ( 1 ) ;
2017-12-27 20:51:19 +01:00
2020-03-09 16:56:48 +01:00
memdelete ( process_map ) ;
NetSocketPosix : : cleanup ( ) ;
2022-05-09 01:25:49 +02:00
# ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
remove_error_handler ( & error_handlers ) ;
# endif
2017-07-10 02:48:22 +02:00
}
2022-02-07 17:33:55 +01:00
Error OS_Windows : : get_entropy ( uint8_t * r_buffer , int p_bytes ) {
NTSTATUS status = BCryptGenRandom ( nullptr , r_buffer , p_bytes , BCRYPT_USE_SYSTEM_PREFERRED_RNG ) ;
ERR_FAIL_COND_V ( status , FAILED ) ;
return OK ;
}
2017-07-10 02:48:22 +02:00
2023-03-27 10:49:05 +02:00
# ifdef DEBUG_ENABLED
void debug_dynamic_library_check_dependencies ( const String & p_root_path , const String & p_path , HashSet < String > & r_checked , HashSet < String > & r_missing ) {
if ( r_checked . has ( p_path ) ) {
return ;
}
r_checked . insert ( p_path ) ;
LOADED_IMAGE loaded_image ;
HANDLE file = CreateFileW ( ( LPCWSTR ) p_path . utf16 ( ) . get_data ( ) , GENERIC_READ , FILE_SHARE_READ , nullptr , OPEN_EXISTING , 0 , nullptr ) ;
if ( file ! = INVALID_HANDLE_VALUE ) {
HANDLE file_mapping = CreateFileMappingW ( file , nullptr , PAGE_READONLY | SEC_COMMIT , 0 , 0 , nullptr ) ;
if ( file_mapping ! = INVALID_HANDLE_VALUE ) {
PVOID mapping = MapViewOfFile ( file_mapping , FILE_MAP_READ , 0 , 0 , 0 ) ;
if ( mapping ) {
PIMAGE_DOS_HEADER dos_header = ( PIMAGE_DOS_HEADER ) mapping ;
PIMAGE_NT_HEADERS nt_header = nullptr ;
if ( dos_header - > e_magic = = IMAGE_DOS_SIGNATURE ) {
PCHAR nt_header_ptr ;
nt_header_ptr = ( ( PCHAR ) mapping ) + dos_header - > e_lfanew ;
nt_header = ( PIMAGE_NT_HEADERS ) nt_header_ptr ;
if ( nt_header - > Signature ! = IMAGE_NT_SIGNATURE ) {
nt_header = nullptr ;
}
}
if ( nt_header ) {
loaded_image . ModuleName = nullptr ;
loaded_image . hFile = file ;
loaded_image . MappedAddress = ( PUCHAR ) mapping ;
loaded_image . FileHeader = nt_header ;
loaded_image . Sections = ( PIMAGE_SECTION_HEADER ) ( ( LPBYTE ) & nt_header - > OptionalHeader + nt_header - > FileHeader . SizeOfOptionalHeader ) ;
loaded_image . NumberOfSections = nt_header - > FileHeader . NumberOfSections ;
loaded_image . SizeOfImage = GetFileSize ( file , nullptr ) ;
loaded_image . Characteristics = nt_header - > FileHeader . Characteristics ;
loaded_image . LastRvaSection = loaded_image . Sections ;
loaded_image . fSystemImage = false ;
loaded_image . fDOSImage = false ;
loaded_image . Links . Flink = & loaded_image . Links ;
loaded_image . Links . Blink = & loaded_image . Links ;
ULONG size = 0 ;
const IMAGE_IMPORT_DESCRIPTOR * import_desc = ( const IMAGE_IMPORT_DESCRIPTOR * ) ImageDirectoryEntryToData ( ( HMODULE ) loaded_image . MappedAddress , false , IMAGE_DIRECTORY_ENTRY_IMPORT , & size ) ;
if ( import_desc ) {
for ( ; import_desc - > Name & & import_desc - > FirstThunk ; import_desc + + ) {
char16_t full_name_wc [ MAX_PATH ] ;
2024-03-12 15:40:40 +01:00
const char * name_cs = ( const char * ) ImageRvaToVa ( loaded_image . FileHeader , loaded_image . MappedAddress , import_desc - > Name , nullptr ) ;
2023-03-27 10:49:05 +02:00
String name = String ( name_cs ) ;
if ( name . begins_with ( " api-ms-win- " ) ) {
r_checked . insert ( name ) ;
} else if ( SearchPathW ( nullptr , ( LPCWSTR ) name . utf16 ( ) . get_data ( ) , nullptr , MAX_PATH , ( LPWSTR ) full_name_wc , nullptr ) ) {
debug_dynamic_library_check_dependencies ( p_root_path , String : : utf16 ( full_name_wc ) , r_checked , r_missing ) ;
} else if ( SearchPathW ( ( LPCWSTR ) ( p_path . get_base_dir ( ) . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) name . utf16 ( ) . get_data ( ) , nullptr , MAX_PATH , ( LPWSTR ) full_name_wc , nullptr ) ) {
debug_dynamic_library_check_dependencies ( p_root_path , String : : utf16 ( full_name_wc ) , r_checked , r_missing ) ;
} else {
r_missing . insert ( name ) ;
}
}
}
}
UnmapViewOfFile ( mapping ) ;
}
CloseHandle ( file_mapping ) ;
}
CloseHandle ( file ) ;
}
}
# endif
2024-04-19 16:14:05 +02:00
Error OS_Windows : : open_dynamic_library ( const String & p_path , void * & p_library_handle , GDExtensionData * p_data ) {
2020-10-22 12:17:21 +02:00
String path = p_path . replace ( " / " , " \\ " ) ;
2018-01-04 19:42:29 +01:00
if ( ! FileAccess : : exists ( path ) ) {
2022-12-07 12:11:28 +01:00
//this code exists so gdextension can load .dll files from within the executable path
2022-08-30 02:34:01 +02:00
path = get_executable_path ( ) . get_base_dir ( ) . path_join ( p_path . get_file ( ) ) ;
2018-01-04 19:42:29 +01:00
}
2024-08-04 19:00:56 +02:00
// Path to load from may be different from original if we make copies.
String load_path = path ;
2018-01-04 19:42:29 +01:00
2024-01-01 12:48:28 +01:00
ERR_FAIL_COND_V ( ! FileAccess : : exists ( path ) , ERR_FILE_NOT_FOUND ) ;
2024-01-11 13:33:35 +01:00
// Here we want a copy to be loaded.
// This is so the original file isn't locked and can be updated by a compiler.
2024-04-19 16:14:05 +02:00
if ( p_data ! = nullptr & & p_data - > generate_temp_files ) {
2024-01-11 13:33:35 +01:00
// Copy the file to the same directory as the original with a prefix in the name.
// This is so relative path to dependencies are satisfied.
2024-08-04 19:00:56 +02:00
load_path = path . get_base_dir ( ) . path_join ( " ~ " + path . get_file ( ) ) ;
2024-01-11 13:33:35 +01:00
// If there's a left-over copy (possibly from a crash) then delete it first.
2024-08-04 19:00:56 +02:00
if ( FileAccess : : exists ( load_path ) ) {
DirAccess : : remove_absolute ( load_path ) ;
2024-01-11 13:33:35 +01:00
}
2024-08-04 19:00:56 +02:00
Error copy_err = DirAccess : : copy_absolute ( path , load_path ) ;
2024-01-11 13:33:35 +01:00
if ( copy_err ) {
ERR_PRINT ( " Error copying library: " + path ) ;
return ERR_CANT_CREATE ;
}
2024-08-04 19:00:56 +02:00
FileAccess : : set_hidden_attribute ( load_path , true ) ;
2024-01-11 13:33:35 +01:00
2024-08-04 19:00:56 +02:00
Error pdb_err = WindowsUtils : : copy_and_rename_pdb ( load_path ) ;
2024-01-11 13:33:35 +01:00
if ( pdb_err ! = OK & & pdb_err ! = ERR_SKIP ) {
WARN_PRINT ( vformat ( " Failed to rename the PDB file. The original PDB file for '%s' will be loaded. " , path ) ) ;
}
}
2017-12-06 13:29:01 +01:00
typedef DLL_DIRECTORY_COOKIE ( WINAPI * PAddDllDirectory ) ( PCWSTR ) ;
typedef BOOL ( WINAPI * PRemoveDllDirectory ) ( DLL_DIRECTORY_COOKIE ) ;
PAddDllDirectory add_dll_directory = ( PAddDllDirectory ) GetProcAddress ( GetModuleHandle ( " kernel32.dll " ) , " AddDllDirectory " ) ;
PRemoveDllDirectory remove_dll_directory = ( PRemoveDllDirectory ) GetProcAddress ( GetModuleHandle ( " kernel32.dll " ) , " RemoveDllDirectory " ) ;
2020-04-02 01:20:12 +02:00
bool has_dll_directory_api = ( ( add_dll_directory ! = nullptr ) & & ( remove_dll_directory ! = nullptr ) ) ;
DLL_DIRECTORY_COOKIE cookie = nullptr ;
2017-11-30 14:00:10 +01:00
2024-04-19 16:14:05 +02:00
if ( p_data ! = nullptr & & p_data - > also_set_library_path & & has_dll_directory_api ) {
2024-08-04 19:00:56 +02:00
cookie = add_dll_directory ( ( LPCWSTR ) ( load_path . get_base_dir ( ) . utf16 ( ) . get_data ( ) ) ) ;
2017-11-30 14:00:10 +01:00
}
2024-08-04 19:00:56 +02:00
p_library_handle = ( void * ) LoadLibraryExW ( ( LPCWSTR ) ( load_path . utf16 ( ) . get_data ( ) ) , nullptr , ( p_data ! = nullptr & & p_data - > also_set_library_path & & has_dll_directory_api ) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0 ) ;
2023-03-27 10:49:05 +02:00
if ( ! p_library_handle ) {
2024-04-19 16:14:05 +02:00
if ( p_data ! = nullptr & & p_data - > generate_temp_files ) {
2024-08-04 19:00:56 +02:00
DirAccess : : remove_absolute ( load_path ) ;
2024-01-11 13:33:35 +01:00
}
# ifdef DEBUG_ENABLED
2023-03-27 10:49:05 +02:00
DWORD err_code = GetLastError ( ) ;
2024-08-04 19:00:56 +02:00
HashSet < String > checked_libs ;
2023-03-27 10:49:05 +02:00
HashSet < String > missing_libs ;
2024-08-04 19:00:56 +02:00
debug_dynamic_library_check_dependencies ( load_path , load_path , checked_libs , missing_libs ) ;
2023-03-27 10:49:05 +02:00
if ( ! missing_libs . is_empty ( ) ) {
String missing ;
for ( const String & E : missing_libs ) {
if ( ! missing . is_empty ( ) ) {
missing + = " , " ;
}
missing + = E ;
}
2023-06-28 17:51:02 +02:00
ERR_FAIL_V_MSG ( ERR_CANT_OPEN , vformat ( " Can't open dynamic library: %s. Missing dependencies: %s. Error: %s. " , p_path , missing , format_error_message ( err_code ) ) ) ;
2023-03-27 10:49:05 +02:00
} else {
2023-06-28 17:51:02 +02:00
ERR_FAIL_V_MSG ( ERR_CANT_OPEN , vformat ( " Can't open dynamic library: %s. Error: %s. " , p_path , format_error_message ( err_code ) ) ) ;
2023-03-27 10:49:05 +02:00
}
2024-01-11 13:33:35 +01:00
# endif
2023-03-27 10:49:05 +02:00
}
2024-01-11 13:33:35 +01:00
# ifndef DEBUG_ENABLED
2023-09-09 17:46:44 +02:00
ERR_FAIL_NULL_V_MSG ( p_library_handle , ERR_CANT_OPEN , vformat ( " Can't open dynamic library: %s. Error: %s. " , p_path , format_error_message ( GetLastError ( ) ) ) ) ;
2023-03-27 10:49:05 +02:00
# endif
2017-11-30 14:00:10 +01:00
2018-01-26 11:48:20 +01:00
if ( cookie ) {
2017-12-06 13:29:01 +01:00
remove_dll_directory ( cookie ) ;
2017-11-30 14:00:10 +01:00
}
2024-04-19 16:14:05 +02:00
if ( p_data ! = nullptr & & p_data - > r_resolved_path ! = nullptr ) {
* p_data - > r_resolved_path = path ;
2022-04-29 00:51:04 +02:00
}
2024-04-19 16:14:05 +02:00
if ( p_data ! = nullptr & & p_data - > generate_temp_files ) {
2024-08-04 19:00:56 +02:00
// Save the copied path so it can be deleted later.
temp_libraries [ p_library_handle ] = load_path ;
2024-01-11 13:33:35 +01:00
}
2017-03-08 02:50:13 +01:00
return OK ;
}
2017-04-03 16:11:38 +02:00
Error OS_Windows : : close_dynamic_library ( void * p_library_handle ) {
if ( ! FreeLibrary ( ( HMODULE ) p_library_handle ) ) {
2017-03-08 02:50:13 +01:00
return FAILED ;
}
2024-01-11 13:33:35 +01:00
// Delete temporary copy of library if it exists.
_remove_temp_library ( p_library_handle ) ;
2017-03-08 02:50:13 +01:00
return OK ;
}
2024-01-11 13:33:35 +01:00
void OS_Windows : : _remove_temp_library ( void * p_library_handle ) {
if ( temp_libraries . has ( p_library_handle ) ) {
String path = temp_libraries [ p_library_handle ] ;
DirAccess : : remove_absolute ( path ) ;
WindowsUtils : : remove_temp_pdbs ( path ) ;
temp_libraries . erase ( p_library_handle ) ;
}
}
2024-01-09 02:36:19 +01:00
Error OS_Windows : : get_dynamic_library_symbol_handle ( void * p_library_handle , const String & p_name , void * & p_symbol_handle , bool p_optional ) {
2017-04-03 16:11:38 +02:00
p_symbol_handle = ( void * ) GetProcAddress ( ( HMODULE ) p_library_handle , p_name . utf8 ( ) . get_data ( ) ) ;
2017-03-08 02:50:13 +01:00
if ( ! p_symbol_handle ) {
2017-07-27 09:23:21 +02:00
if ( ! p_optional ) {
2023-03-27 10:49:05 +02:00
ERR_FAIL_V_MSG ( ERR_CANT_RESOLVE , vformat ( " Can't resolve symbol %s, error: \" %s \" . " , p_name , format_error_message ( GetLastError ( ) ) ) ) ;
2017-07-27 09:23:21 +02:00
} else {
return ERR_CANT_RESOLVE ;
}
2017-03-08 02:50:13 +01:00
}
return OK ;
}
2019-05-20 19:36:24 +02:00
String OS_Windows : : get_name ( ) const {
2014-02-10 02:10:30 +01:00
return " Windows " ;
}
2022-09-16 11:14:14 +02:00
String OS_Windows : : get_distribution_name ( ) const {
return get_name ( ) ;
}
String OS_Windows : : get_version ( ) const {
RtlGetVersionPtr version_ptr = ( RtlGetVersionPtr ) GetProcAddress ( GetModuleHandle ( " ntdll.dll " ) , " RtlGetVersion " ) ;
if ( version_ptr ! = nullptr ) {
2022-10-06 08:30:25 +02:00
RTL_OSVERSIONINFOW fow ;
ZeroMemory ( & fow , sizeof ( fow ) ) ;
2022-09-16 11:14:14 +02:00
fow . dwOSVersionInfoSize = sizeof ( fow ) ;
if ( version_ptr ( & fow ) = = 0x00000000 ) {
return vformat ( " %d.%d.%d " , ( int64_t ) fow . dwMajorVersion , ( int64_t ) fow . dwMinorVersion , ( int64_t ) fow . dwBuildNumber ) ;
}
}
return " " ;
}
2022-10-11 12:39:41 +02:00
Vector < String > OS_Windows : : get_video_adapter_driver_info ( ) const {
2023-06-06 23:29:36 +02:00
if ( RenderingServer : : get_singleton ( ) = = nullptr ) {
2022-10-27 22:52:38 +02:00
return Vector < String > ( ) ;
}
2023-05-27 18:21:23 +02:00
static Vector < String > info ;
if ( ! info . is_empty ( ) ) {
return info ;
}
2022-10-11 12:39:41 +02:00
REFCLSID clsid = CLSID_WbemLocator ; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator ; // Interface UUID
2024-03-12 15:40:40 +01:00
IWbemLocator * wbemLocator = nullptr ; // to get the services
IWbemServices * wbemServices = nullptr ; // to get the class
IEnumWbemClassObject * iter = nullptr ;
2022-10-11 12:39:41 +02:00
IWbemClassObject * pnpSDriverObject [ 1 ] ; // contains driver name, version, etc.
2023-05-27 18:21:23 +02:00
String driver_name ;
String driver_version ;
2022-10-11 12:39:41 +02:00
2023-06-06 23:29:36 +02:00
const String device_name = RenderingServer : : get_singleton ( ) - > get_video_adapter_name ( ) ;
2022-10-11 12:39:41 +02:00
if ( device_name . is_empty ( ) ) {
return Vector < String > ( ) ;
}
2024-03-12 15:40:40 +01:00
HRESULT hr = CoCreateInstance ( clsid , nullptr , CLSCTX_INPROC_SERVER , uuid , ( LPVOID * ) & wbemLocator ) ;
2022-10-11 12:39:41 +02:00
if ( hr ! = S_OK ) {
return Vector < String > ( ) ;
}
2022-10-12 19:17:49 +02:00
BSTR resource_name = SysAllocString ( L " root \\ CIMV2 " ) ;
2024-03-12 15:40:40 +01:00
hr = wbemLocator - > ConnectServer ( resource_name , nullptr , nullptr , nullptr , 0 , nullptr , nullptr , & wbemServices ) ;
2022-10-12 19:17:49 +02:00
SysFreeString ( resource_name ) ;
2022-10-11 12:39:41 +02:00
SAFE_RELEASE ( wbemLocator ) // from now on, use `wbemServices`
if ( hr ! = S_OK ) {
SAFE_RELEASE ( wbemServices )
return Vector < String > ( ) ;
}
const String gpu_device_class_query = vformat ( " SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \" %s \" " , device_name ) ;
BSTR query = SysAllocString ( ( const WCHAR * ) gpu_device_class_query . utf16 ( ) . get_data ( ) ) ;
BSTR query_lang = SysAllocString ( L " WQL " ) ;
2024-03-12 15:40:40 +01:00
hr = wbemServices - > ExecQuery ( query_lang , query , WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY , nullptr , & iter ) ;
2022-10-11 12:39:41 +02:00
SysFreeString ( query_lang ) ;
SysFreeString ( query ) ;
if ( hr = = S_OK ) {
ULONG resultCount ;
hr = iter - > Next ( 5000 , 1 , pnpSDriverObject , & resultCount ) ; // Get exactly 1. Wait max 5 seconds.
if ( hr = = S_OK & & resultCount > 0 ) {
VARIANT dn ;
VariantInit ( & dn ) ;
BSTR object_name = SysAllocString ( L " DriverName " ) ;
2024-03-12 15:40:40 +01:00
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , nullptr , nullptr ) ;
2022-10-11 12:39:41 +02:00
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
String d_name = String ( V_BSTR ( & dn ) ) ;
if ( d_name . is_empty ( ) ) {
object_name = SysAllocString ( L " DriverProviderName " ) ;
2024-03-12 15:40:40 +01:00
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , nullptr , nullptr ) ;
2022-10-11 12:39:41 +02:00
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
driver_name = String ( V_BSTR ( & dn ) ) ;
}
} else {
driver_name = d_name ;
}
} else {
object_name = SysAllocString ( L " DriverProviderName " ) ;
2024-03-12 15:40:40 +01:00
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , nullptr , nullptr ) ;
2022-10-11 12:39:41 +02:00
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
driver_name = String ( V_BSTR ( & dn ) ) ;
}
}
VARIANT dv ;
VariantInit ( & dv ) ;
object_name = SysAllocString ( L " DriverVersion " ) ;
2024-03-12 15:40:40 +01:00
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dv , nullptr , nullptr ) ;
2022-10-11 12:39:41 +02:00
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
driver_version = String ( V_BSTR ( & dv ) ) ;
}
for ( ULONG i = 0 ; i < resultCount ; i + + ) {
SAFE_RELEASE ( pnpSDriverObject [ i ] )
}
}
}
SAFE_RELEASE ( wbemServices )
SAFE_RELEASE ( iter )
info . push_back ( driver_name ) ;
info . push_back ( driver_version ) ;
return info ;
}
2022-09-08 07:36:10 +02:00
OS : : DateTime OS_Windows : : get_datetime ( bool p_utc ) const {
2014-02-10 02:10:30 +01:00
SYSTEMTIME systemtime ;
2021-10-28 17:16:47 +02:00
if ( p_utc ) {
2015-06-06 03:40:56 +02:00
GetSystemTime ( & systemtime ) ;
2021-10-28 17:16:47 +02:00
} else {
2015-06-06 03:40:56 +02:00
GetLocalTime ( & systemtime ) ;
2021-10-28 17:16:47 +02:00
}
2015-06-07 15:06:13 +02:00
2022-03-17 11:28:08 +01:00
//Get DST information from Windows, but only if p_utc is false.
TIME_ZONE_INFORMATION info ;
2023-05-11 12:32:23 +02:00
bool is_daylight = false ;
2022-03-17 11:28:08 +01:00
if ( ! p_utc & & GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
2023-05-11 12:32:23 +02:00
is_daylight = true ;
2022-03-17 11:28:08 +01:00
}
2022-09-08 07:36:10 +02:00
DateTime dt ;
dt . year = systemtime . wYear ;
dt . month = Month ( systemtime . wMonth ) ;
dt . day = systemtime . wDay ;
dt . weekday = Weekday ( systemtime . wDayOfWeek ) ;
dt . hour = systemtime . wHour ;
dt . minute = systemtime . wMinute ;
dt . second = systemtime . wSecond ;
2023-05-11 12:32:23 +02:00
dt . dst = is_daylight ;
2022-09-08 07:36:10 +02:00
return dt ;
2014-02-10 02:10:30 +01:00
}
2015-06-06 05:35:38 +02:00
OS : : TimeZoneInfo OS_Windows : : get_time_zone_info ( ) const {
TIME_ZONE_INFORMATION info ;
2023-05-11 12:32:23 +02:00
bool is_daylight = false ;
2022-02-16 13:56:32 +01:00
if ( GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
2023-05-11 12:32:23 +02:00
is_daylight = true ;
2022-02-16 13:56:32 +01:00
}
2015-06-06 05:35:38 +02:00
2022-03-17 11:28:08 +01:00
// Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update.
2015-06-07 16:10:33 +02:00
TimeZoneInfo ret ;
2023-05-11 12:32:23 +02:00
if ( is_daylight ) {
2015-06-06 05:35:38 +02:00
ret . name = info . DaylightName ;
2022-03-17 11:28:08 +01:00
ret . bias = info . Bias + info . DaylightBias ;
2015-06-06 05:35:38 +02:00
} else {
ret . name = info . StandardName ;
2022-03-17 11:28:08 +01:00
ret . bias = info . Bias + info . StandardBias ;
2015-06-06 05:35:38 +02:00
}
2019-02-13 01:41:19 +01:00
// Bias value returned by GetTimeZoneInformation is inverted of what we expect
2021-03-12 14:35:16 +01:00
// For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
2022-03-17 11:28:08 +01:00
ret . bias = - ret . bias ;
2015-06-06 05:35:38 +02:00
return ret ;
}
2014-02-10 02:10:30 +01:00
2020-05-31 14:19:31 +02:00
double OS_Windows : : get_unix_time ( ) const {
// 1 Windows tick is 100ns
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000 ;
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL ;
2016-06-11 19:09:21 +02:00
2015-08-06 19:29:33 +02:00
SYSTEMTIME st ;
GetSystemTime ( & st ) ;
2016-01-10 22:24:55 +01:00
FILETIME ft ;
SystemTimeToFileTime ( & st , & ft ) ;
2020-05-31 14:19:31 +02:00
uint64_t ticks_time ;
ticks_time = ft . dwHighDateTime ;
ticks_time < < = 32 ;
ticks_time | = ft . dwLowDateTime ;
2016-06-11 19:09:21 +02:00
2020-05-31 14:19:31 +02:00
return ( double ) ( ticks_time - TICKS_TO_UNIX_EPOCH ) / WINDOWS_TICKS_PER_SECOND ;
2015-08-06 19:29:33 +02:00
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : delay_usec ( uint32_t p_usec ) const {
2022-02-16 13:56:32 +01:00
if ( p_usec < 1000 ) {
2014-02-10 02:10:30 +01:00
Sleep ( 1 ) ;
2022-02-16 13:56:32 +01:00
} else {
2014-02-10 02:10:30 +01:00
Sleep ( p_usec / 1000 ) ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
uint64_t OS_Windows : : get_ticks_usec ( ) const {
uint64_t ticks ;
2020-05-22 13:20:19 +02:00
2014-02-10 02:10:30 +01:00
// This is the number of clock ticks since start
2022-01-04 22:38:44 +01:00
QueryPerformanceCounter ( ( LARGE_INTEGER * ) & ticks ) ;
// Subtract the ticks at game start to get
// the ticks since the game started
ticks - = ticks_start ;
2020-05-22 13:20:19 +02:00
2014-02-10 02:10:30 +01:00
// Divide by frequency to get the time in seconds
2020-05-22 13:20:19 +02:00
// original calculation shown below is subject to overflow
// with high ticks_per_second and a number of days since the last reboot.
// time = ticks * 1000000L / ticks_per_second;
// we can prevent this by either using 128 bit math
// or separating into a calculation for seconds, and the fraction
uint64_t seconds = ticks / ticks_per_second ;
// compiler will optimize these two into one divide
uint64_t leftover = ticks % ticks_per_second ;
// remainder
uint64_t time = ( leftover * 1000000L ) / ticks_per_second ;
// seconds
time + = seconds * 1000000L ;
2014-02-10 02:10:30 +01:00
return time ;
}
2020-05-19 15:34:15 +02:00
String OS_Windows : : _quote_command_line_argument ( const String & p_text ) const {
for ( int i = 0 ; i < p_text . size ( ) ; i + + ) {
2020-07-27 12:43:20 +02:00
char32_t c = p_text [ i ] ;
2020-05-19 15:34:15 +02:00
if ( c = = ' ' | | c = = ' & ' | | c = = ' ( ' | | c = = ' ) ' | | c = = ' [ ' | | c = = ' ] ' | | c = = ' { ' | | c = = ' } ' | | c = = ' ^ ' | | c = = ' = ' | | c = = ' ; ' | | c = = ' ! ' | | c = = ' \' ' | | c = = ' + ' | | c = = ' , ' | | c = = ' ` ' | | c = = ' ~ ' ) {
return " \" " + p_text + " \" " ;
}
}
return p_text ;
}
2022-05-10 12:27:45 +02:00
static void _append_to_pipe ( char * p_bytes , int p_size , String * r_pipe , Mutex * p_pipe_mutex ) {
// Try to convert from default ANSI code page to Unicode.
LocalVector < wchar_t > wchars ;
int total_wchars = MultiByteToWideChar ( CP_ACP , 0 , p_bytes , p_size , nullptr , 0 ) ;
if ( total_wchars > 0 ) {
wchars . resize ( total_wchars ) ;
if ( MultiByteToWideChar ( CP_ACP , 0 , p_bytes , p_size , wchars . ptr ( ) , total_wchars ) = = 0 ) {
wchars . clear ( ) ;
}
}
if ( p_pipe_mutex ) {
p_pipe_mutex - > lock ( ) ;
}
if ( wchars . is_empty ( ) ) {
// Let's hope it's compatible with UTF-8.
( * r_pipe ) + = String : : utf8 ( p_bytes , p_size ) ;
} else {
( * r_pipe ) + = String ( wchars . ptr ( ) , total_wchars ) ;
}
if ( p_pipe_mutex ) {
p_pipe_mutex - > unlock ( ) ;
}
}
2023-04-03 10:46:29 +02:00
Dictionary OS_Windows : : get_memory_info ( ) const {
Dictionary meminfo ;
meminfo [ " physical " ] = - 1 ;
meminfo [ " free " ] = - 1 ;
meminfo [ " available " ] = - 1 ;
meminfo [ " stack " ] = - 1 ;
PERFORMANCE_INFORMATION pref_info ;
pref_info . cb = sizeof ( pref_info ) ;
GetPerformanceInfo ( & pref_info , sizeof ( pref_info ) ) ;
typedef void ( WINAPI * PGetCurrentThreadStackLimits ) ( PULONG_PTR , PULONG_PTR ) ;
PGetCurrentThreadStackLimits GetCurrentThreadStackLimits = ( PGetCurrentThreadStackLimits ) GetProcAddress ( GetModuleHandleA ( " kernel32.dll " ) , " GetCurrentThreadStackLimits " ) ;
ULONG_PTR LowLimit = 0 ;
ULONG_PTR HighLimit = 0 ;
if ( GetCurrentThreadStackLimits ) {
GetCurrentThreadStackLimits ( & LowLimit , & HighLimit ) ;
}
if ( pref_info . PhysicalTotal * pref_info . PageSize ! = 0 ) {
2023-05-07 16:01:20 +02:00
meminfo [ " physical " ] = static_cast < int64_t > ( pref_info . PhysicalTotal * pref_info . PageSize ) ;
2023-04-03 10:46:29 +02:00
}
if ( pref_info . PhysicalAvailable * pref_info . PageSize ! = 0 ) {
2023-05-07 16:01:20 +02:00
meminfo [ " free " ] = static_cast < int64_t > ( pref_info . PhysicalAvailable * pref_info . PageSize ) ;
2023-04-03 10:46:29 +02:00
}
if ( pref_info . CommitLimit * pref_info . PageSize ! = 0 ) {
2023-05-07 16:01:20 +02:00
meminfo [ " available " ] = static_cast < int64_t > ( pref_info . CommitLimit * pref_info . PageSize ) ;
2023-04-03 10:46:29 +02:00
}
if ( HighLimit - LowLimit ! = 0 ) {
2023-05-07 16:01:20 +02:00
meminfo [ " stack " ] = static_cast < int64_t > ( HighLimit - LowLimit ) ;
2023-04-03 10:46:29 +02:00
}
return meminfo ;
}
2023-10-19 13:35:10 +02:00
Dictionary OS_Windows : : execute_with_pipe ( const String & p_path , const List < String > & p_arguments ) {
# define CLEAN_PIPES \
if ( pipe_in [ 0 ] ! = 0 ) { \
CloseHandle ( pipe_in [ 0 ] ) ; \
} \
if ( pipe_in [ 1 ] ! = 0 ) { \
CloseHandle ( pipe_in [ 1 ] ) ; \
} \
if ( pipe_out [ 0 ] ! = 0 ) { \
CloseHandle ( pipe_out [ 0 ] ) ; \
} \
if ( pipe_out [ 1 ] ! = 0 ) { \
CloseHandle ( pipe_out [ 1 ] ) ; \
} \
if ( pipe_err [ 0 ] ! = 0 ) { \
CloseHandle ( pipe_err [ 0 ] ) ; \
} \
if ( pipe_err [ 1 ] ! = 0 ) { \
CloseHandle ( pipe_err [ 1 ] ) ; \
}
Dictionary ret ;
String path = p_path . replace ( " / " , " \\ " ) ;
String command = _quote_command_line_argument ( path ) ;
for ( const String & E : p_arguments ) {
command + = " " + _quote_command_line_argument ( E ) ;
}
// Create pipes.
HANDLE pipe_in [ 2 ] = { 0 , 0 } ;
HANDLE pipe_out [ 2 ] = { 0 , 0 } ;
HANDLE pipe_err [ 2 ] = { 0 , 0 } ;
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( SECURITY_ATTRIBUTES ) ;
sa . bInheritHandle = true ;
sa . lpSecurityDescriptor = nullptr ;
ERR_FAIL_COND_V ( ! CreatePipe ( & pipe_in [ 0 ] , & pipe_in [ 1 ] , & sa , 0 ) , ret ) ;
if ( ! SetHandleInformation ( pipe_in [ 1 ] , HANDLE_FLAG_INHERIT , 0 ) ) {
CLEAN_PIPES
ERR_FAIL_V ( ret ) ;
}
if ( ! CreatePipe ( & pipe_out [ 0 ] , & pipe_out [ 1 ] , & sa , 0 ) ) {
CLEAN_PIPES
ERR_FAIL_V ( ret ) ;
}
if ( ! SetHandleInformation ( pipe_out [ 0 ] , HANDLE_FLAG_INHERIT , 0 ) ) {
CLEAN_PIPES
ERR_FAIL_V ( ret ) ;
}
if ( ! CreatePipe ( & pipe_err [ 0 ] , & pipe_err [ 1 ] , & sa , 0 ) ) {
CLEAN_PIPES
ERR_FAIL_V ( ret ) ;
}
ERR_FAIL_COND_V ( ! SetHandleInformation ( pipe_err [ 0 ] , HANDLE_FLAG_INHERIT , 0 ) , ret ) ;
// Create process.
ProcessInfo pi ;
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
pi . si . cb = sizeof ( pi . si ) ;
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
LPSTARTUPINFOW si_w = ( LPSTARTUPINFOW ) & pi . si ;
pi . si . dwFlags | = STARTF_USESTDHANDLES ;
pi . si . hStdInput = pipe_in [ 0 ] ;
pi . si . hStdOutput = pipe_out [ 1 ] ;
pi . si . hStdError = pipe_err [ 1 ] ;
DWORD creation_flags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW ;
if ( ! CreateProcessW ( nullptr , ( LPWSTR ) ( command . utf16 ( ) . ptrw ( ) ) , nullptr , nullptr , true , creation_flags , nullptr , nullptr , si_w , & pi . pi ) ) {
CLEAN_PIPES
ERR_FAIL_V_MSG ( ret , " Could not create child process: " + command ) ;
}
CloseHandle ( pipe_in [ 0 ] ) ;
CloseHandle ( pipe_out [ 1 ] ) ;
CloseHandle ( pipe_err [ 1 ] ) ;
ProcessID pid = pi . pi . dwProcessId ;
2024-04-07 20:19:46 +02:00
process_map_mutex . lock ( ) ;
2023-10-19 13:35:10 +02:00
process_map - > insert ( pid , pi ) ;
2024-04-07 20:19:46 +02:00
process_map_mutex . unlock ( ) ;
2023-10-19 13:35:10 +02:00
Ref < FileAccessWindowsPipe > main_pipe ;
main_pipe . instantiate ( ) ;
main_pipe - > open_existing ( pipe_out [ 0 ] , pipe_in [ 1 ] ) ;
Ref < FileAccessWindowsPipe > err_pipe ;
err_pipe . instantiate ( ) ;
err_pipe - > open_existing ( pipe_err [ 0 ] , 0 ) ;
ret [ " stdio " ] = main_pipe ;
ret [ " stderr " ] = err_pipe ;
ret [ " pid " ] = pid ;
# undef CLEAN_PIPES
return ret ;
}
2021-12-16 14:00:55 +01:00
Error OS_Windows : : execute ( const String & p_path , const List < String > & p_arguments , String * r_pipe , int * r_exitcode , bool read_stderr , Mutex * p_pipe_mutex , bool p_open_console ) {
2020-10-22 12:17:21 +02:00
String path = p_path . replace ( " / " , " \\ " ) ;
2020-12-18 19:49:13 +01:00
String command = _quote_command_line_argument ( path ) ;
2021-07-16 05:45:57 +02:00
for ( const String & E : p_arguments ) {
command + = " " + _quote_command_line_argument ( E ) ;
2020-12-18 19:49:13 +01:00
}
2020-10-22 12:17:21 +02:00
2021-12-16 14:00:55 +01:00
ProcessInfo pi ;
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
pi . si . cb = sizeof ( pi . si ) ;
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
LPSTARTUPINFOW si_w = ( LPSTARTUPINFOW ) & pi . si ;
bool inherit_handles = false ;
HANDLE pipe [ 2 ] = { nullptr , nullptr } ;
2020-12-18 19:49:13 +01:00
if ( r_pipe ) {
2021-12-16 14:00:55 +01:00
// Create pipe for StdOut and StdErr.
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( SECURITY_ATTRIBUTES ) ;
sa . bInheritHandle = true ;
sa . lpSecurityDescriptor = nullptr ;
ERR_FAIL_COND_V ( ! CreatePipe ( & pipe [ 0 ] , & pipe [ 1 ] , & sa , 0 ) , ERR_CANT_FORK ) ;
ERR_FAIL_COND_V ( ! SetHandleInformation ( pipe [ 0 ] , HANDLE_FLAG_INHERIT , 0 ) , ERR_CANT_FORK ) ; // Read handle is for host process only and should not be inherited.
pi . si . dwFlags | = STARTF_USESTDHANDLES ;
pi . si . hStdOutput = pipe [ 1 ] ;
2019-03-01 23:51:53 +01:00
if ( read_stderr ) {
2021-12-16 14:00:55 +01:00
pi . si . hStdError = pipe [ 1 ] ;
2014-02-10 02:10:30 +01:00
}
2021-12-16 14:00:55 +01:00
inherit_handles = true ;
}
2022-01-27 03:11:00 +01:00
DWORD creation_flags = NORMAL_PRIORITY_CLASS ;
2021-12-16 14:00:55 +01:00
if ( p_open_console ) {
2022-01-27 03:11:00 +01:00
creation_flags | = CREATE_NEW_CONSOLE ;
2021-12-16 14:00:55 +01:00
} else {
2022-01-27 03:11:00 +01:00
creation_flags | = CREATE_NO_WINDOW ;
2021-12-16 14:00:55 +01:00
}
2022-01-27 03:11:00 +01:00
int ret = CreateProcessW ( nullptr , ( LPWSTR ) ( command . utf16 ( ) . ptrw ( ) ) , nullptr , nullptr , inherit_handles , creation_flags , nullptr , nullptr , si_w , & pi . pi ) ;
2021-12-16 14:00:55 +01:00
if ( ! ret & & r_pipe ) {
CloseHandle ( pipe [ 0 ] ) ; // Cleanup pipe handles.
CloseHandle ( pipe [ 1 ] ) ;
}
ERR_FAIL_COND_V_MSG ( ret = = 0 , ERR_CANT_FORK , " Could not create child process: " + command ) ;
if ( r_pipe ) {
CloseHandle ( pipe [ 1 ] ) ; // Close pipe write handle (only child process is writing).
2022-05-10 12:27:45 +02:00
LocalVector < char > bytes ;
int bytes_in_buffer = 0 ;
const int CHUNK_SIZE = 4096 ;
2021-12-16 14:00:55 +01:00
DWORD read = 0 ;
for ( ; ; ) { // Read StdOut and StdErr from pipe.
2022-05-10 12:27:45 +02:00
bytes . resize ( bytes_in_buffer + CHUNK_SIZE ) ;
2024-03-12 15:40:40 +01:00
const bool success = ReadFile ( pipe [ 0 ] , bytes . ptr ( ) + bytes_in_buffer , CHUNK_SIZE , & read , nullptr ) ;
2021-12-16 14:00:55 +01:00
if ( ! success | | read = = 0 ) {
break ;
}
2022-05-10 12:27:45 +02:00
// Assume that all possible encodings are ASCII-compatible.
// Break at newline to allow receiving long output in portions.
int newline_index = - 1 ;
for ( int i = read - 1 ; i > = 0 ; i - - ) {
if ( bytes [ bytes_in_buffer + i ] = = ' \n ' ) {
newline_index = i ;
break ;
}
2019-04-07 20:46:52 +02:00
}
2022-05-10 12:27:45 +02:00
if ( newline_index = = - 1 ) {
bytes_in_buffer + = read ;
continue ;
2019-04-07 20:46:52 +02:00
}
2022-05-10 12:27:45 +02:00
const int bytes_to_convert = bytes_in_buffer + ( newline_index + 1 ) ;
_append_to_pipe ( bytes . ptr ( ) , bytes_to_convert , r_pipe , p_pipe_mutex ) ;
bytes_in_buffer = read - ( newline_index + 1 ) ;
memmove ( bytes . ptr ( ) , bytes . ptr ( ) + bytes_to_convert , bytes_in_buffer ) ;
}
if ( bytes_in_buffer > 0 ) {
_append_to_pipe ( bytes . ptr ( ) , bytes_in_buffer , r_pipe , p_pipe_mutex ) ;
2022-02-16 13:56:32 +01:00
}
2022-05-10 12:27:45 +02:00
2021-12-16 14:00:55 +01:00
CloseHandle ( pipe [ 0 ] ) ; // Close pipe read handle.
2014-02-10 02:10:30 +01:00
}
2023-02-27 08:54:49 +01:00
WaitForSingleObject ( pi . pi . hProcess , INFINITE ) ;
2014-02-10 02:10:30 +01:00
2020-12-18 19:49:13 +01:00
if ( r_exitcode ) {
DWORD ret2 ;
GetExitCodeProcess ( pi . pi . hProcess , & ret2 ) ;
* r_exitcode = ret2 ;
}
2021-12-16 14:00:55 +01:00
2020-12-18 19:49:13 +01:00
CloseHandle ( pi . pi . hProcess ) ;
CloseHandle ( pi . pi . hThread ) ;
return OK ;
2022-02-16 13:56:32 +01:00
}
2020-12-18 19:49:13 +01:00
2021-12-16 14:00:55 +01:00
Error OS_Windows : : create_process ( const String & p_path , const List < String > & p_arguments , ProcessID * r_child_id , bool p_open_console ) {
2020-12-18 19:49:13 +01:00
String path = p_path . replace ( " / " , " \\ " ) ;
String command = _quote_command_line_argument ( path ) ;
2021-07-16 05:45:57 +02:00
for ( const String & E : p_arguments ) {
command + = " " + _quote_command_line_argument ( E ) ;
2020-05-19 15:34:15 +02:00
}
2014-02-10 02:10:30 +01:00
ProcessInfo pi ;
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
pi . si . cb = sizeof ( pi . si ) ;
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
2014-02-15 06:01:39 +01:00
LPSTARTUPINFOW si_w = ( LPSTARTUPINFOW ) & pi . si ;
2014-02-10 02:10:30 +01:00
2022-01-27 03:11:00 +01:00
DWORD creation_flags = NORMAL_PRIORITY_CLASS ;
2021-12-16 14:00:55 +01:00
if ( p_open_console ) {
2022-01-27 03:11:00 +01:00
creation_flags | = CREATE_NEW_CONSOLE ;
2021-12-16 14:00:55 +01:00
} else {
2022-01-27 03:11:00 +01:00
creation_flags | = CREATE_NO_WINDOW ;
2021-12-14 13:43:13 +01:00
}
2021-11-25 19:54:23 +01:00
2022-01-27 03:11:00 +01:00
int ret = CreateProcessW ( nullptr , ( LPWSTR ) ( command . utf16 ( ) . ptrw ( ) ) , nullptr , nullptr , false , creation_flags , nullptr , nullptr , si_w , & pi . pi ) ;
2020-12-18 19:49:13 +01:00
ERR_FAIL_COND_V_MSG ( ret = = 0 , ERR_CANT_FORK , " Could not create child process: " + command ) ;
2014-02-10 02:10:30 +01:00
2020-12-18 19:49:13 +01:00
ProcessID pid = pi . pi . dwProcessId ;
if ( r_child_id ) {
* r_child_id = pid ;
2020-05-19 15:34:15 +02:00
}
2024-04-07 20:19:46 +02:00
process_map_mutex . lock ( ) ;
2020-12-18 19:49:13 +01:00
process_map - > insert ( pid , pi ) ;
2024-04-07 20:19:46 +02:00
process_map_mutex . unlock ( ) ;
2020-12-18 19:49:13 +01:00
2014-02-10 02:10:30 +01:00
return OK ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
2018-08-27 17:32:43 +02:00
Error OS_Windows : : kill ( const ProcessID & p_pid ) {
2023-01-12 13:41:57 +01:00
int ret = 0 ;
2024-04-07 20:19:46 +02:00
MutexLock lock ( process_map_mutex ) ;
2023-01-12 13:41:57 +01:00
if ( process_map - > has ( p_pid ) ) {
const PROCESS_INFORMATION pi = ( * process_map ) [ p_pid ] . pi ;
process_map - > erase ( p_pid ) ;
2014-02-10 02:10:30 +01:00
2023-01-12 13:41:57 +01:00
ret = TerminateProcess ( pi . hProcess , 0 ) ;
2014-02-10 02:10:30 +01:00
2023-01-12 13:41:57 +01:00
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
} else {
HANDLE hProcess = OpenProcess ( PROCESS_TERMINATE , false , ( DWORD ) p_pid ) ;
2024-03-12 15:40:40 +01:00
if ( hProcess ! = nullptr ) {
2023-01-12 13:41:57 +01:00
ret = TerminateProcess ( hProcess , 0 ) ;
2014-02-10 02:10:30 +01:00
2023-01-12 13:41:57 +01:00
CloseHandle ( hProcess ) ;
}
}
2014-02-10 02:10:30 +01:00
2018-08-27 17:32:43 +02:00
return ret ! = 0 ? OK : FAILED ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
2017-08-07 12:17:31 +02:00
int OS_Windows : : get_process_id ( ) const {
2016-04-29 18:57:57 +02:00
return _getpid ( ) ;
}
2021-08-13 05:36:23 +02:00
bool OS_Windows : : is_process_running ( const ProcessID & p_pid ) const {
2024-04-07 20:19:46 +02:00
MutexLock lock ( process_map_mutex ) ;
2021-08-13 05:36:23 +02:00
if ( ! process_map - > has ( p_pid ) ) {
return false ;
}
2024-04-07 20:19:46 +02:00
const ProcessInfo & info = ( * process_map ) [ p_pid ] ;
if ( ! info . is_running ) {
return false ;
}
2021-08-13 05:36:23 +02:00
2024-04-07 20:19:46 +02:00
const PROCESS_INFORMATION & pi = info . pi ;
2021-08-13 05:36:23 +02:00
DWORD dw_exit_code = 0 ;
if ( ! GetExitCodeProcess ( pi . hProcess , & dw_exit_code ) ) {
return false ;
}
if ( dw_exit_code ! = STILL_ACTIVE ) {
2024-04-07 20:19:46 +02:00
info . is_running = false ;
info . exit_code = dw_exit_code ;
2021-08-13 05:36:23 +02:00
return false ;
}
return true ;
}
2024-04-07 20:19:46 +02:00
int OS_Windows : : get_process_exit_code ( const ProcessID & p_pid ) const {
MutexLock lock ( process_map_mutex ) ;
if ( ! process_map - > has ( p_pid ) ) {
return - 1 ;
}
const ProcessInfo & info = ( * process_map ) [ p_pid ] ;
if ( ! info . is_running ) {
return info . exit_code ;
}
const PROCESS_INFORMATION & pi = info . pi ;
DWORD dw_exit_code = 0 ;
if ( ! GetExitCodeProcess ( pi . hProcess , & dw_exit_code ) ) {
return - 1 ;
}
if ( dw_exit_code = = STILL_ACTIVE ) {
return - 1 ;
}
info . is_running = false ;
info . exit_code = dw_exit_code ;
return dw_exit_code ;
}
2014-02-10 02:10:30 +01:00
Error OS_Windows : : set_cwd ( const String & p_cwd ) {
2022-02-16 13:56:32 +01:00
if ( _wchdir ( ( LPCWSTR ) ( p_cwd . utf16 ( ) . get_data ( ) ) ) ! = 0 ) {
2014-02-10 02:10:30 +01:00
return ERR_CANT_OPEN ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
return OK ;
}
2022-07-08 14:38:30 +02:00
Vector < String > OS_Windows : : get_system_fonts ( ) const {
2022-11-21 14:04:01 +01:00
if ( ! dwrite_init ) {
return Vector < String > ( ) ;
}
2022-07-08 14:38:30 +02:00
Vector < String > ret ;
HashSet < String > font_names ;
UINT32 family_count = font_collection - > GetFontFamilyCount ( ) ;
for ( UINT32 i = 0 ; i < family_count ; i + + ) {
ComAutoreleaseRef < IDWriteFontFamily > family ;
2022-11-21 14:04:01 +01:00
HRESULT hr = font_collection - > GetFontFamily ( i , & family . reference ) ;
2022-07-08 14:38:30 +02:00
ERR_CONTINUE ( FAILED ( hr ) | | family . is_null ( ) ) ;
ComAutoreleaseRef < IDWriteLocalizedStrings > family_names ;
hr = family - > GetFamilyNames ( & family_names . reference ) ;
ERR_CONTINUE ( FAILED ( hr ) | | family_names . is_null ( ) ) ;
UINT32 index = 0 ;
BOOL exists = false ;
UINT32 length = 0 ;
Char16String name ;
hr = family_names - > FindLocaleName ( L " en-us " , & index , & exists ) ;
ERR_CONTINUE ( FAILED ( hr ) ) ;
hr = family_names - > GetStringLength ( index , & length ) ;
ERR_CONTINUE ( FAILED ( hr ) ) ;
name . resize ( length + 1 ) ;
hr = family_names - > GetString ( index , ( WCHAR * ) name . ptrw ( ) , length + 1 ) ;
ERR_CONTINUE ( FAILED ( hr ) ) ;
font_names . insert ( String : : utf16 ( name . ptr ( ) , length ) ) ;
}
for ( const String & E : font_names ) {
ret . push_back ( E ) ;
}
return ret ;
}
2022-11-21 14:04:01 +01:00
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
# endif
class FallbackTextAnalysisSource : public IDWriteTextAnalysisSource {
LONG _cRef = 1 ;
bool rtl = false ;
Char16String string ;
Char16String locale ;
IDWriteNumberSubstitution * n_sub = nullptr ;
public :
2023-01-31 01:59:38 +01:00
HRESULT STDMETHODCALLTYPE QueryInterface ( REFIID riid , VOID * * ppvInterface ) override {
2022-11-21 14:04:01 +01:00
if ( IID_IUnknown = = riid ) {
AddRef ( ) ;
* ppvInterface = ( IUnknown * ) this ;
} else if ( __uuidof ( IMMNotificationClient ) = = riid ) {
AddRef ( ) ;
* ppvInterface = ( IMMNotificationClient * ) this ;
} else {
* ppvInterface = nullptr ;
return E_NOINTERFACE ;
}
return S_OK ;
}
2023-01-31 01:59:38 +01:00
ULONG STDMETHODCALLTYPE AddRef ( ) override {
2022-11-21 14:04:01 +01:00
return InterlockedIncrement ( & _cRef ) ;
}
2023-01-31 01:59:38 +01:00
ULONG STDMETHODCALLTYPE Release ( ) override {
2022-11-21 14:04:01 +01:00
ULONG ulRef = InterlockedDecrement ( & _cRef ) ;
if ( 0 = = ulRef ) {
delete this ;
}
return ulRef ;
}
HRESULT STDMETHODCALLTYPE GetTextAtPosition ( UINT32 p_text_position , WCHAR const * * r_text_string , UINT32 * r_text_length ) override {
if ( p_text_position > = ( UINT32 ) string . length ( ) ) {
* r_text_string = nullptr ;
* r_text_length = 0 ;
return S_OK ;
}
* r_text_string = reinterpret_cast < const wchar_t * > ( string . get_data ( ) ) + p_text_position ;
* r_text_length = string . length ( ) - p_text_position ;
return S_OK ;
}
HRESULT STDMETHODCALLTYPE GetTextBeforePosition ( UINT32 p_text_position , WCHAR const * * r_text_string , UINT32 * r_text_length ) override {
if ( p_text_position < 1 | | p_text_position > = ( UINT32 ) string . length ( ) ) {
* r_text_string = nullptr ;
* r_text_length = 0 ;
return S_OK ;
}
* r_text_string = reinterpret_cast < const wchar_t * > ( string . get_data ( ) ) ;
* r_text_length = p_text_position ;
return S_OK ;
}
DWRITE_READING_DIRECTION STDMETHODCALLTYPE GetParagraphReadingDirection ( ) override {
return ( rtl ) ? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT : DWRITE_READING_DIRECTION_LEFT_TO_RIGHT ;
}
HRESULT STDMETHODCALLTYPE GetLocaleName ( UINT32 p_text_position , UINT32 * r_text_length , WCHAR const * * r_locale_name ) override {
* r_locale_name = reinterpret_cast < const wchar_t * > ( locale . get_data ( ) ) ;
return S_OK ;
}
HRESULT STDMETHODCALLTYPE GetNumberSubstitution ( UINT32 p_text_position , UINT32 * r_text_length , IDWriteNumberSubstitution * * r_number_substitution ) override {
* r_number_substitution = n_sub ;
return S_OK ;
}
FallbackTextAnalysisSource ( const Char16String & p_text , const Char16String & p_locale , bool p_rtl , IDWriteNumberSubstitution * p_nsub ) {
_cRef = 1 ;
string = p_text ;
locale = p_locale ;
n_sub = p_nsub ;
rtl = p_rtl ;
} ;
virtual ~ FallbackTextAnalysisSource ( ) { }
} ;
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
String OS_Windows : : _get_default_fontname ( const String & p_font_name ) const {
2022-07-08 14:38:30 +02:00
String font_name = p_font_name ;
if ( font_name . to_lower ( ) = = " sans-serif " ) {
font_name = " Arial " ;
} else if ( font_name . to_lower ( ) = = " serif " ) {
font_name = " Times New Roman " ;
} else if ( font_name . to_lower ( ) = = " monospace " ) {
font_name = " Courier New " ;
} else if ( font_name . to_lower ( ) = = " cursive " ) {
font_name = " Comic Sans MS " ;
} else if ( font_name . to_lower ( ) = = " fantasy " ) {
font_name = " Gabriola " ;
}
2022-11-21 14:04:01 +01:00
return font_name ;
}
DWRITE_FONT_WEIGHT OS_Windows : : _weight_to_dw ( int p_weight ) const {
if ( p_weight < 150 ) {
return DWRITE_FONT_WEIGHT_THIN ;
} else if ( p_weight < 250 ) {
return DWRITE_FONT_WEIGHT_EXTRA_LIGHT ;
} else if ( p_weight < 325 ) {
return DWRITE_FONT_WEIGHT_LIGHT ;
} else if ( p_weight < 375 ) {
return DWRITE_FONT_WEIGHT_SEMI_LIGHT ;
} else if ( p_weight < 450 ) {
return DWRITE_FONT_WEIGHT_NORMAL ;
} else if ( p_weight < 550 ) {
return DWRITE_FONT_WEIGHT_MEDIUM ;
} else if ( p_weight < 650 ) {
return DWRITE_FONT_WEIGHT_DEMI_BOLD ;
} else if ( p_weight < 750 ) {
return DWRITE_FONT_WEIGHT_BOLD ;
} else if ( p_weight < 850 ) {
return DWRITE_FONT_WEIGHT_EXTRA_BOLD ;
} else if ( p_weight < 925 ) {
return DWRITE_FONT_WEIGHT_BLACK ;
} else {
return DWRITE_FONT_WEIGHT_EXTRA_BLACK ;
}
}
DWRITE_FONT_STRETCH OS_Windows : : _stretch_to_dw ( int p_stretch ) const {
if ( p_stretch < 56 ) {
return DWRITE_FONT_STRETCH_ULTRA_CONDENSED ;
} else if ( p_stretch < 69 ) {
return DWRITE_FONT_STRETCH_EXTRA_CONDENSED ;
} else if ( p_stretch < 81 ) {
return DWRITE_FONT_STRETCH_CONDENSED ;
} else if ( p_stretch < 93 ) {
return DWRITE_FONT_STRETCH_SEMI_CONDENSED ;
} else if ( p_stretch < 106 ) {
return DWRITE_FONT_STRETCH_NORMAL ;
} else if ( p_stretch < 137 ) {
return DWRITE_FONT_STRETCH_SEMI_EXPANDED ;
} else if ( p_stretch < 144 ) {
return DWRITE_FONT_STRETCH_EXPANDED ;
} else if ( p_stretch < 162 ) {
return DWRITE_FONT_STRETCH_EXTRA_EXPANDED ;
} else {
return DWRITE_FONT_STRETCH_ULTRA_EXPANDED ;
}
}
Vector < String > OS_Windows : : get_system_font_path_for_text ( const String & p_font_name , const String & p_text , const String & p_locale , const String & p_script , int p_weight , int p_stretch , bool p_italic ) const {
if ( ! dwrite2_init ) {
return Vector < String > ( ) ;
}
String font_name = _get_default_fontname ( p_font_name ) ;
bool rtl = TS - > is_locale_right_to_left ( p_locale ) ;
Char16String text = p_text . utf16 ( ) ;
Char16String locale = p_locale . utf16 ( ) ;
ComAutoreleaseRef < IDWriteNumberSubstitution > number_substitution ;
HRESULT hr = dwrite_factory - > CreateNumberSubstitution ( DWRITE_NUMBER_SUBSTITUTION_METHOD_NONE , reinterpret_cast < const wchar_t * > ( locale . get_data ( ) ) , true , & number_substitution . reference ) ;
ERR_FAIL_COND_V ( FAILED ( hr ) | | number_substitution . is_null ( ) , Vector < String > ( ) ) ;
FallbackTextAnalysisSource fs = FallbackTextAnalysisSource ( text , locale , rtl , number_substitution . reference ) ;
UINT32 mapped_length = 0 ;
FLOAT scale = 0.0 ;
ComAutoreleaseRef < IDWriteFont > dwrite_font ;
hr = system_font_fallback - > MapCharacters (
& fs ,
0 ,
( UINT32 ) text . length ( ) ,
font_collection ,
reinterpret_cast < const wchar_t * > ( font_name . utf16 ( ) . get_data ( ) ) ,
_weight_to_dw ( p_weight ) ,
p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL ,
_stretch_to_dw ( p_stretch ) ,
& mapped_length ,
& dwrite_font . reference ,
& scale ) ;
if ( FAILED ( hr ) | | dwrite_font . is_null ( ) ) {
return Vector < String > ( ) ;
}
ComAutoreleaseRef < IDWriteFontFace > dwrite_face ;
hr = dwrite_font - > CreateFontFace ( & dwrite_face . reference ) ;
if ( FAILED ( hr ) | | dwrite_face . is_null ( ) ) {
return Vector < String > ( ) ;
}
UINT32 number_of_files = 0 ;
hr = dwrite_face - > GetFiles ( & number_of_files , nullptr ) ;
if ( FAILED ( hr ) ) {
return Vector < String > ( ) ;
}
Vector < ComAutoreleaseRef < IDWriteFontFile > > files ;
files . resize ( number_of_files ) ;
hr = dwrite_face - > GetFiles ( & number_of_files , ( IDWriteFontFile * * ) files . ptrw ( ) ) ;
if ( FAILED ( hr ) ) {
return Vector < String > ( ) ;
}
Vector < String > ret ;
for ( UINT32 i = 0 ; i < number_of_files ; i + + ) {
void const * reference_key = nullptr ;
UINT32 reference_key_size = 0 ;
ComAutoreleaseRef < IDWriteLocalFontFileLoader > loader ;
hr = files . write [ i ] - > GetLoader ( ( IDWriteFontFileLoader * * ) & loader . reference ) ;
if ( FAILED ( hr ) | | loader . is_null ( ) ) {
continue ;
}
hr = files . write [ i ] - > GetReferenceKey ( & reference_key , & reference_key_size ) ;
if ( FAILED ( hr ) ) {
continue ;
}
WCHAR file_path [ MAX_PATH ] ;
hr = loader - > GetFilePathFromKey ( reference_key , reference_key_size , & file_path [ 0 ] , MAX_PATH ) ;
if ( FAILED ( hr ) ) {
continue ;
}
2023-12-27 17:19:43 +01:00
String fpath = String : : utf16 ( ( const char16_t * ) & file_path [ 0 ] ) . replace ( " \\ " , " / " ) ;
2022-11-21 14:04:01 +01:00
WIN32_FIND_DATAW d ;
HANDLE fnd = FindFirstFileW ( ( LPCWSTR ) & file_path [ 0 ] , & d ) ;
if ( fnd ! = INVALID_HANDLE_VALUE ) {
String fname = String : : utf16 ( ( const char16_t * ) d . cFileName ) ;
if ( ! fname . is_empty ( ) ) {
fpath = fpath . get_base_dir ( ) . path_join ( fname ) ;
}
FindClose ( fnd ) ;
}
ret . push_back ( fpath ) ;
}
return ret ;
}
2022-07-08 14:38:30 +02:00
2022-11-21 14:04:01 +01:00
String OS_Windows : : get_system_font_path ( const String & p_font_name , int p_weight , int p_stretch , bool p_italic ) const {
if ( ! dwrite_init ) {
return String ( ) ;
}
2022-07-08 14:38:30 +02:00
2022-11-21 14:04:01 +01:00
String font_name = _get_default_fontname ( p_font_name ) ;
2022-07-08 14:38:30 +02:00
UINT32 index = 0 ;
BOOL exists = false ;
2022-11-21 14:04:01 +01:00
HRESULT hr = font_collection - > FindFamilyName ( ( const WCHAR * ) font_name . utf16 ( ) . get_data ( ) , & index , & exists ) ;
2022-12-12 14:08:55 +01:00
if ( FAILED ( hr ) | | ! exists ) {
2022-07-08 14:38:30 +02:00
return String ( ) ;
}
ComAutoreleaseRef < IDWriteFontFamily > family ;
hr = font_collection - > GetFontFamily ( index , & family . reference ) ;
if ( FAILED ( hr ) | | family . is_null ( ) ) {
return String ( ) ;
}
ComAutoreleaseRef < IDWriteFont > dwrite_font ;
2022-11-21 14:04:01 +01:00
hr = family - > GetFirstMatchingFont ( _weight_to_dw ( p_weight ) , _stretch_to_dw ( p_stretch ) , p_italic ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL , & dwrite_font . reference ) ;
2022-07-08 14:38:30 +02:00
if ( FAILED ( hr ) | | dwrite_font . is_null ( ) ) {
return String ( ) ;
}
ComAutoreleaseRef < IDWriteFontFace > dwrite_face ;
hr = dwrite_font - > CreateFontFace ( & dwrite_face . reference ) ;
if ( FAILED ( hr ) | | dwrite_face . is_null ( ) ) {
return String ( ) ;
}
UINT32 number_of_files = 0 ;
hr = dwrite_face - > GetFiles ( & number_of_files , nullptr ) ;
if ( FAILED ( hr ) ) {
return String ( ) ;
}
Vector < ComAutoreleaseRef < IDWriteFontFile > > files ;
files . resize ( number_of_files ) ;
hr = dwrite_face - > GetFiles ( & number_of_files , ( IDWriteFontFile * * ) files . ptrw ( ) ) ;
if ( FAILED ( hr ) ) {
return String ( ) ;
}
for ( UINT32 i = 0 ; i < number_of_files ; i + + ) {
void const * reference_key = nullptr ;
UINT32 reference_key_size = 0 ;
ComAutoreleaseRef < IDWriteLocalFontFileLoader > loader ;
hr = files . write [ i ] - > GetLoader ( ( IDWriteFontFileLoader * * ) & loader . reference ) ;
if ( FAILED ( hr ) | | loader . is_null ( ) ) {
continue ;
}
hr = files . write [ i ] - > GetReferenceKey ( & reference_key , & reference_key_size ) ;
if ( FAILED ( hr ) ) {
continue ;
}
WCHAR file_path [ MAX_PATH ] ;
hr = loader - > GetFilePathFromKey ( reference_key , reference_key_size , & file_path [ 0 ] , MAX_PATH ) ;
if ( FAILED ( hr ) ) {
continue ;
}
2023-12-27 17:19:43 +01:00
String fpath = String : : utf16 ( ( const char16_t * ) & file_path [ 0 ] ) . replace ( " \\ " , " / " ) ;
2022-11-17 08:39:31 +01:00
WIN32_FIND_DATAW d ;
HANDLE fnd = FindFirstFileW ( ( LPCWSTR ) & file_path [ 0 ] , & d ) ;
if ( fnd ! = INVALID_HANDLE_VALUE ) {
String fname = String : : utf16 ( ( const char16_t * ) d . cFileName ) ;
if ( ! fname . is_empty ( ) ) {
fpath = fpath . get_base_dir ( ) . path_join ( fname ) ;
}
FindClose ( fnd ) ;
}
return fpath ;
2022-07-08 14:38:30 +02:00
}
return String ( ) ;
}
2014-02-15 06:01:39 +01:00
String OS_Windows : : get_executable_path ( ) const {
2020-07-27 12:43:20 +02:00
WCHAR bufname [ 4096 ] ;
2020-04-02 01:20:12 +02:00
GetModuleFileNameW ( nullptr , bufname , 4096 ) ;
2020-10-22 12:17:21 +02:00
String s = String : : utf16 ( ( const char16_t * ) bufname ) . replace ( " \\ " , " / " ) ;
2014-02-15 06:01:39 +01:00
return s ;
}
2014-02-10 02:10:30 +01:00
bool OS_Windows : : has_environment ( const String & p_var ) const {
2018-10-04 15:38:52 +02:00
# ifdef MINGW_ENABLED
2020-07-27 12:43:20 +02:00
return _wgetenv ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ! = nullptr ;
2018-10-04 15:38:52 +02:00
# else
2020-07-27 12:43:20 +02:00
WCHAR * env ;
2018-10-04 15:38:52 +02:00
size_t len ;
2020-07-27 12:43:20 +02:00
_wdupenv_s ( & env , & len , ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ;
2020-04-02 01:20:12 +02:00
const bool has_env = env ! = nullptr ;
2018-10-04 15:38:52 +02:00
free ( env ) ;
return has_env ;
# endif
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
String OS_Windows : : get_environment ( const String & p_var ) const {
2020-07-27 12:43:20 +02:00
WCHAR wval [ 0x7fff ] ; // MSDN says 32767 char is the maximum
int wlen = GetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , wval , 0x7fff ) ;
2015-05-04 18:12:05 +02:00
if ( wlen > 0 ) {
2020-07-27 12:43:20 +02:00
return String : : utf16 ( ( const char16_t * ) wval ) ;
2015-05-04 18:12:05 +02:00
}
2014-02-13 22:03:28 +01:00
return " " ;
2015-05-04 18:12:05 +02:00
}
2014-02-10 02:10:30 +01:00
2023-01-16 14:26:14 +01:00
void OS_Windows : : set_environment ( const String & p_var , const String & p_value ) const {
ERR_FAIL_COND_MSG ( p_var . is_empty ( ) | | p_var . contains ( " = " ) , vformat ( " Invalid environment variable name '%s', cannot be empty or include '='. " , p_var ) ) ;
Char16String var = p_var . utf16 ( ) ;
Char16String value = p_value . utf16 ( ) ;
ERR_FAIL_COND_MSG ( var . length ( ) + value . length ( ) + 2 > 32767 , vformat ( " Invalid definition for environment variable '%s', cannot exceed 32767 characters. " , p_var ) ) ;
SetEnvironmentVariableW ( ( LPCWSTR ) ( var . get_data ( ) ) , ( LPCWSTR ) ( value . get_data ( ) ) ) ;
}
void OS_Windows : : unset_environment ( const String & p_var ) const {
ERR_FAIL_COND_MSG ( p_var . is_empty ( ) | | p_var . contains ( " = " ) , vformat ( " Invalid environment variable name '%s', cannot be empty or include '='. " , p_var ) ) ;
SetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , nullptr ) ; // Null to delete.
2019-01-29 22:59:38 +01:00
}
2022-12-29 21:06:11 +01:00
String OS_Windows : : get_stdin_string ( ) {
2024-04-25 11:56:07 +02:00
char buff [ 1024 ] ;
2022-12-29 21:06:11 +01:00
DWORD count = 0 ;
2024-04-25 11:56:07 +02:00
if ( ReadFile ( GetStdHandle ( STD_INPUT_HANDLE ) , buff , 1024 , & count , nullptr ) ) {
return String : : utf8 ( ( const char * ) buff , count ) ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
return String ( ) ;
}
2024-01-09 02:36:19 +01:00
Error OS_Windows : : shell_open ( const String & p_uri ) {
2021-09-19 12:29:56 +02:00
INT_PTR ret = ( INT_PTR ) ShellExecuteW ( nullptr , nullptr , ( LPCWSTR ) ( p_uri . utf16 ( ) . get_data ( ) ) , nullptr , nullptr , SW_SHOWNORMAL ) ;
if ( ret > 32 ) {
return OK ;
} else {
switch ( ret ) {
case ERROR_FILE_NOT_FOUND :
case SE_ERR_DLLNOTFOUND :
return ERR_FILE_NOT_FOUND ;
case ERROR_PATH_NOT_FOUND :
return ERR_FILE_BAD_PATH ;
case ERROR_BAD_FORMAT :
return ERR_FILE_CORRUPT ;
case SE_ERR_ACCESSDENIED :
return ERR_UNAUTHORIZED ;
case 0 :
case SE_ERR_OOM :
return ERR_OUT_OF_MEMORY ;
default :
return FAILED ;
}
}
2014-04-18 16:43:54 +02:00
}
2022-12-07 04:33:35 +01:00
Error OS_Windows : : shell_show_in_file_manager ( String p_path , bool p_open_folder ) {
bool open_folder = false ;
if ( DirAccess : : dir_exists_absolute ( p_path ) & & p_open_folder ) {
open_folder = true ;
}
2023-07-02 22:25:17 +02:00
if ( ! p_path . is_quoted ( ) ) {
p_path = p_path . quote ( ) ;
2022-12-07 04:33:35 +01:00
}
p_path = p_path . replace ( " / " , " \\ " ) ;
INT_PTR ret = OK ;
if ( open_folder ) {
ret = ( INT_PTR ) ShellExecuteW ( nullptr , nullptr , L " explorer.exe " , LPCWSTR ( p_path . utf16 ( ) . get_data ( ) ) , nullptr , SW_SHOWNORMAL ) ;
} else {
ret = ( INT_PTR ) ShellExecuteW ( nullptr , nullptr , L " explorer.exe " , LPCWSTR ( ( String ( " /select, " ) + p_path ) . utf16 ( ) . get_data ( ) ) , nullptr , SW_SHOWNORMAL ) ;
}
if ( ret > 32 ) {
return OK ;
} else {
switch ( ret ) {
case ERROR_FILE_NOT_FOUND :
case SE_ERR_DLLNOTFOUND :
return ERR_FILE_NOT_FOUND ;
case ERROR_PATH_NOT_FOUND :
return ERR_FILE_BAD_PATH ;
case ERROR_BAD_FORMAT :
return ERR_FILE_CORRUPT ;
case SE_ERR_ACCESSDENIED :
return ERR_UNAUTHORIZED ;
case 0 :
case SE_ERR_OOM :
return ERR_OUT_OF_MEMORY ;
default :
return FAILED ;
}
}
}
2014-02-10 02:10:30 +01:00
String OS_Windows : : get_locale ( ) const {
const _WinLocale * wl = & _win_locales [ 0 ] ;
LANGID langid = GetUserDefaultUILanguage ( ) ;
String neutral ;
2020-07-25 22:42:11 +02:00
int lang = PRIMARYLANGID ( langid ) ;
int sublang = SUBLANGID ( langid ) ;
2014-02-10 02:10:30 +01:00
while ( wl - > locale ) {
2022-02-16 13:56:32 +01:00
if ( wl - > main_lang = = lang & & wl - > sublang = = SUBLANG_NEUTRAL ) {
2014-02-10 02:10:30 +01:00
neutral = wl - > locale ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
2022-02-16 13:56:32 +01:00
if ( lang = = wl - > main_lang & & sublang = = wl - > sublang ) {
2020-07-25 22:42:11 +02:00
return String ( wl - > locale ) . replace ( " - " , " _ " ) ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
wl + + ;
}
2022-02-16 13:56:32 +01:00
if ( ! neutral . is_empty ( ) ) {
2020-07-25 22:42:11 +02:00
return String ( neutral ) . replace ( " - " , " _ " ) ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
return " en " ;
}
2020-12-27 01:50:21 +01:00
String OS_Windows : : get_processor_name ( ) const {
const String id = " Hardware \\ Description \\ System \\ CentralProcessor \\ 0 " ;
HKEY hkey ;
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) ( id . utf16 ( ) . get_data ( ) ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
ERR_FAIL_V_MSG ( " " , String ( " Couldn't get the CPU model name. Returning an empty string. " ) ) ;
}
WCHAR buffer [ 256 ] ;
DWORD buffer_len = 256 ;
DWORD vtype = REG_SZ ;
2024-03-12 15:40:40 +01:00
if ( RegQueryValueExW ( hkey , L " ProcessorNameString " , nullptr , & vtype , ( LPBYTE ) buffer , & buffer_len ) = = ERROR_SUCCESS ) {
2020-12-27 01:50:21 +01:00
RegCloseKey ( hkey ) ;
return String : : utf16 ( ( const char16_t * ) buffer , buffer_len ) . strip_edges ( ) ;
} else {
RegCloseKey ( hkey ) ;
ERR_FAIL_V_MSG ( " " , String ( " Couldn't get the CPU model name. Returning an empty string. " ) ) ;
}
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : run ( ) {
2022-02-16 13:56:32 +01:00
if ( ! main_loop ) {
2014-02-10 02:10:30 +01:00
return ;
2022-02-16 13:56:32 +01:00
}
2016-03-09 00:00:52 +01:00
2020-12-22 10:50:29 +01:00
main_loop - > initialize ( ) ;
2016-03-09 00:00:52 +01:00
2022-08-16 20:29:55 +02:00
while ( true ) {
2020-03-09 16:56:48 +01:00
DisplayServer : : get_singleton ( ) - > process_events ( ) ; // get rid of pending events
2022-02-16 13:56:32 +01:00
if ( Main : : iteration ( ) ) {
2014-02-10 02:10:30 +01:00
break ;
2022-02-16 13:56:32 +01:00
}
}
2016-03-09 00:00:52 +01:00
2020-12-22 10:50:29 +01:00
main_loop - > finalize ( ) ;
2014-02-10 02:10:30 +01:00
}
MainLoop * OS_Windows : : get_main_loop ( ) const {
return main_loop ;
}
2021-12-20 10:28:54 +01:00
uint64_t OS_Windows : : get_embedded_pck_offset ( ) const {
Ref < FileAccess > f = FileAccess : : open ( get_executable_path ( ) , FileAccess : : READ ) ;
if ( f . is_null ( ) ) {
return 0 ;
}
// Process header.
{
f - > seek ( 0x3c ) ;
uint32_t pe_pos = f - > get_32 ( ) ;
f - > seek ( pe_pos ) ;
uint32_t magic = f - > get_32 ( ) ;
if ( magic ! = 0x00004550 ) {
return 0 ;
}
}
int num_sections ;
{
int64_t header_pos = f - > get_position ( ) ;
f - > seek ( header_pos + 2 ) ;
num_sections = f - > get_16 ( ) ;
f - > seek ( header_pos + 16 ) ;
uint16_t opt_header_size = f - > get_16 ( ) ;
// Skip rest of header + optional header to go to the section headers.
f - > seek ( f - > get_position ( ) + 2 + opt_header_size ) ;
}
int64_t section_table_pos = f - > get_position ( ) ;
// Search for the "pck" section.
int64_t off = 0 ;
for ( int i = 0 ; i < num_sections ; + + i ) {
int64_t section_header_pos = section_table_pos + i * 40 ;
f - > seek ( section_header_pos ) ;
uint8_t section_name [ 9 ] ;
f - > get_buffer ( section_name , 8 ) ;
section_name [ 8 ] = ' \0 ' ;
if ( strcmp ( ( char * ) section_name , " pck " ) = = 0 ) {
f - > seek ( section_header_pos + 20 ) ;
off = f - > get_32 ( ) ;
break ;
}
}
return off ;
}
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-17 17:11:41 +01:00
String OS_Windows : : get_config_path ( ) const {
2021-05-21 12:48:12 +02:00
if ( has_environment ( " APPDATA " ) ) {
2021-07-03 22:41:17 +02:00
return get_environment ( " APPDATA " ) . replace ( " \\ " , " / " ) ;
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-17 17:11:41 +01:00
}
2021-05-21 12:48:12 +02:00
return " . " ;
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-17 17:11:41 +01:00
}
String OS_Windows : : get_data_path ( ) const {
2021-05-21 12:48:12 +02:00
return get_config_path ( ) ;
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-17 17:11:41 +01:00
}
String OS_Windows : : get_cache_path ( ) const {
2021-02-22 22:54:12 +01:00
static String cache_path_cache ;
if ( cache_path_cache . is_empty ( ) ) {
2022-10-07 19:00:24 +02:00
if ( has_environment ( " LOCALAPPDATA " ) ) {
2021-02-22 22:54:12 +01:00
cache_path_cache = get_environment ( " LOCALAPPDATA " ) . replace ( " \\ " , " / " ) ;
}
if ( cache_path_cache . is_empty ( ) & & has_environment ( " TEMP " ) ) {
cache_path_cache = get_environment ( " TEMP " ) . replace ( " \\ " , " / " ) ;
}
if ( cache_path_cache . is_empty ( ) ) {
cache_path_cache = get_config_path ( ) ;
2021-05-07 19:02:35 +02:00
}
2021-05-21 12:48:12 +02:00
}
2021-02-22 22:54:12 +01:00
return cache_path_cache ;
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-17 17:11:41 +01:00
}
// Get properly capitalized engine name for system paths
String OS_Windows : : get_godot_dir_name ( ) const {
2017-11-19 21:18:01 +01:00
return String ( VERSION_SHORT_NAME ) . capitalize ( ) ;
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-17 17:11:41 +01:00
}
2021-07-11 03:39:31 +02:00
String OS_Windows : : get_system_dir ( SystemDir p_dir , bool p_shared_storage ) const {
2019-04-18 04:43:33 +02:00
KNOWNFOLDERID id ;
2014-12-02 18:02:41 +01:00
switch ( p_dir ) {
case SYSTEM_DIR_DESKTOP : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Desktop ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DCIM : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Pictures ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DOCUMENTS : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Documents ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_DOWNLOADS : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Downloads ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_MOVIES : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Videos ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_MUSIC : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Music ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_PICTURES : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Pictures ;
2014-12-02 18:02:41 +01:00
} break ;
case SYSTEM_DIR_RINGTONES : {
2019-04-18 04:43:33 +02:00
id = FOLDERID_Music ;
2014-12-02 18:02:41 +01:00
} break ;
}
2019-04-18 04:43:33 +02:00
PWSTR szPath ;
2020-04-02 01:20:12 +02:00
HRESULT res = SHGetKnownFolderPath ( id , 0 , nullptr , & szPath ) ;
2014-12-02 18:02:41 +01:00
ERR_FAIL_COND_V ( res ! = S_OK , String ( ) ) ;
2021-07-03 22:41:17 +02:00
String path = String : : utf16 ( ( const char16_t * ) szPath ) . replace ( " \\ " , " / " ) ;
2019-04-18 04:43:33 +02:00
CoTaskMemFree ( szPath ) ;
return path ;
2014-12-02 18:02:41 +01:00
}
2014-02-10 02:10:30 +01: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-17 17:11:41 +01:00
String OS_Windows : : get_user_data_dir ( ) const {
2022-10-18 16:43:37 +02:00
String appname = get_safe_dir_name ( GLOBAL_GET ( " application/config/name " ) ) ;
2021-12-09 10:42:46 +01:00
if ( ! appname . is_empty ( ) ) {
2022-10-18 16:43:37 +02:00
bool use_custom_dir = GLOBAL_GET ( " application/config/use_custom_user_dir " ) ;
2017-11-26 19:00:53 +01:00
if ( use_custom_dir ) {
2022-10-18 16:43:37 +02:00
String custom_dir = get_safe_dir_name ( GLOBAL_GET ( " application/config/custom_user_dir_name " ) , true ) ;
2021-12-09 10:42:46 +01:00
if ( custom_dir . is_empty ( ) ) {
2017-11-26 19:00:53 +01:00
custom_dir = appname ;
}
2022-08-30 02:34:01 +02:00
return get_data_path ( ) . path_join ( custom_dir ) . replace ( " \\ " , " / " ) ;
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-17 17:11:41 +01:00
} else {
2022-08-30 02:34:01 +02:00
return get_data_path ( ) . path_join ( get_godot_dir_name ( ) ) . path_join ( " app_userdata " ) . path_join ( appname ) . replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
}
}
2022-08-30 02:34:01 +02:00
return get_data_path ( ) . path_join ( get_godot_dir_name ( ) ) . path_join ( " app_userdata " ) . path_join ( " [unnamed project] " ) ;
2014-02-10 02:10:30 +01:00
}
2018-01-20 14:40:52 +01:00
String OS_Windows : : get_unique_id ( ) const {
2022-05-21 18:41:45 +02:00
HW_PROFILE_INFOA HwProfInfo ;
ERR_FAIL_COND_V ( ! GetCurrentHwProfileA ( & HwProfInfo ) , " " ) ;
return String ( ( HwProfInfo . szHwProfileGuid ) , HW_PROFILE_GUIDLEN ) ;
2018-01-20 14:40:52 +01:00
}
2017-07-19 22:00:46 +02:00
bool OS_Windows : : _check_internal_feature_support ( const String & p_feature ) {
2022-10-31 18:12:18 +01:00
if ( p_feature = = " system_fonts " ) {
2022-11-21 14:04:01 +01:00
return dwrite_init ;
2022-10-31 18:12:18 +01:00
}
if ( p_feature = = " pc " ) {
return true ;
}
return false ;
2017-02-09 00:07:35 +01:00
}
2016-06-06 00:14:33 +02:00
2017-09-08 03:01:49 +02:00
void OS_Windows : : disable_crash_handler ( ) {
crash_handler . disable ( ) ;
}
bool OS_Windows : : is_disable_crash_handler ( ) const {
return crash_handler . is_disabled ( ) ;
}
2017-09-25 15:15:11 +02:00
Error OS_Windows : : move_to_trash ( const String & p_path ) {
2018-08-14 10:49:16 +02:00
SHFILEOPSTRUCTW sf ;
2020-07-27 12:43:20 +02:00
Char16String utf16 = p_path . utf16 ( ) ;
WCHAR * from = new WCHAR [ utf16 . length ( ) + 2 ] ;
wcscpy_s ( from , utf16 . length ( ) + 1 , ( LPCWSTR ) ( utf16 . get_data ( ) ) ) ;
from [ utf16 . length ( ) + 1 ] = 0 ;
2018-08-18 00:30:22 +02:00
2020-03-09 16:56:48 +01:00
sf . hwnd = main_window ;
2017-09-25 15:15:11 +02:00
sf . wFunc = FO_DELETE ;
2018-08-18 00:30:22 +02:00
sf . pFrom = from ;
2020-04-02 01:20:12 +02:00
sf . pTo = nullptr ;
2017-09-25 15:15:11 +02:00
sf . fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION ;
sf . fAnyOperationsAborted = FALSE ;
2020-04-02 01:20:12 +02:00
sf . hNameMappings = nullptr ;
sf . lpszProgressTitle = nullptr ;
2017-09-25 15:15:11 +02:00
2018-08-14 10:49:16 +02:00
int ret = SHFileOperationW ( & sf ) ;
2018-08-18 00:30:22 +02:00
delete [ ] from ;
2017-09-25 15:15:11 +02:00
if ( ret ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " SHFileOperation error: " + itos ( ret ) ) ;
2017-09-25 15:15:11 +02:00
return FAILED ;
}
return OK ;
}
2023-04-13 21:17:55 +02:00
String OS_Windows : : get_system_ca_certificates ( ) {
HCERTSTORE cert_store = CertOpenSystemStoreA ( 0 , " ROOT " ) ;
2023-09-09 17:46:44 +02:00
ERR_FAIL_NULL_V_MSG ( cert_store , " " , " Failed to read the root certificate store. " ) ;
2023-04-13 21:17:55 +02:00
2023-05-14 14:38:46 +02:00
FILETIME curr_time ;
GetSystemTimeAsFileTime ( & curr_time ) ;
2023-04-13 21:17:55 +02:00
String certs ;
PCCERT_CONTEXT curr = CertEnumCertificatesInStore ( cert_store , nullptr ) ;
while ( curr ) {
2023-05-14 14:38:46 +02:00
FILETIME ft ;
DWORD size = sizeof ( ft ) ;
// Check if the certificate is disallowed.
if ( CertGetCertificateContextProperty ( curr , CERT_DISALLOWED_FILETIME_PROP_ID , & ft , & size ) & & CompareFileTime ( & curr_time , & ft ) ! = - 1 ) {
curr = CertEnumCertificatesInStore ( cert_store , curr ) ;
continue ;
}
// Encode and add to certificate list.
2023-04-13 21:17:55 +02:00
bool success = CryptBinaryToStringA ( curr - > pbCertEncoded , curr - > cbCertEncoded , CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR , nullptr , & size ) ;
ERR_CONTINUE ( ! success ) ;
PackedByteArray pba ;
pba . resize ( size ) ;
CryptBinaryToStringA ( curr - > pbCertEncoded , curr - > cbCertEncoded , CRYPT_STRING_BASE64HEADER | CRYPT_STRING_NOCR , ( char * ) pba . ptrw ( ) , & size ) ;
certs + = String ( ( char * ) pba . ptr ( ) , size ) ;
curr = CertEnumCertificatesInStore ( cert_store , curr ) ;
}
CertCloseStore ( cert_store , 0 ) ;
return certs ;
}
2014-02-10 02:10:30 +01:00
OS_Windows : : OS_Windows ( HINSTANCE _hInstance ) {
hInstance = _hInstance ;
2024-04-25 11:56:07 +02:00
# ifndef WINDOWS_SUBSYSTEM_CONSOLE
RedirectIOToConsole ( ) ;
# endif
SetConsoleOutputCP ( CP_UTF8 ) ;
SetConsoleCP ( CP_UTF8 ) ;
2023-07-20 11:14:16 +02:00
CoInitializeEx ( nullptr , COINIT_APARTMENTTHREADED ) ;
2023-04-10 09:38:25 +02:00
2017-08-27 19:01:34 +02:00
# ifdef WASAPI_ENABLED
AudioDriverManager : : add_driver ( & driver_wasapi ) ;
# endif
2016-10-17 17:40:45 +02:00
# ifdef XAUDIO2_ENABLED
2017-01-16 19:19:45 +01:00
AudioDriverManager : : add_driver ( & driver_xaudio2 ) ;
2016-10-17 17:40:45 +02:00
# endif
2017-09-22 07:56:02 +02:00
2020-03-09 16:56:48 +01:00
DisplayServerWindows : : register_windows_driver ( ) ;
2020-12-05 12:50:20 +01:00
// Enable ANSI escape code support on Windows 10 v1607 (Anniversary Update) and later.
// This lets the engine and projects use ANSI escape codes to color text just like on macOS and Linux.
//
// NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead.
// Therefore, error/warning messages are still colored on Windows versions older than 10.
2022-10-11 10:09:36 +02:00
HANDLE stdoutHandle = GetStdHandle ( STD_OUTPUT_HANDLE ) ;
DWORD outMode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING ;
2020-12-05 12:50:20 +01:00
if ( ! SetConsoleMode ( stdoutHandle , outMode ) ) {
// Windows 8.1 or below, or Windows 10 prior to Anniversary Update.
print_verbose ( " Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode. `print_rich()` will not work as expected. " ) ;
}
2017-11-21 10:35:01 +01:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( WindowsTerminalLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
OS_Windows : : ~ OS_Windows ( ) {
2023-04-10 09:38:25 +02:00
CoUninitialize ( ) ;
2014-02-10 02:10:30 +01:00
}