2023-01-05 13:25:55 +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
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"
2017-03-05 16:44:50 +01:00
# include "drivers/windows/dir_access_windows.h"
# include "drivers/windows/file_access_windows.h"
2019-02-12 15:43:54 +01:00
# include "joypad_windows.h"
2017-03-05 16:44:50 +01:00
# include "lang_table.h"
# include "main/main.h"
2020-03-09 16:56:48 +01:00
# include "platform/windows/display_server_windows.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"
2017-09-22 07:56:02 +02:00
# include "windows_terminal_logger.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>
2017-03-05 16:44:50 +01:00
# include <regstr.h>
# include <shlobj.h>
2022-10-11 12:39:41 +02:00
# include <wbemcli.h>
2015-02-12 04:17:29 +01:00
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
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 ) ;
return msg ;
}
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 ( ) {
2021-12-16 14:00:55 +01:00
if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) {
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
2021-12-16 14:00:55 +01:00
# ifndef WINDOWS_SUBSYSTEM_CONSOLE
RedirectIOToConsole ( ) ;
# endif
2017-12-10 19:38:26 +01:00
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 ) ;
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
CoInitialize ( nullptr ) ;
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. " ) ;
}
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 ( ) {
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
2022-04-29 00:51:04 +02:00
Error OS_Windows : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_path , String * r_resolved_path ) {
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
}
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
2017-12-06 13:29:01 +01:00
if ( p_also_set_library_path & & has_dll_directory_api ) {
2020-07-27 12:43:20 +02:00
cookie = add_dll_directory ( ( LPCWSTR ) ( path . get_base_dir ( ) . utf16 ( ) . get_data ( ) ) ) ;
2017-11-30 14:00:10 +01:00
}
2020-07-27 12:43:20 +02:00
p_library_handle = ( void * ) LoadLibraryExW ( ( LPCWSTR ) ( path . utf16 ( ) . get_data ( ) ) , nullptr , ( p_also_set_library_path & & has_dll_directory_api ) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0 ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_V_MSG ( ! p_library_handle , ERR_CANT_OPEN , " Can't open dynamic library: " + p_path + " , error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
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
}
2022-04-29 00:51:04 +02:00
if ( r_resolved_path ! = nullptr ) {
* r_resolved_path = path ;
}
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 ;
}
return OK ;
}
2017-07-27 09:23:21 +02: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 ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( ERR_CANT_RESOLVE , " Can't resolve symbol " + p_name + " , error: " + String : : num ( 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 {
2022-10-06 08:30:25 +02:00
typedef LONG NTSTATUS ;
2022-09-16 11:14:14 +02:00
typedef NTSTATUS ( WINAPI * RtlGetVersionPtr ) ( PRTL_OSVERSIONINFOW ) ;
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 {
2022-10-27 22:52:38 +02:00
if ( RenderingServer : : get_singleton ( ) - > get_rendering_device ( ) = = nullptr ) {
return Vector < String > ( ) ;
}
2022-10-11 12:39:41 +02:00
REFCLSID clsid = CLSID_WbemLocator ; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator ; // Interface UUID
IWbemLocator * wbemLocator = NULL ; // to get the services
IWbemServices * wbemServices = NULL ; // to get the class
IEnumWbemClassObject * iter = NULL ;
IWbemClassObject * pnpSDriverObject [ 1 ] ; // contains driver name, version, etc.
static String driver_name ;
static String driver_version ;
const String device_name = RenderingServer : : get_singleton ( ) - > get_rendering_device ( ) - > get_device_name ( ) ;
if ( device_name . is_empty ( ) ) {
return Vector < String > ( ) ;
}
CoInitialize ( nullptr ) ;
HRESULT hr = CoCreateInstance ( clsid , NULL , CLSCTX_INPROC_SERVER , uuid , ( LPVOID * ) & wbemLocator ) ;
if ( hr ! = S_OK ) {
return Vector < String > ( ) ;
}
2022-10-12 19:17:49 +02:00
BSTR resource_name = SysAllocString ( L " root \\ CIMV2 " ) ;
hr = wbemLocator - > ConnectServer ( resource_name , NULL , NULL , NULL , 0 , NULL , NULL , & wbemServices ) ;
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 " ) ;
hr = wbemServices - > ExecQuery ( query_lang , query , WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY , NULL , & iter ) ;
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 " ) ;
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , NULL , NULL ) ;
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
String d_name = String ( V_BSTR ( & dn ) ) ;
if ( d_name . is_empty ( ) ) {
object_name = SysAllocString ( L " DriverProviderName " ) ;
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , NULL , NULL ) ;
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
driver_name = String ( V_BSTR ( & dn ) ) ;
}
} else {
driver_name = d_name ;
}
} else {
object_name = SysAllocString ( L " DriverProviderName " ) ;
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dn , NULL , NULL ) ;
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
driver_name = String ( V_BSTR ( & dn ) ) ;
}
}
VARIANT dv ;
VariantInit ( & dv ) ;
object_name = SysAllocString ( L " DriverVersion " ) ;
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & dv , NULL , NULL ) ;
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 )
Vector < String > info ;
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 ;
bool daylight = false ;
if ( ! p_utc & & GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
daylight = true ;
}
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 ;
dt . dst = daylight ;
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 ;
bool daylight = false ;
2022-02-16 13:56:32 +01:00
if ( GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
2015-06-06 05:35:38 +02:00
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 ;
2015-06-06 05:35:38 +02:00
if ( daylight ) {
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 ;
2017-03-05 16:44:50 +01:00
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 ) {
2017-03-05 16:44:50 +01:00
Sleep ( 1 ) ;
2022-02-16 13:56:32 +01:00
} else {
2017-03-05 16:44:50 +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 {
2017-03-05 16:44:50 +01:00
uint64_t ticks ;
2020-05-22 13:20:19 +02:00
2017-03-05 16:44:50 +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
2017-03-05 16:44:50 +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 ;
2017-03-05 16:44:50 +01:00
return time ;
2014-02-10 02:10:30 +01:00
}
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 ( ) ;
}
}
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 ) ;
const bool success = ReadFile ( pipe [ 0 ] , bytes . ptr ( ) + bytes_in_buffer , CHUNK_SIZE , & read , NULL ) ;
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.
} else {
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 ;
2017-03-05 16:44:50 +01:00
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
2014-02-10 02:10:30 +01:00
pi . si . cb = sizeof ( pi . si ) ;
2017-03-05 16:44:50 +01:00
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
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
}
2020-12-18 19:49:13 +01:00
process_map - > insert ( pid , pi ) ;
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 ) {
2018-07-30 09:55:33 +02:00
ERR_FAIL_COND_V ( ! process_map - > has ( p_pid ) , FAILED ) ;
2014-02-10 02:10:30 +01:00
2018-07-30 09:55:33 +02:00
const PROCESS_INFORMATION pi = ( * process_map ) [ p_pid ] . pi ;
process_map - > erase ( p_pid ) ;
2014-02-10 02:10:30 +01:00
2018-08-27 17:32:43 +02:00
const int ret = TerminateProcess ( pi . hProcess , 0 ) ;
2014-02-10 02:10:30 +01:00
2018-07-30 09:55:33 +02:00
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
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 {
if ( ! process_map - > has ( p_pid ) ) {
return false ;
}
const PROCESS_INFORMATION & pi = ( * process_map ) [ p_pid ] . pi ;
DWORD dw_exit_code = 0 ;
if ( ! GetExitCodeProcess ( pi . hProcess , & dw_exit_code ) ) {
return false ;
}
if ( dw_exit_code ! = STILL_ACTIVE ) {
return false ;
}
return true ;
}
2017-03-05 16:44:50 +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 :
HRESULT STDMETHODCALLTYPE QueryInterface ( REFIID riid , VOID * * ppvInterface ) {
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 ;
}
ULONG STDMETHODCALLTYPE AddRef ( ) {
return InterlockedIncrement ( & _cRef ) ;
}
ULONG STDMETHODCALLTYPE Release ( ) {
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 ;
}
String fpath = String : : utf16 ( ( const char16_t * ) & file_path [ 0 ] ) ;
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 ;
}
2022-11-17 08:39:31 +01:00
String fpath = String : : utf16 ( ( const char16_t * ) & file_path [ 0 ] ) ;
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 ;
}
2017-03-05 16:44:50 +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
2017-03-05 16:44:50 +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 ) ;
2017-03-05 16:44:50 +01: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
2019-01-29 22:59:38 +01:00
bool OS_Windows : : set_environment ( const String & p_var , const String & p_value ) const {
2020-07-27 12:43:20 +02:00
return ( bool ) SetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) ( p_value . utf16 ( ) . get_data ( ) ) ) ;
2019-01-29 22:59:38 +01:00
}
2022-12-29 21:06:11 +01:00
String OS_Windows : : get_stdin_string ( ) {
WCHAR buff [ 1024 ] ;
DWORD count = 0 ;
if ( ReadConsoleW ( GetStdHandle ( STD_INPUT_HANDLE ) , buff , 1024 , & count , nullptr ) ) {
return String : : utf16 ( ( const char16_t * ) buff , count ) ;
2022-02-16 13:56:32 +01:00
}
2014-02-10 02:10:30 +01:00
return String ( ) ;
}
2014-04-18 16:43:54 +02:00
Error OS_Windows : : shell_open ( 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
}
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
2017-03-05 16:44:50 +01:00
while ( wl - > locale ) {
2022-02-16 13:56:32 +01:00
if ( wl - > main_lang = = lang & & wl - > sublang = = SUBLANG_NEUTRAL ) {
2017-03-05 16:44:50 +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 " ;
}
2018-01-29 16:46:30 +01:00
// We need this because GetSystemInfo() is unreliable on WOW64
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724381(v=vs.85).aspx
// Taken from MSDN
typedef BOOL ( WINAPI * LPFN_ISWOW64PROCESS ) ( HANDLE , PBOOL ) ;
LPFN_ISWOW64PROCESS fnIsWow64Process ;
BOOL is_wow64 ( ) {
BOOL wow64 = FALSE ;
fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress ( GetModuleHandle ( TEXT ( " kernel32 " ) ) , " IsWow64Process " ) ;
if ( fnIsWow64Process ) {
if ( ! fnIsWow64Process ( GetCurrentProcess ( ) , & wow64 ) ) {
wow64 = FALSE ;
}
}
return wow64 ;
}
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 ;
if ( RegQueryValueExW ( hkey , L " ProcessorNameString " , NULL , & vtype , ( LPBYTE ) buffer , & buffer_len ) = = ERROR_SUCCESS ) {
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
2017-03-05 16:44:50 +01:00
switch ( p_dir ) {
2014-12-02 18:02:41 +01:00
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 ) ;
2017-03-05 16:44:50 +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 ;
}
2014-02-10 02:10:30 +01:00
OS_Windows : : OS_Windows ( HINSTANCE _hInstance ) {
2017-03-05 16:44:50 +01:00
hInstance = _hInstance ;
2014-02-10 02:10:30 +01: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
}
2017-03-05 16:44:50 +01:00
OS_Windows : : ~ OS_Windows ( ) {
2014-02-10 02:10:30 +01:00
}