2014-02-10 09:10:30 +08:00
/*************************************************************************/
/* os_windows.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 20:16:55 +08:00
/* https://godotengine.org */
2014-02-10 09:10:30 +08:00
/*************************************************************************/
2022-01-04 04:27:34 +08:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 09:10:30 +08:00
/* */
/* 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-11 04:35:26 +08:00
2014-02-10 09:10:30 +08:00
# include "os_windows.h"
2016-12-21 13:29:58 +08:00
2020-02-27 10:30:20 +08:00
# include "core/debugger/engine_debugger.h"
# include "core/debugger/script_debugger.h"
2018-09-12 00:13:45 +08:00
# include "core/io/marshalls.h"
# include "core/version_generated.gen.h"
2021-09-23 14:56:12 +08:00
# include "drivers/unix/net_socket_posix.h"
2017-03-05 23:44:50 +08:00
# include "drivers/windows/dir_access_windows.h"
# include "drivers/windows/file_access_windows.h"
2019-02-12 22:43:54 +08:00
# include "joypad_windows.h"
2017-03-05 23:44:50 +08:00
# include "lang_table.h"
# include "main/main.h"
2020-03-09 23:56:48 +08:00
# include "platform/windows/display_server_windows.h"
2017-06-10 21:15:33 +08:00
# include "servers/audio_server.h"
2020-12-04 05:09:47 +08:00
# include "servers/rendering/rendering_server_default.h"
2017-09-22 13:56:02 +08:00
# include "windows_terminal_logger.h"
2014-12-03 01:02:41 +08:00
2019-02-12 22:43:54 +08:00
# include <avrt.h>
2022-02-08 00:33:55 +08:00
# include <bcrypt.h>
2019-02-20 20:00:19 +08:00
# include <direct.h>
2019-04-23 00:42:11 +08:00
# include <knownfolders.h>
2016-04-30 00:57:57 +08:00
# include <process.h>
2017-03-05 23:44:50 +08:00
# include <regstr.h>
# include <shlobj.h>
2015-02-12 11:17:29 +08:00
2015-01-17 09:48:35 +08:00
extern " C " {
2018-11-03 06:10:44 +08:00
__declspec ( dllexport ) DWORD NvOptimusEnablement = 1 ;
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
2015-01-17 09:48:35 +08:00
}
2014-12-03 01:02:41 +08:00
2017-08-19 02:46:13 +08:00
// Workaround mingw-w64 < 4.0 bug
# ifndef WM_TOUCH
# define WM_TOUCH 576
2016-02-05 00:16:22 +08:00
# endif
2018-12-14 04:32:11 +08:00
# ifndef WM_POINTERUPDATE
# define WM_POINTERUPDATE 0x0245
# endif
2020-01-16 19:07:58 +08:00
# if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
# define GetProcAddress (void *)GetProcAddress
# endif
2017-12-01 19:42:57 +08:00
static String format_error_message ( DWORD id ) {
2020-04-02 07:20:12 +08:00
LPWSTR messageBuffer = nullptr ;
2017-12-01 19:42:57 +08:00
size_t size = FormatMessageW ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2020-04-02 07:20:12 +08:00
nullptr , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , nullptr ) ;
2017-12-01 19:42:57 +08:00
2020-07-27 18:43:20 +08:00
String msg = " Error " + itos ( id ) + " : " + String : : utf16 ( ( const char16_t * ) messageBuffer , size ) ;
2017-12-01 19:42:57 +08:00
LocalFree ( messageBuffer ) ;
return msg ;
}
2022-01-28 14:30:01 +08: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 09:10:30 +08:00
void RedirectIOToConsole ( ) {
2021-12-16 21:00:55 +08:00
if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) {
2022-01-28 14:30:01 +08:00
RedirectStream ( " CONIN$ " , " r " , stdin , STD_INPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stdout , STD_OUTPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stderr , STD_ERROR_HANDLE ) ;
2014-02-10 09:10:30 +08:00
2021-12-16 21:00:55 +08:00
printf ( " \n " ) ; // Make sure our output is starting from the new line.
}
2019-11-28 20:41:07 +08:00
}
2020-03-09 23:56:48 +08:00
BOOL WINAPI HandlerRoutine ( _In_ DWORD dwCtrlType ) {
2022-02-16 20:56:32 +08:00
if ( ! EngineDebugger : : is_active ( ) ) {
2020-03-09 23:56:48 +08:00
return FALSE ;
2022-02-16 20:56:32 +08:00
}
2018-12-18 21:17:43 +08:00
2020-03-09 23:56:48 +08: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 21:17:43 +08:00
}
2021-07-23 00:23:48 +08: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 23:56:48 +08:00
void OS_Windows : : initialize_debugging ( ) {
SetConsoleCtrlHandler ( HandlerRoutine , TRUE ) ;
2017-12-11 02:38:26 +08:00
}
2020-03-09 23:56:48 +08:00
void OS_Windows : : initialize ( ) {
crash_handler . initialize ( ) ;
2017-12-11 02:38:26 +08:00
2021-12-16 21:00:55 +08:00
# ifndef WINDOWS_SUBSYSTEM_CONSOLE
RedirectIOToConsole ( ) ;
# endif
2017-12-11 02:38:26 +08:00
2020-03-09 23:56:48 +08: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-11 02:38:26 +08:00
2020-03-09 23:56:48 +08:00
NetSocketPosix : : make_default ( ) ;
2017-12-11 02:38:26 +08:00
2020-03-09 23:56:48 +08:00
// We need to know how often the clock is updated
2022-01-05 05:38:44 +08:00
QueryPerformanceFrequency ( ( LARGE_INTEGER * ) & ticks_per_second ) ;
QueryPerformanceCounter ( ( LARGE_INTEGER * ) & ticks_start ) ;
2017-12-11 02:38:26 +08:00
2020-03-09 23:56:48 +08: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-11 02:38:26 +08:00
2020-03-09 23:56:48 +08:00
process_map = memnew ( ( Map < ProcessID , ProcessInfo > ) ) ;
2017-12-11 02:38:26 +08:00
2020-05-11 22:26:10 +08: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 08:48:18 +08:00
IPUnix : : make_default ( ) ;
2020-04-02 07:20:12 +08:00
main_loop = nullptr ;
2017-12-11 02:38:26 +08:00
}
2020-03-09 23:56:48 +08:00
void OS_Windows : : delete_main_loop ( ) {
2022-02-16 20:56:32 +08:00
if ( main_loop ) {
2020-03-09 23:56:48 +08:00
memdelete ( main_loop ) ;
2022-02-16 20:56:32 +08:00
}
2020-04-02 07:20:12 +08:00
main_loop = nullptr ;
2017-12-11 02:38:26 +08:00
}
2020-03-09 23:56:48 +08:00
void OS_Windows : : set_main_loop ( MainLoop * p_main_loop ) {
main_loop = p_main_loop ;
2017-12-11 02:38:26 +08:00
}
2020-03-09 23:56:48 +08:00
void OS_Windows : : finalize ( ) {
# ifdef WINMIDI_ENABLED
driver_midi . close ( ) ;
# endif
2017-07-10 08:48:22 +08:00
2022-02-16 20:56:32 +08:00
if ( main_loop ) {
2020-03-09 23:56:48 +08:00
memdelete ( main_loop ) ;
2022-02-16 20:56:32 +08:00
}
2016-01-03 12:18:28 +08:00
2020-04-02 07:20:12 +08:00
main_loop = nullptr ;
2016-01-03 12:18:28 +08:00
}
2020-03-09 23:56:48 +08:00
void OS_Windows : : finalize_core ( ) {
timeEndPeriod ( 1 ) ;
2017-12-28 03:51:19 +08:00
2020-03-09 23:56:48 +08:00
memdelete ( process_map ) ;
NetSocketPosix : : cleanup ( ) ;
2017-07-10 08:48:22 +08:00
}
2022-02-08 00:33:55 +08: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 08:48:22 +08:00
2017-11-30 21:00:10 +08:00
Error OS_Windows : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_path ) {
2020-10-22 18:17:21 +08:00
String path = p_path . replace ( " / " , " \\ " ) ;
2018-01-05 02:42:29 +08:00
if ( ! FileAccess : : exists ( path ) ) {
//this code exists so gdnative can load .dll files from within the executable path
path = get_executable_path ( ) . get_base_dir ( ) . plus_file ( p_path . get_file ( ) ) ;
}
2017-12-06 20:29:01 +08: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 07:20:12 +08:00
bool has_dll_directory_api = ( ( add_dll_directory ! = nullptr ) & & ( remove_dll_directory ! = nullptr ) ) ;
DLL_DIRECTORY_COOKIE cookie = nullptr ;
2017-11-30 21:00:10 +08:00
2017-12-06 20:29:01 +08:00
if ( p_also_set_library_path & & has_dll_directory_api ) {
2020-07-27 18:43:20 +08:00
cookie = add_dll_directory ( ( LPCWSTR ) ( path . get_base_dir ( ) . utf16 ( ) . get_data ( ) ) ) ;
2017-11-30 21:00:10 +08:00
}
2020-07-27 18:43:20 +08: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 12:49:33 +08: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 21:00:10 +08:00
2018-01-26 18:48:20 +08:00
if ( cookie ) {
2017-12-06 20:29:01 +08:00
remove_dll_directory ( cookie ) ;
2017-11-30 21:00:10 +08:00
}
2017-03-08 09:50:13 +08:00
return OK ;
}
2017-04-03 22:11:38 +08:00
Error OS_Windows : : close_dynamic_library ( void * p_library_handle ) {
if ( ! FreeLibrary ( ( HMODULE ) p_library_handle ) ) {
2017-03-08 09:50:13 +08:00
return FAILED ;
}
return OK ;
}
2017-07-27 15:23:21 +08: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 22:11:38 +08:00
p_symbol_handle = ( void * ) GetProcAddress ( ( HMODULE ) p_library_handle , p_name . utf8 ( ) . get_data ( ) ) ;
2017-03-08 09:50:13 +08:00
if ( ! p_symbol_handle ) {
2017-07-27 15:23:21 +08:00
if ( ! p_optional ) {
2019-08-09 12:49:33 +08:00
ERR_FAIL_V_MSG ( ERR_CANT_RESOLVE , " Can't resolve symbol " + p_name + " , error: " + String : : num ( GetLastError ( ) ) + " . " ) ;
2017-07-27 15:23:21 +08:00
} else {
return ERR_CANT_RESOLVE ;
}
2017-03-08 09:50:13 +08:00
}
return OK ;
}
2019-05-21 01:36:24 +08:00
String OS_Windows : : get_name ( ) const {
2014-02-10 09:10:30 +08:00
return " Windows " ;
}
2021-10-28 23:16:47 +08:00
OS : : Date OS_Windows : : get_date ( bool p_utc ) const {
2014-02-10 09:10:30 +08:00
SYSTEMTIME systemtime ;
2021-10-28 23:16:47 +08:00
if ( p_utc ) {
2015-06-06 09:40:56 +08:00
GetSystemTime ( & systemtime ) ;
2021-10-28 23:16:47 +08:00
} else {
2015-06-06 09:40:56 +08:00
GetLocalTime ( & systemtime ) ;
2021-10-28 23:16:47 +08:00
}
2015-06-07 21:06:13 +08:00
2022-03-17 18:28:08 +08: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 ;
}
2014-02-10 09:10:30 +08:00
Date date ;
2017-03-05 23:44:50 +08:00
date . day = systemtime . wDay ;
date . month = Month ( systemtime . wMonth ) ;
date . weekday = Weekday ( systemtime . wDayOfWeek ) ;
date . year = systemtime . wYear ;
2022-03-17 18:28:08 +08:00
date . dst = daylight ;
2014-02-10 09:10:30 +08:00
return date ;
}
2020-05-14 20:29:06 +08:00
2021-10-28 23:16:47 +08:00
OS : : Time OS_Windows : : get_time ( bool p_utc ) const {
2014-02-10 09:10:30 +08:00
SYSTEMTIME systemtime ;
2021-10-28 23:16:47 +08:00
if ( p_utc ) {
2015-06-06 09:40:56 +08:00
GetSystemTime ( & systemtime ) ;
2021-10-28 23:16:47 +08:00
} else {
2015-06-06 09:40:56 +08:00
GetLocalTime ( & systemtime ) ;
2021-10-28 23:16:47 +08:00
}
2014-02-10 09:10:30 +08:00
Time time ;
2017-03-05 23:44:50 +08:00
time . hour = systemtime . wHour ;
2021-05-24 19:54:05 +08:00
time . minute = systemtime . wMinute ;
time . second = systemtime . wSecond ;
2014-02-10 09:10:30 +08:00
return time ;
}
2015-06-06 11:35:38 +08:00
OS : : TimeZoneInfo OS_Windows : : get_time_zone_info ( ) const {
TIME_ZONE_INFORMATION info ;
bool daylight = false ;
2022-02-16 20:56:32 +08:00
if ( GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
2015-06-06 11:35:38 +08:00
daylight = true ;
2022-02-16 20:56:32 +08:00
}
2015-06-06 11:35:38 +08:00
2022-03-17 18:28:08 +08: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 22:10:33 +08:00
TimeZoneInfo ret ;
2015-06-06 11:35:38 +08:00
if ( daylight ) {
ret . name = info . DaylightName ;
2022-03-17 18:28:08 +08:00
ret . bias = info . Bias + info . DaylightBias ;
2015-06-06 11:35:38 +08:00
} else {
ret . name = info . StandardName ;
2022-03-17 18:28:08 +08:00
ret . bias = info . Bias + info . StandardBias ;
2015-06-06 11:35:38 +08:00
}
2019-02-13 08:41:19 +08:00
// Bias value returned by GetTimeZoneInformation is inverted of what we expect
2021-03-12 21:35:16 +08:00
// For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
2022-03-17 18:28:08 +08:00
ret . bias = - ret . bias ;
2015-06-06 11:35:38 +08:00
return ret ;
}
2014-02-10 09:10:30 +08:00
2020-05-31 20:19:31 +08: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-12 01:09:21 +08:00
2015-08-07 01:29:33 +08:00
SYSTEMTIME st ;
GetSystemTime ( & st ) ;
2016-01-11 05:24:55 +08:00
FILETIME ft ;
2017-03-05 23:44:50 +08:00
SystemTimeToFileTime ( & st , & ft ) ;
2020-05-31 20:19:31 +08:00
uint64_t ticks_time ;
ticks_time = ft . dwHighDateTime ;
ticks_time < < = 32 ;
ticks_time | = ft . dwLowDateTime ;
2016-06-12 01:09:21 +08:00
2020-05-31 20:19:31 +08:00
return ( double ) ( ticks_time - TICKS_TO_UNIX_EPOCH ) / WINDOWS_TICKS_PER_SECOND ;
2015-08-07 01:29:33 +08:00
}
2014-02-10 09:10:30 +08:00
void OS_Windows : : delay_usec ( uint32_t p_usec ) const {
2022-02-16 20:56:32 +08:00
if ( p_usec < 1000 ) {
2017-03-05 23:44:50 +08:00
Sleep ( 1 ) ;
2022-02-16 20:56:32 +08:00
} else {
2017-03-05 23:44:50 +08:00
Sleep ( p_usec / 1000 ) ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
}
2020-05-14 20:29:06 +08:00
2014-02-10 09:10:30 +08:00
uint64_t OS_Windows : : get_ticks_usec ( ) const {
2017-03-05 23:44:50 +08:00
uint64_t ticks ;
2020-05-22 19:20:19 +08:00
2017-03-05 23:44:50 +08:00
// This is the number of clock ticks since start
2022-01-05 05:38:44 +08: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 19:20:19 +08:00
2017-03-05 23:44:50 +08:00
// Divide by frequency to get the time in seconds
2020-05-22 19:20:19 +08: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 23:44:50 +08:00
return time ;
2014-02-10 09:10:30 +08:00
}
2020-05-19 21:34:15 +08: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 18:43:20 +08:00
char32_t c = p_text [ i ] ;
2020-05-19 21:34:15 +08: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 ;
}
2021-12-16 21:00:55 +08: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 18:17:21 +08:00
String path = p_path . replace ( " / " , " \\ " ) ;
2020-12-19 02:49:13 +08:00
String command = _quote_command_line_argument ( path ) ;
2021-07-16 11:45:57 +08:00
for ( const String & E : p_arguments ) {
command + = " " + _quote_command_line_argument ( E ) ;
2020-12-19 02:49:13 +08:00
}
2020-10-22 18:17:21 +08:00
2021-12-16 21:00:55 +08: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-19 02:49:13 +08:00
if ( r_pipe ) {
2021-12-16 21:00:55 +08: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-02 06:51:53 +08:00
if ( read_stderr ) {
2021-12-16 21:00:55 +08:00
pi . si . hStdError = pipe [ 1 ] ;
2014-02-10 09:10:30 +08:00
}
2021-12-16 21:00:55 +08:00
inherit_handles = true ;
}
2022-01-27 10:11:00 +08:00
DWORD creation_flags = NORMAL_PRIORITY_CLASS ;
2021-12-16 21:00:55 +08:00
if ( p_open_console ) {
2022-01-27 10:11:00 +08:00
creation_flags | = CREATE_NEW_CONSOLE ;
2021-12-16 21:00:55 +08:00
} else {
2022-01-27 10:11:00 +08:00
creation_flags | = CREATE_NO_WINDOW ;
2021-12-16 21:00:55 +08:00
}
2022-01-27 10:11:00 +08:00
int ret = CreateProcessW ( nullptr , ( LPWSTR ) ( command . utf16 ( ) . ptrw ( ) ) , nullptr , nullptr , inherit_handles , creation_flags , nullptr , nullptr , si_w , & pi . pi ) ;
2021-12-16 21:00:55 +08: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).
char buf [ 4096 ] ;
DWORD read = 0 ;
for ( ; ; ) { // Read StdOut and StdErr from pipe.
bool success = ReadFile ( pipe [ 0 ] , buf , 4096 , & read , NULL ) ;
if ( ! success | | read = = 0 ) {
break ;
}
2019-04-08 02:46:52 +08:00
if ( p_pipe_mutex ) {
p_pipe_mutex - > lock ( ) ;
}
2021-12-16 21:00:55 +08:00
( * r_pipe ) + = String : : utf8 ( buf , read ) ;
2019-04-08 02:46:52 +08:00
if ( p_pipe_mutex ) {
2019-04-25 02:52:15 +08:00
p_pipe_mutex - > unlock ( ) ;
2019-04-08 02:46:52 +08:00
}
2022-02-16 20:56:32 +08:00
}
2021-12-16 21:00:55 +08:00
CloseHandle ( pipe [ 0 ] ) ; // Close pipe read handle.
} else {
WaitForSingleObject ( pi . pi . hProcess , INFINITE ) ;
2014-02-10 09:10:30 +08:00
}
2020-12-19 02:49:13 +08:00
if ( r_exitcode ) {
DWORD ret2 ;
GetExitCodeProcess ( pi . pi . hProcess , & ret2 ) ;
* r_exitcode = ret2 ;
}
2021-12-16 21:00:55 +08:00
2020-12-19 02:49:13 +08:00
CloseHandle ( pi . pi . hProcess ) ;
CloseHandle ( pi . pi . hThread ) ;
return OK ;
2022-02-16 20:56:32 +08:00
}
2020-12-19 02:49:13 +08:00
2021-12-16 21:00:55 +08: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-19 02:49:13 +08:00
String path = p_path . replace ( " / " , " \\ " ) ;
String command = _quote_command_line_argument ( path ) ;
2021-07-16 11:45:57 +08:00
for ( const String & E : p_arguments ) {
command + = " " + _quote_command_line_argument ( E ) ;
2020-05-19 21:34:15 +08:00
}
2014-02-10 09:10:30 +08:00
ProcessInfo pi ;
2017-03-05 23:44:50 +08:00
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
2014-02-10 09:10:30 +08:00
pi . si . cb = sizeof ( pi . si ) ;
2017-03-05 23:44:50 +08:00
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
LPSTARTUPINFOW si_w = ( LPSTARTUPINFOW ) & pi . si ;
2014-02-10 09:10:30 +08:00
2022-01-27 10:11:00 +08:00
DWORD creation_flags = NORMAL_PRIORITY_CLASS ;
2021-12-16 21:00:55 +08:00
if ( p_open_console ) {
2022-01-27 10:11:00 +08:00
creation_flags | = CREATE_NEW_CONSOLE ;
2021-12-16 21:00:55 +08:00
} else {
2022-01-27 10:11:00 +08:00
creation_flags | = CREATE_NO_WINDOW ;
2021-12-14 20:43:13 +08:00
}
2021-11-26 02:54:23 +08:00
2022-01-27 10:11:00 +08:00
int ret = CreateProcessW ( nullptr , ( LPWSTR ) ( command . utf16 ( ) . ptrw ( ) ) , nullptr , nullptr , false , creation_flags , nullptr , nullptr , si_w , & pi . pi ) ;
2020-12-19 02:49:13 +08:00
ERR_FAIL_COND_V_MSG ( ret = = 0 , ERR_CANT_FORK , " Could not create child process: " + command ) ;
2014-02-10 09:10:30 +08:00
2020-12-19 02:49:13 +08:00
ProcessID pid = pi . pi . dwProcessId ;
if ( r_child_id ) {
* r_child_id = pid ;
2020-05-19 21:34:15 +08:00
}
2020-12-19 02:49:13 +08:00
process_map - > insert ( pid , pi ) ;
2014-02-10 09:10:30 +08:00
return OK ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
2018-08-27 23:32:43 +08:00
Error OS_Windows : : kill ( const ProcessID & p_pid ) {
2018-07-30 15:55:33 +08:00
ERR_FAIL_COND_V ( ! process_map - > has ( p_pid ) , FAILED ) ;
2014-02-10 09:10:30 +08:00
2018-07-30 15:55:33 +08:00
const PROCESS_INFORMATION pi = ( * process_map ) [ p_pid ] . pi ;
process_map - > erase ( p_pid ) ;
2014-02-10 09:10:30 +08:00
2018-08-27 23:32:43 +08:00
const int ret = TerminateProcess ( pi . hProcess , 0 ) ;
2014-02-10 09:10:30 +08:00
2018-07-30 15:55:33 +08:00
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
2014-02-10 09:10:30 +08:00
2018-08-27 23:32:43 +08:00
return ret ! = 0 ? OK : FAILED ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
2017-08-07 18:17:31 +08:00
int OS_Windows : : get_process_id ( ) const {
2016-04-30 00:57:57 +08:00
return _getpid ( ) ;
}
2017-03-05 23:44:50 +08:00
Error OS_Windows : : set_cwd ( const String & p_cwd ) {
2022-02-16 20:56:32 +08:00
if ( _wchdir ( ( LPCWSTR ) ( p_cwd . utf16 ( ) . get_data ( ) ) ) ! = 0 ) {
2014-02-10 09:10:30 +08:00
return ERR_CANT_OPEN ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
return OK ;
}
2014-02-15 13:01:39 +08:00
String OS_Windows : : get_executable_path ( ) const {
2020-07-27 18:43:20 +08:00
WCHAR bufname [ 4096 ] ;
2020-04-02 07:20:12 +08:00
GetModuleFileNameW ( nullptr , bufname , 4096 ) ;
2020-10-22 18:17:21 +08:00
String s = String : : utf16 ( ( const char16_t * ) bufname ) . replace ( " \\ " , " / " ) ;
2014-02-15 13:01:39 +08:00
return s ;
}
2017-03-05 23:44:50 +08:00
bool OS_Windows : : has_environment ( const String & p_var ) const {
2018-10-04 21:38:52 +08:00
# ifdef MINGW_ENABLED
2020-07-27 18:43:20 +08:00
return _wgetenv ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ! = nullptr ;
2018-10-04 21:38:52 +08:00
# else
2020-07-27 18:43:20 +08:00
WCHAR * env ;
2018-10-04 21:38:52 +08:00
size_t len ;
2020-07-27 18:43:20 +08:00
_wdupenv_s ( & env , & len , ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ;
2020-04-02 07:20:12 +08:00
const bool has_env = env ! = nullptr ;
2018-10-04 21:38:52 +08:00
free ( env ) ;
return has_env ;
# endif
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
2017-03-05 23:44:50 +08:00
String OS_Windows : : get_environment ( const String & p_var ) const {
2020-07-27 18:43:20 +08: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 23:44:50 +08:00
if ( wlen > 0 ) {
2020-07-27 18:43:20 +08:00
return String : : utf16 ( ( const char16_t * ) wval ) ;
2015-05-05 00:12:05 +08:00
}
2014-02-14 05:03:28 +08:00
return " " ;
2015-05-05 00:12:05 +08:00
}
2014-02-10 09:10:30 +08:00
2019-01-30 05:59:38 +08:00
bool OS_Windows : : set_environment ( const String & p_var , const String & p_value ) const {
2020-07-27 18:43:20 +08:00
return ( bool ) SetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) ( p_value . utf16 ( ) . get_data ( ) ) ) ;
2019-01-30 05:59:38 +08:00
}
2014-02-10 09:10:30 +08:00
String OS_Windows : : get_stdin_string ( bool p_block ) {
if ( p_block ) {
char buff [ 1024 ] ;
2017-03-05 23:44:50 +08:00
return fgets ( buff , 1024 , stdin ) ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
return String ( ) ;
}
2014-04-18 22:43:54 +08:00
Error OS_Windows : : shell_open ( String p_uri ) {
2021-09-19 18:29:56 +08: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 22:43:54 +08:00
}
2014-02-10 09:10:30 +08:00
String OS_Windows : : get_locale ( ) const {
const _WinLocale * wl = & _win_locales [ 0 ] ;
LANGID langid = GetUserDefaultUILanguage ( ) ;
String neutral ;
2020-07-26 04:42:11 +08:00
int lang = PRIMARYLANGID ( langid ) ;
int sublang = SUBLANGID ( langid ) ;
2014-02-10 09:10:30 +08:00
2017-03-05 23:44:50 +08:00
while ( wl - > locale ) {
2022-02-16 20:56:32 +08:00
if ( wl - > main_lang = = lang & & wl - > sublang = = SUBLANG_NEUTRAL ) {
2017-03-05 23:44:50 +08:00
neutral = wl - > locale ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
2022-02-16 20:56:32 +08:00
if ( lang = = wl - > main_lang & & sublang = = wl - > sublang ) {
2020-07-26 04:42:11 +08:00
return String ( wl - > locale ) . replace ( " - " , " _ " ) ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
wl + + ;
}
2022-02-16 20:56:32 +08:00
if ( ! neutral . is_empty ( ) ) {
2020-07-26 04:42:11 +08:00
return String ( neutral ) . replace ( " - " , " _ " ) ;
2022-02-16 20:56:32 +08:00
}
2014-02-10 09:10:30 +08:00
return " en " ;
}
2018-01-29 23:46:30 +08: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 ;
}
int OS_Windows : : get_processor_count ( ) const {
SYSTEM_INFO sysinfo ;
2022-02-16 20:56:32 +08:00
if ( is_wow64 ( ) ) {
2018-01-29 23:46:30 +08:00
GetNativeSystemInfo ( & sysinfo ) ;
2022-02-16 20:56:32 +08:00
} else {
2018-01-29 23:46:30 +08:00
GetSystemInfo ( & sysinfo ) ;
2022-02-16 20:56:32 +08:00
}
2018-01-29 23:46:30 +08:00
return sysinfo . dwNumberOfProcessors ;
}
2020-12-27 08:50:21 +08: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 09:10:30 +08:00
void OS_Windows : : run ( ) {
2022-02-16 20:56:32 +08:00
if ( ! main_loop ) {
2014-02-10 09:10:30 +08:00
return ;
2022-02-16 20:56:32 +08:00
}
2016-03-09 07:00:52 +08:00
2020-12-22 17:50:29 +08:00
main_loop - > initialize ( ) ;
2016-03-09 07:00:52 +08:00
2014-02-10 09:10:30 +08:00
while ( ! force_quit ) {
2020-03-09 23:56:48 +08:00
DisplayServer : : get_singleton ( ) - > process_events ( ) ; // get rid of pending events
2022-02-16 20:56:32 +08:00
if ( Main : : iteration ( ) ) {
2014-02-10 09:10:30 +08:00
break ;
2022-02-16 20:56:32 +08:00
}
}
2016-03-09 07:00:52 +08:00
2020-12-22 17:50:29 +08:00
main_loop - > finalize ( ) ;
2014-02-10 09:10:30 +08:00
}
MainLoop * OS_Windows : : get_main_loop ( ) const {
return main_loop ;
}
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
String OS_Windows : : get_config_path ( ) const {
2021-05-08 01:02:35 +08:00
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if ( has_environment ( " XDG_CONFIG_HOME " ) ) {
2021-06-03 21:41:22 +08:00
if ( get_environment ( " XDG_CONFIG_HOME " ) . is_absolute_path ( ) ) {
2021-07-04 04:41:17 +08:00
return get_environment ( " XDG_CONFIG_HOME " ) . replace ( " \\ " , " / " ) ;
2021-05-08 01:02:35 +08:00
} else {
WARN_PRINT_ONCE ( " `XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification. " ) ;
}
2021-05-21 18:48:12 +08:00
}
if ( has_environment ( " APPDATA " ) ) {
2021-07-04 04:41:17 +08: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-18 00:11:41 +08:00
}
2021-05-21 18:48:12 +08: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-18 00:11:41 +08:00
}
String OS_Windows : : get_data_path ( ) const {
2021-05-08 01:02:35 +08:00
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
if ( has_environment ( " XDG_DATA_HOME " ) ) {
2021-06-03 21:41:22 +08:00
if ( get_environment ( " XDG_DATA_HOME " ) . is_absolute_path ( ) ) {
2021-07-04 04:41:17 +08:00
return get_environment ( " XDG_DATA_HOME " ) . replace ( " \\ " , " / " ) ;
2021-05-08 01:02:35 +08:00
} else {
WARN_PRINT_ONCE ( " `XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification. " ) ;
}
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
}
2021-05-21 18:48:12 +08: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-18 00:11:41 +08:00
}
String OS_Windows : : get_cache_path ( ) const {
2021-02-23 05:54:12 +08:00
static String cache_path_cache ;
if ( cache_path_cache . is_empty ( ) ) {
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if ( has_environment ( " XDG_CACHE_HOME " ) ) {
if ( get_environment ( " XDG_CACHE_HOME " ) . is_absolute_path ( ) ) {
cache_path_cache = get_environment ( " XDG_CACHE_HOME " ) . replace ( " \\ " , " / " ) ;
} else {
WARN_PRINT_ONCE ( " `XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%LOCALAPPDATA% \\ cache`, `%TEMP%` or `get_config_path()` per the XDG Base Directory specification. " ) ;
}
}
if ( cache_path_cache . is_empty ( ) & & has_environment ( " LOCALAPPDATA " ) ) {
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-08 01:02:35 +08:00
}
2021-05-21 18:48:12 +08:00
}
2021-02-23 05:54:12 +08: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-18 00:11:41 +08:00
}
// Get properly capitalized engine name for system paths
String OS_Windows : : get_godot_dir_name ( ) const {
2017-11-20 04:18:01 +08: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-18 00:11:41 +08:00
}
2021-07-11 09:39:31 +08:00
String OS_Windows : : get_system_dir ( SystemDir p_dir , bool p_shared_storage ) const {
2019-04-18 10:43:33 +08:00
KNOWNFOLDERID id ;
2014-12-03 01:02:41 +08:00
2017-03-05 23:44:50 +08:00
switch ( p_dir ) {
2014-12-03 01:02:41 +08:00
case SYSTEM_DIR_DESKTOP : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Desktop ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_DCIM : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Pictures ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_DOCUMENTS : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Documents ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_DOWNLOADS : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Downloads ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_MOVIES : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Videos ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_MUSIC : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Music ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_PICTURES : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Pictures ;
2014-12-03 01:02:41 +08:00
} break ;
case SYSTEM_DIR_RINGTONES : {
2019-04-18 10:43:33 +08:00
id = FOLDERID_Music ;
2014-12-03 01:02:41 +08:00
} break ;
}
2019-04-18 10:43:33 +08:00
PWSTR szPath ;
2020-04-02 07:20:12 +08:00
HRESULT res = SHGetKnownFolderPath ( id , 0 , nullptr , & szPath ) ;
2017-03-05 23:44:50 +08:00
ERR_FAIL_COND_V ( res ! = S_OK , String ( ) ) ;
2021-07-04 04:41:17 +08:00
String path = String : : utf16 ( ( const char16_t * ) szPath ) . replace ( " \\ " , " / " ) ;
2019-04-18 10:43:33 +08:00
CoTaskMemFree ( szPath ) ;
return path ;
2014-12-03 01:02:41 +08:00
}
2014-02-10 09:10:30 +08:00
Add initial support for the XDG Base Directory spec
Spec version 0.7 from https://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
(latest as of this commit).
Three virtual methods are added to OS for the various XDG paths we will use:
- OS::get_data_path gives XDG_DATA_HOME, or if missing:
~/.local/share on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_config_path gives XDG_CONFIG_HOME, or if missing:
~/.config on X11, ~/Library/Application Support/ on macOS and %APPDATA% on Windows
- OS::get_cache_path gives XDG_CACHE_HOME, or if missing:
~/.cache on X11, ~/Library/Caches on macOS and %APPDATA% on Windows
So for Windows there are no changes, for Linux we follow the full split spec
and for macOS stuff will move from ~/.godot to ~/Library/Application Support/Godot.
Support for system-wide installation of templates on Unix was removed for now,
as it's a bit hackish and I don't think anyone uses it.
user:// will still be OS::get_data_path() + "/godot/app_userdata/$name" by
default, but when using the application/config/use_shared_user_dir option
it will now use XDG_DATA_HOME/$name, e.g. ~/.local/share/MyGame.
For now everything still goes in EditorSettings::get_settings_dir(), but
this will be changed in a later commit to make use of the new splitting
where relevant.
Part of #3513.
2017-11-18 00:11:41 +08:00
String OS_Windows : : get_user_data_dir ( ) const {
2017-11-27 02:00:53 +08:00
String appname = get_safe_dir_name ( ProjectSettings : : get_singleton ( ) - > get ( " application/config/name " ) ) ;
2021-12-09 17:42:46 +08:00
if ( ! appname . is_empty ( ) ) {
2017-11-27 02:00:53 +08:00
bool use_custom_dir = ProjectSettings : : get_singleton ( ) - > get ( " application/config/use_custom_user_dir " ) ;
if ( use_custom_dir ) {
String custom_dir = get_safe_dir_name ( ProjectSettings : : get_singleton ( ) - > get ( " application/config/custom_user_dir_name " ) , true ) ;
2021-12-09 17:42:46 +08:00
if ( custom_dir . is_empty ( ) ) {
2017-11-27 02:00:53 +08:00
custom_dir = appname ;
}
return get_data_path ( ) . plus_file ( 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-18 00:11:41 +08:00
} else {
2017-11-27 02:00:53 +08:00
return get_data_path ( ) . plus_file ( get_godot_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( appname ) . replace ( " \\ " , " / " ) ;
2014-02-10 09:10:30 +08:00
}
}
2021-11-18 08:36:13 +08:00
return get_data_path ( ) . plus_file ( get_godot_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( " [unnamed project] " ) ;
2014-02-10 09:10:30 +08:00
}
2018-01-20 21:40:52 +08:00
String OS_Windows : : get_unique_id ( ) const {
HW_PROFILE_INFO HwProfInfo ;
ERR_FAIL_COND_V ( ! GetCurrentHwProfile ( & HwProfInfo ) , " " ) ;
2020-07-27 18:43:20 +08:00
return String : : utf16 ( ( const char16_t * ) ( HwProfInfo . szHwProfileGuid ) , HW_PROFILE_GUIDLEN ) ;
2018-01-20 21:40:52 +08:00
}
2017-07-20 04:00:46 +08:00
bool OS_Windows : : _check_internal_feature_support ( const String & p_feature ) {
2019-02-26 22:58:47 +08:00
return p_feature = = " pc " ;
2017-02-09 07:07:35 +08:00
}
2016-06-06 06:14:33 +08:00
2017-09-08 09:01:49 +08: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 21:15:11 +08:00
Error OS_Windows : : move_to_trash ( const String & p_path ) {
2018-08-14 16:49:16 +08:00
SHFILEOPSTRUCTW sf ;
2020-07-27 18:43:20 +08: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 06:30:22 +08:00
2020-03-09 23:56:48 +08:00
sf . hwnd = main_window ;
2017-09-25 21:15:11 +08:00
sf . wFunc = FO_DELETE ;
2018-08-18 06:30:22 +08:00
sf . pFrom = from ;
2020-04-02 07:20:12 +08:00
sf . pTo = nullptr ;
2017-09-25 21:15:11 +08:00
sf . fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION ;
sf . fAnyOperationsAborted = FALSE ;
2020-04-02 07:20:12 +08:00
sf . hNameMappings = nullptr ;
sf . lpszProgressTitle = nullptr ;
2017-09-25 21:15:11 +08:00
2018-08-14 16:49:16 +08:00
int ret = SHFileOperationW ( & sf ) ;
2018-08-18 06:30:22 +08:00
delete [ ] from ;
2017-09-25 21:15:11 +08:00
if ( ret ) {
2019-11-07 00:03:04 +08:00
ERR_PRINT ( " SHFileOperation error: " + itos ( ret ) ) ;
2017-09-25 21:15:11 +08:00
return FAILED ;
}
return OK ;
}
2014-02-10 09:10:30 +08:00
OS_Windows : : OS_Windows ( HINSTANCE _hInstance ) {
2020-11-24 17:12:55 +08:00
ticks_per_second = 0 ;
ticks_start = 0 ;
main_loop = nullptr ;
process_map = nullptr ;
2017-03-05 23:44:50 +08:00
force_quit = false ;
2018-12-14 04:32:11 +08:00
2017-03-05 23:44:50 +08:00
hInstance = _hInstance ;
2014-02-10 09:10:30 +08:00
# ifdef STDOUT_FILE
2017-03-05 23:44:50 +08:00
stdo = fopen ( " stdout.txt " , " wb " ) ;
2014-02-10 09:10:30 +08:00
# endif
2017-08-28 01:01:34 +08:00
# ifdef WASAPI_ENABLED
AudioDriverManager : : add_driver ( & driver_wasapi ) ;
# endif
2016-10-17 23:40:45 +08:00
# ifdef XAUDIO2_ENABLED
2017-01-17 02:19:45 +08:00
AudioDriverManager : : add_driver ( & driver_xaudio2 ) ;
2016-10-17 23:40:45 +08:00
# endif
2017-09-22 13:56:02 +08:00
2020-03-09 23:56:48 +08:00
DisplayServerWindows : : register_windows_driver ( ) ;
2017-11-21 17:35:01 +08:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( WindowsTerminalLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
2014-02-10 09:10:30 +08:00
}
2017-03-05 23:44:50 +08:00
OS_Windows : : ~ OS_Windows ( ) {
2014-02-10 09:10:30 +08:00
# ifdef STDOUT_FILE
fclose ( stdo ) ;
# endif
}