2023-01-10 15:26:54 +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
2018-09-11 18:13:45 +02:00
# include "core/io/marshalls.h"
2020-06-29 11:31:13 +02:00
# include "core/math/geometry.h"
2018-09-11 18:13:45 +02:00
# include "core/version_generated.gen.h"
2018-03-06 14:53:30 +01:00
# include "drivers/gles2/rasterizer_gles2.h"
2017-01-02 21:38:20 +01:00
# include "drivers/gles3/rasterizer_gles3.h"
2021-09-22 22:37:26 +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"
2017-06-10 15:15:33 +02:00
# include "servers/audio_server.h"
# include "servers/visual/visual_server_raster.h"
# include "servers/visual/visual_server_wrap_mt.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>
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>
2015-02-12 04:17:29 +01:00
2014-02-10 02:10:30 +01:00
static const WORD MAX_CONSOLE_LINES = 1500 ;
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
2020-01-16 12:07:58 +01:00
# if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
# define GetProcAddress (void *)GetProcAddress
# endif
2022-04-07 12:51:16 +02:00
typedef struct {
int count ;
int screen ;
HMONITOR monitor ;
} EnumScreenData ;
2018-07-03 23:52:23 +02:00
typedef struct {
int count ;
int screen ;
Size2 size ;
} EnumSizeData ;
typedef struct {
int count ;
int screen ;
Point2 pos ;
} EnumPosData ;
2022-04-07 12:51:16 +02:00
static BOOL CALLBACK _MonitorEnumProcScreen ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumScreenData * data = ( EnumScreenData * ) dwData ;
if ( data - > monitor = = hMonitor ) {
data - > screen = data - > count ;
}
data - > count + + ;
return TRUE ;
}
2018-07-03 23:52:23 +02:00
static BOOL CALLBACK _MonitorEnumProcSize ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumSizeData * data = ( EnumSizeData * ) dwData ;
if ( data - > count = = data - > screen ) {
data - > size . x = lprcMonitor - > right - lprcMonitor - > left ;
data - > size . y = lprcMonitor - > bottom - lprcMonitor - > top ;
}
data - > count + + ;
return TRUE ;
}
2017-12-01 12:42:57 +01:00
static String format_error_message ( DWORD id ) {
LPWSTR messageBuffer = NULL ;
size_t size = FormatMessageW ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2017-11-30 19:55:38 +01:00
NULL , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , NULL ) ;
2017-12-01 12:42:57 +01:00
2017-11-30 19:55:38 +01:00
String msg = " Error " + itos ( id ) + " : " + String ( messageBuffer , size ) ;
2017-12-01 12:42:57 +01:00
LocalFree ( messageBuffer ) ;
return msg ;
}
2014-02-10 02:10:30 +01:00
extern HINSTANCE godot_hinstance ;
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 12:08:09 +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 ) ;
2014-02-10 02:10:30 +01:00
2021-12-16 12:08:09 +01:00
printf ( " \n " ) ; // Make sure our output is starting from the new line.
}
2014-02-10 02:10:30 +01:00
}
2018-02-11 12:08:37 +01:00
BOOL WINAPI HandlerRoutine ( _In_ DWORD dwCtrlType ) {
if ( ScriptDebugger : : get_singleton ( ) = = NULL )
return FALSE ;
switch ( dwCtrlType ) {
case CTRL_C_EVENT :
ScriptDebugger : : get_singleton ( ) - > set_depth ( - 1 ) ;
ScriptDebugger : : get_singleton ( ) - > set_lines_left ( 1 ) ;
return TRUE ;
default :
return FALSE ;
}
}
2020-05-05 13:53:30 +02:00
// WinTab API
bool OS_Windows : : wintab_available = false ;
WTOpenPtr OS_Windows : : wintab_WTOpen = nullptr ;
WTClosePtr OS_Windows : : wintab_WTClose = nullptr ;
WTInfoPtr OS_Windows : : wintab_WTInfo = nullptr ;
WTPacketPtr OS_Windows : : wintab_WTPacket = nullptr ;
WTEnablePtr OS_Windows : : wintab_WTEnable = nullptr ;
// Windows Ink API
2020-05-19 22:28:57 +02:00
bool OS_Windows : : winink_available = false ;
2018-12-13 21:32:11 +01:00
GetPointerTypePtr OS_Windows : : win8p_GetPointerType = NULL ;
GetPointerPenInfoPtr OS_Windows : : win8p_GetPointerPenInfo = NULL ;
2018-02-11 12:08:37 +01:00
void OS_Windows : : initialize_debugging ( ) {
SetConsoleCtrlHandler ( HandlerRoutine , TRUE ) ;
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : initialize_core ( ) {
2017-09-08 03:01:49 +02:00
crash_handler . initialize ( ) ;
2017-03-05 16:44:50 +01:00
last_button_state = 0 ;
2021-09-16 21:34:46 +02:00
restore_mouse_trails = 0 ;
2014-02-10 02:10:30 +01:00
2022-01-29 20:42:03 +01:00
# ifndef WINDOWS_SUBSYSTEM_CONSOLE
2021-12-16 12:08:09 +01:00
RedirectIOToConsole ( ) ;
2022-01-29 20:42:03 +01:00
# endif
2021-12-16 12:08:09 +01:00
2017-03-05 16:44:50 +01:00
maximized = false ;
minimized = false ;
borderless = false ;
2014-02-10 02:10:30 +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 ) ;
2018-09-02 05:32:12 +02:00
NetSocketPosix : : make_default ( ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +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 ) ;
2014-02-10 02:10:30 +01:00
2018-05-01 09:47:34 +02: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 ) ;
2014-02-10 02:10:30 +01:00
process_map = memnew ( ( Map < ProcessID , ProcessInfo > ) ) ;
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 ) ;
2014-02-10 02:10:30 +01:00
IP_Unix : : make_default ( ) ;
2017-03-05 16:44:50 +01:00
cursor_shape = CURSOR_ARROW ;
2014-02-10 02:10:30 +01:00
}
bool OS_Windows : : can_draw ( ) const {
return ! minimized ;
} ;
2014-09-03 04:13:40 +02:00
# define MI_WP_SIGNATURE 0xFF515700
# define SIGNATURE_MASK 0xFFFFFF00
2018-08-30 20:05:59 +02:00
// Keeping the name suggested by Microsoft, but this macro really answers:
// Is this mouse event emulated from touch or pen input?
2017-03-05 16:44:50 +01:00
# define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
2018-08-30 20:05:59 +02:00
// This one tells whether the event comes from touchscreen (and not from pen)
# define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
2014-09-03 04:13:40 +02:00
2017-11-30 19:55:38 +01:00
void OS_Windows : : _touch_event ( bool p_pressed , float p_x , float p_y , int idx ) {
// Defensive
if ( touch_state . has ( idx ) = = p_pressed )
return ;
if ( p_pressed ) {
touch_state . insert ( idx , Vector2 ( p_x , p_y ) ) ;
} else {
touch_state . erase ( idx ) ;
}
2014-09-03 04:13:40 +02:00
2017-05-20 17:38:03 +02:00
Ref < InputEventScreenTouch > event ;
event . instance ( ) ;
event - > set_index ( idx ) ;
event - > set_pressed ( p_pressed ) ;
2017-06-03 10:54:24 +02:00
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
2014-09-03 04:13:40 +02:00
if ( main_loop ) {
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( event ) ;
2014-09-03 04:13:40 +02:00
}
} ;
2022-05-23 14:02:12 +02:00
bool OS_Windows : : tts_is_speaking ( ) const {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_V_MSG ( ! tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND_V ( ! tts , false ) ;
return tts - > is_speaking ( ) ;
}
bool OS_Windows : : tts_is_paused ( ) const {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_V_MSG ( ! tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND_V ( ! tts , false ) ;
return tts - > is_paused ( ) ;
}
Array OS_Windows : : tts_get_voices ( ) const {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_V_MSG ( ! tts , Array ( ) , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND_V ( ! tts , Array ( ) ) ;
return tts - > get_voices ( ) ;
}
void OS_Windows : : tts_speak ( const String & p_text , const String & p_voice , int p_volume , float p_pitch , float p_rate , int p_utterance_id , bool p_interrupt ) {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND ( ! tts ) ;
tts - > speak ( p_text , p_voice , p_volume , p_pitch , p_rate , p_utterance_id , p_interrupt ) ;
}
void OS_Windows : : tts_pause ( ) {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND ( ! tts ) ;
tts - > pause ( ) ;
}
void OS_Windows : : tts_resume ( ) {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND ( ! tts ) ;
tts - > resume ( ) ;
}
void OS_Windows : : tts_stop ( ) {
2023-05-22 15:41:09 +02:00
ERR_FAIL_COND_MSG ( ! tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2022-05-23 14:02:12 +02:00
ERR_FAIL_COND ( ! tts ) ;
tts - > stop ( ) ;
}
2017-11-30 19:55:38 +01:00
void OS_Windows : : _drag_event ( float p_x , float p_y , int idx ) {
Map < int , Vector2 > : : Element * curr = touch_state . find ( idx ) ;
// Defensive
if ( ! curr )
return ;
if ( curr - > get ( ) = = Vector2 ( p_x , p_y ) )
return ;
2017-05-20 17:38:03 +02:00
Ref < InputEventScreenDrag > event ;
event . instance ( ) ;
event - > set_index ( idx ) ;
2017-06-03 10:54:24 +02:00
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
2019-10-26 23:55:46 +02:00
event - > set_relative ( Vector2 ( p_x , p_y ) - curr - > get ( ) ) ;
2014-09-03 04:13:40 +02:00
if ( main_loop )
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( event ) ;
2019-10-26 23:55:46 +02:00
curr - > get ( ) = Vector2 ( p_x , p_y ) ;
2014-09-03 04:13:40 +02:00
} ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
LRESULT OS_Windows : : WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
2019-03-03 21:12:19 +01:00
if ( drop_events ) {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
} else {
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
} ;
2017-03-05 16:44:50 +01:00
switch ( uMsg ) // Check For Windows Messages
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
case WM_SETFOCUS : {
2017-01-25 19:21:41 +01:00
window_has_focus = true ;
2018-11-09 20:30:08 +01:00
// Restore mouse mode
_set_mouse_mode_impl ( mouse_mode ) ;
2017-01-25 19:21:41 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
case WM_KILLFOCUS : {
2017-01-25 19:21:41 +01:00
window_has_focus = false ;
2018-11-09 20:30:08 +01:00
// Release capture unconditionally because it can be set due to dragging, in addition to captured mode
ReleaseCapture ( ) ;
2017-12-01 21:06:42 +01:00
2017-11-30 19:55:38 +01:00
// Release every touch to avoid sticky points
for ( Map < int , Vector2 > : : Element * E = touch_state . front ( ) ; E ; E = E - > next ( ) ) {
_touch_event ( false , E - > get ( ) . x , E - > get ( ) . y , E - > key ( ) ) ;
}
touch_state . clear ( ) ;
2017-12-01 21:06:42 +01:00
2017-01-25 19:21:41 +01:00
break ;
}
2017-03-05 16:44:50 +01:00
case WM_ACTIVATE : // Watch For Window Activate Message
2014-02-10 02:10:30 +01:00
{
minimized = HIWORD ( wParam ) ! = 0 ;
if ( ! main_loop ) {
return 0 ;
} ;
if ( LOWORD ( wParam ) = = WA_ACTIVE | | LOWORD ( wParam ) = = WA_CLICKACTIVE ) {
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_FOCUS_IN ) ;
2019-11-28 13:41:07 +01:00
window_focused = true ;
2017-03-05 16:44:50 +01:00
alt_mem = false ;
control_mem = false ;
shift_mem = false ;
2023-01-20 14:04:55 +01:00
gr_mem = false ;
2018-11-09 20:30:08 +01:00
} else { // WM_INACTIVE
2019-04-15 19:41:44 +02:00
input - > release_pressed_events ( ) ;
2014-02-10 02:10:30 +01:00
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_FOCUS_OUT ) ;
2019-11-28 13:41:07 +01:00
window_focused = false ;
2017-03-05 16:44:50 +01:00
alt_mem = false ;
2014-02-10 02:10:30 +01:00
} ;
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
2020-05-05 13:53:30 +02:00
wintab_WTEnable ( wtctx , GET_WM_ACTIVATE_STATE ( wParam , lParam ) ) ;
}
return 0 ; // Return To The Message Loop
2014-02-10 02:10:30 +01:00
}
2019-06-13 14:31:08 +02:00
case WM_GETMINMAXINFO : {
if ( video_mode . resizable & & ! video_mode . fullscreen ) {
Size2 decor = get_real_window_size ( ) - get_window_size ( ) ; // Size of window decorations
MINMAXINFO * min_max_info = ( MINMAXINFO * ) lParam ;
if ( min_size ! = Size2 ( ) ) {
min_max_info - > ptMinTrackSize . x = min_size . x + decor . x ;
min_max_info - > ptMinTrackSize . y = min_size . y + decor . y ;
}
if ( max_size ! = Size2 ( ) ) {
min_max_info - > ptMaxTrackSize . x = max_size . x + decor . x ;
min_max_info - > ptMaxTrackSize . y = max_size . y + decor . y ;
}
return 0 ;
} else {
break ;
}
}
2014-02-10 02:10:30 +01:00
case WM_PAINT :
Main : : force_redraw ( ) ;
break ;
2017-03-05 16:44:50 +01:00
case WM_SYSCOMMAND : // Intercept System Commands
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
switch ( wParam ) // Check System Calls
2014-02-10 02:10:30 +01:00
{
2017-03-05 16:44:50 +01:00
case SC_SCREENSAVE : // Screensaver Trying To Start?
case SC_MONITORPOWER : // Monitor Trying To Enter Powersave?
return 0 ; // Prevent From Happening
2014-02-10 02:10:30 +01:00
case SC_KEYMENU :
2017-03-05 16:44:50 +01:00
if ( ( lParam > > 16 ) < = 0 )
2014-02-10 02:10:30 +01:00
return 0 ;
}
2017-03-05 16:44:50 +01:00
break ; // Exit
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
case WM_CLOSE : // Did We Receive A Close Message?
2014-02-10 02:10:30 +01:00
{
if ( main_loop )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_QUIT_REQUEST ) ;
//force_quit=true;
2017-03-05 16:44:50 +01:00
return 0 ; // Jump Back
2014-02-10 02:10:30 +01:00
}
case WM_MOUSELEAVE : {
2017-03-05 16:44:50 +01:00
old_invalid = true ;
outside = true ;
if ( main_loop & & mouse_mode ! = MOUSE_MODE_CAPTURED )
2015-09-24 23:06:15 +02:00
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_EXIT ) ;
2014-02-10 02:10:30 +01:00
} break ;
2018-07-27 18:32:27 +02:00
case WM_INPUT : {
if ( mouse_mode ! = MOUSE_MODE_CAPTURED | | ! use_raw_input ) {
break ;
}
UINT dwSize ;
GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , NULL , & dwSize , sizeof ( RAWINPUTHEADER ) ) ;
LPBYTE lpb = new BYTE [ dwSize ] ;
if ( lpb = = NULL ) {
return 0 ;
}
if ( GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , lpb , & dwSize , sizeof ( RAWINPUTHEADER ) ) ! = dwSize )
OutputDebugString ( TEXT ( " GetRawInputData does not return correct size ! \n " ) ) ;
RAWINPUT * raw = ( RAWINPUT * ) lpb ;
if ( raw - > header . dwType = = RIM_TYPEMOUSE ) {
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_control ( control_mem ) ;
mm - > set_shift ( shift_mem ) ;
mm - > set_alt ( alt_mem ) ;
2020-05-05 13:53:30 +02:00
2020-05-03 14:38:58 +02:00
mm - > set_pressure ( ( raw - > data . mouse . ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN ) ? 1.0f : 0.0f ) ;
2018-07-27 18:32:27 +02:00
mm - > set_button_mask ( last_button_state ) ;
Point2i c ( video_mode . width / 2 , video_mode . height / 2 ) ;
// centering just so it works as before
POINT pos = { ( int ) c . x , ( int ) c . y } ;
ClientToScreen ( hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
mm - > set_position ( c ) ;
mm - > set_global_position ( c ) ;
mm - > set_speed ( Vector2 ( 0 , 0 ) ) ;
2018-08-24 09:35:07 +02:00
if ( raw - > data . mouse . usFlags = = MOUSE_MOVE_RELATIVE ) {
2018-07-27 18:32:27 +02:00
mm - > set_relative ( Vector2 ( raw - > data . mouse . lLastX , raw - > data . mouse . lLastY ) ) ;
2018-08-23 02:27:15 +02:00
} else if ( raw - > data . mouse . usFlags = = MOUSE_MOVE_ABSOLUTE ) {
int nScreenWidth = GetSystemMetrics ( SM_CXVIRTUALSCREEN ) ;
int nScreenHeight = GetSystemMetrics ( SM_CYVIRTUALSCREEN ) ;
int nScreenLeft = GetSystemMetrics ( SM_XVIRTUALSCREEN ) ;
int nScreenTop = GetSystemMetrics ( SM_YVIRTUALSCREEN ) ;
Vector2 abs_pos (
2018-08-24 09:35:07 +02:00
( double ( raw - > data . mouse . lLastX ) - 65536.0 / ( nScreenWidth ) ) * nScreenWidth / 65536.0 + nScreenLeft ,
( double ( raw - > data . mouse . lLastY ) - 65536.0 / ( nScreenHeight ) ) * nScreenHeight / 65536.0 + nScreenTop ) ;
2018-08-23 02:27:15 +02:00
POINT coords ; //client coords
coords . x = abs_pos . x ;
coords . y = abs_pos . y ;
ScreenToClient ( hWnd , & coords ) ;
2018-08-24 09:35:07 +02:00
mm - > set_relative ( Vector2 ( coords . x - old_x , coords . y - old_y ) ) ;
2018-08-23 02:27:15 +02:00
old_x = coords . x ;
old_y = coords . y ;
/*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth));
Input . mi . dy = ( int ) ( ( ( ( double ) ( pos . y ) - nScreenTop ) * 65536 ) / nScreenHeight + 65536 / ( nScreenHeight ) ) ;
*/
2018-07-27 18:32:27 +02:00
}
2018-09-23 11:06:55 +02:00
if ( window_has_focus & & main_loop & & mm - > get_relative ( ) ! = Vector2 ( ) )
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mm ) ;
2018-07-27 18:32:27 +02:00
}
delete [ ] lpb ;
} break ;
2020-05-05 13:53:30 +02:00
case WT_CSRCHANGE :
case WT_PROXIMITY : {
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
2020-05-05 13:53:30 +02:00
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
min_pressure = int ( pressure . axMin ) ;
max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
return 0 ;
}
} break ;
case WT_PACKET : {
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
2020-05-05 13:53:30 +02:00
PACKET packet ;
if ( wintab_WTPacket ( wtctx , wParam , & packet ) ) {
float pressure = float ( packet . pkNormalPressure - min_pressure ) / float ( max_pressure - min_pressure ) ;
last_pressure = pressure ;
last_pressure_update = 0 ;
double azim = ( packet . pkOrientation . orAzimuth / 10.0f ) * ( Math_PI / 180 ) ;
double alt = Math : : tan ( ( Math : : abs ( packet . pkOrientation . orAltitude / 10.0f ) ) * ( Math_PI / 180 ) ) ;
if ( tilt_supported ) {
last_tilt = Vector2 ( Math : : atan ( Math : : sin ( azim ) / alt ) , Math : : atan ( Math : : cos ( azim ) / alt ) ) ;
} else {
last_tilt = Vector2 ( ) ;
}
2020-05-11 11:32:15 +02:00
2022-06-18 13:27:02 +02:00
last_pen_inverted = packet . pkStatus & TPS_INVERT ;
2020-05-11 11:32:15 +02:00
POINT coords ;
GetCursorPos ( & coords ) ;
ScreenToClient ( hWnd , & coords ) ;
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2022-07-29 19:41:29 +02:00
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-05-11 11:32:15 +02:00
break ;
2022-07-29 19:41:29 +02:00
}
2020-05-11 11:32:15 +02:00
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
2020-08-17 13:43:23 +02:00
mm - > set_control ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift ( GetKeyState ( VK_SHIFT ) < 0 ) ;
2020-05-11 11:32:15 +02:00
mm - > set_alt ( alt_mem ) ;
2022-06-18 13:27:02 +02:00
mm - > set_pen_inverted ( last_pen_inverted ) ;
2020-05-11 11:32:15 +02:00
mm - > set_pressure ( last_pressure ) ;
mm - > set_tilt ( last_tilt ) ;
mm - > set_button_mask ( last_button_state ) ;
mm - > set_position ( Vector2 ( coords . x , coords . y ) ) ;
mm - > set_global_position ( Vector2 ( coords . x , coords . y ) ) ;
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
Point2i c ( video_mode . width / 2 , video_mode . height / 2 ) ;
old_x = c . x ;
old_y = c . y ;
if ( mm - > get_position ( ) = = c ) {
center = c ;
return 0 ;
}
Point2i ncenter = mm - > get_position ( ) ;
center = ncenter ;
POINT pos = { ( int ) c . x , ( int ) c . y } ;
ClientToScreen ( hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
}
mm - > set_speed ( input - > get_last_mouse_speed ( ) ) ;
if ( old_invalid ) {
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
old_invalid = false ;
}
mm - > set_relative ( Vector2 ( mm - > get_position ( ) - Vector2 ( old_x , old_y ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
if ( window_has_focus & & main_loop )
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mm ) ;
2020-05-05 13:53:30 +02:00
}
return 0 ;
}
} break ;
2020-05-22 09:44:23 +02:00
case WM_POINTERENTER : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
if ( ( get_current_tablet_driver ( ) ! = " winink " ) | | ! winink_available ) {
break ;
}
uint32_t pointer_id = LOWORD ( wParam ) ;
POINTER_INPUT_TYPE pointer_type = PT_POINTER ;
if ( ! win8p_GetPointerType ( pointer_id , & pointer_type ) ) {
break ;
}
if ( pointer_type ! = PT_PEN ) {
break ;
}
block_mm = true ;
return 0 ;
} break ;
case WM_POINTERLEAVE : {
block_mm = false ;
return 0 ;
} break ;
2018-12-13 21:32:11 +01:00
case WM_POINTERUPDATE : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) ! = " winink " ) | | ! winink_available ) {
2018-12-13 21:32:11 +01:00
break ;
}
uint32_t pointer_id = LOWORD ( wParam ) ;
POINTER_INPUT_TYPE pointer_type = PT_POINTER ;
if ( ! win8p_GetPointerType ( pointer_id , & pointer_type ) ) {
break ;
}
if ( pointer_type ! = PT_PEN ) {
break ;
}
POINTER_PEN_INFO pen_info ;
if ( ! win8p_GetPointerPenInfo ( pointer_id , & pen_info ) ) {
break ;
}
if ( input - > is_emulating_mouse_from_touch ( ) ) {
// Universal translation enabled; ignore OS translation
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
if ( outside ) {
//mouse enter
if ( main_loop & & mouse_mode ! = MOUSE_MODE_CAPTURED )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_ENTER ) ;
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
outside = false ;
//Once-Off notification, must call again....
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( TRACKMOUSEEVENT ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
tme . dwHoverTime = HOVER_DEFAULT ;
TrackMouseEvent ( & tme ) ;
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2022-07-29 19:41:29 +02:00
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2018-12-13 21:32:11 +01:00
break ;
2022-07-29 19:41:29 +02:00
}
2018-12-13 21:32:11 +01:00
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
2022-06-18 13:27:02 +02:00
mm - > set_pen_inverted ( pen_info . penFlags & ( PEN_FLAG_INVERTED | PEN_FLAG_ERASER ) ) ;
2020-05-03 14:38:58 +02:00
if ( pen_info . penMask & PEN_MASK_PRESSURE ) {
mm - > set_pressure ( ( float ) pen_info . pressure / 1024 ) ;
} else {
mm - > set_pressure ( ( HIWORD ( wParam ) & POINTER_MESSAGE_FLAG_FIRSTBUTTON ) ? 1.0f : 0.0f ) ;
}
if ( ( pen_info . penMask & PEN_MASK_TILT_X ) & & ( pen_info . penMask & PEN_MASK_TILT_Y ) ) {
mm - > set_tilt ( Vector2 ( ( float ) pen_info . tiltX / 90 , ( float ) pen_info . tiltY / 90 ) ) ;
}
2018-12-13 21:32:11 +01:00
2020-08-17 13:43:23 +02:00
mm - > set_control ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift ( GetKeyState ( VK_SHIFT ) < 0 ) ;
2018-12-13 21:32:11 +01:00
mm - > set_alt ( alt_mem ) ;
mm - > set_button_mask ( last_button_state ) ;
2019-11-07 13:12:15 +01:00
POINT coords ; //client coords
coords . x = GET_X_LPARAM ( lParam ) ;
coords . y = GET_Y_LPARAM ( lParam ) ;
ScreenToClient ( hWnd , & coords ) ;
mm - > set_position ( Vector2 ( coords . x , coords . y ) ) ;
mm - > set_global_position ( Vector2 ( coords . x , coords . y ) ) ;
2018-12-13 21:32:11 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
Point2i c ( video_mode . width / 2 , video_mode . height / 2 ) ;
old_x = c . x ;
old_y = c . y ;
if ( mm - > get_position ( ) = = c ) {
center = c ;
return 0 ;
}
Point2i ncenter = mm - > get_position ( ) ;
center = ncenter ;
POINT pos = { ( int ) c . x , ( int ) c . y } ;
ClientToScreen ( hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
}
mm - > set_speed ( input - > get_last_mouse_speed ( ) ) ;
if ( old_invalid ) {
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
old_invalid = false ;
}
mm - > set_relative ( Vector2 ( mm - > get_position ( ) - Vector2 ( old_x , old_y ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
if ( window_has_focus & & main_loop )
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mm ) ;
2020-05-22 09:44:23 +02:00
return 0 ;
2018-12-13 21:32:11 +01:00
} break ;
2014-02-10 02:10:30 +01:00
case WM_MOUSEMOVE : {
2020-05-22 09:44:23 +02:00
if ( block_mm ) {
break ;
}
2018-07-27 18:32:27 +02:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2014-02-10 02:10:30 +01:00
2018-02-24 03:04:30 +01:00
if ( input - > is_emulating_mouse_from_touch ( ) ) {
// Universal translation enabled; ignore OS translation
LPARAM extra = GetMessageExtraInfo ( ) ;
2018-08-30 20:05:59 +02:00
if ( IsTouchEvent ( extra ) ) {
2018-02-24 03:04:30 +01:00
break ;
}
}
2014-09-03 04:13:40 +02:00
if ( outside ) {
2015-09-24 23:06:15 +02:00
//mouse enter
2017-03-05 16:44:50 +01:00
if ( main_loop & & mouse_mode ! = MOUSE_MODE_CAPTURED )
2015-09-24 23:06:15 +02:00
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_ENTER ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
2014-09-03 04:13:40 +02:00
set_cursor_shape ( c ) ;
2017-03-05 16:44:50 +01:00
outside = false ;
2014-09-03 04:13:40 +02:00
//Once-Off notification, must call again....
TRACKMOUSEEVENT tme ;
2017-03-05 16:44:50 +01:00
tme . cbSize = sizeof ( TRACKMOUSEEVENT ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
tme . dwHoverTime = HOVER_DEFAULT ;
2014-09-03 04:13:40 +02:00
TrackMouseEvent ( & tme ) ;
}
2017-01-25 19:21:41 +01:00
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2022-07-29 19:41:29 +02:00
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2017-01-25 19:21:41 +01:00
break ;
2022-07-29 19:41:29 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_control ( ( wParam & MK_CONTROL ) ! = 0 ) ;
mm - > set_shift ( ( wParam & MK_SHIFT ) ! = 0 ) ;
mm - > set_alt ( alt_mem ) ;
2017-03-05 16:44:50 +01:00
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
2020-05-05 13:53:30 +02:00
// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not update recently.
if ( last_pressure_update < 10 ) {
last_pressure_update + + ;
} else {
last_tilt = Vector2 ( ) ;
last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-06-18 13:27:02 +02:00
last_pen_inverted = false ;
2020-05-05 13:53:30 +02:00
}
} else {
last_tilt = Vector2 ( ) ;
last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-06-18 13:27:02 +02:00
last_pen_inverted = false ;
2020-05-05 13:53:30 +02:00
}
mm - > set_pressure ( last_pressure ) ;
mm - > set_tilt ( last_tilt ) ;
2022-06-18 13:27:02 +02:00
mm - > set_pen_inverted ( last_pen_inverted ) ;
2020-05-05 13:53:30 +02:00
2018-07-08 15:12:13 +02:00
mm - > set_button_mask ( last_button_state ) ;
2018-07-05 16:50:30 +02:00
2017-06-03 10:54:24 +02:00
mm - > set_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
2018-01-05 18:33:13 +01:00
mm - > set_global_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
Point2i c ( video_mode . width / 2 , video_mode . height / 2 ) ;
2017-02-11 04:54:36 +01:00
old_x = c . x ;
old_y = c . y ;
2017-06-03 10:54:24 +02:00
if ( mm - > get_position ( ) = = c ) {
2017-03-05 16:44:50 +01:00
center = c ;
2014-02-10 02:10:30 +01:00
return 0 ;
}
2017-06-03 10:54:24 +02:00
Point2i ncenter = mm - > get_position ( ) ;
2017-03-05 16:44:50 +01:00
center = ncenter ;
POINT pos = { ( int ) c . x , ( int ) c . y } ;
2014-02-10 02:10:30 +01:00
ClientToScreen ( hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
}
2017-05-20 17:38:03 +02:00
mm - > set_speed ( input - > get_last_mouse_speed ( ) ) ;
2014-02-10 02:10:30 +01:00
if ( old_invalid ) {
2017-06-03 10:54:24 +02:00
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2017-03-05 16:44:50 +01:00
old_invalid = false ;
2014-02-10 02:10:30 +01:00
}
2017-06-03 10:54:24 +02:00
mm - > set_relative ( Vector2 ( mm - > get_position ( ) - Vector2 ( old_x , old_y ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2017-01-25 19:21:41 +01:00
if ( window_has_focus & & main_loop )
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mm ) ;
2014-02-10 02:10:30 +01:00
} break ;
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
2018-06-21 21:01:51 +02:00
if ( input - > is_emulating_mouse_from_touch ( ) ) {
// Universal translation enabled; ignore OS translations for left button
LPARAM extra = GetMessageExtraInfo ( ) ;
2018-08-30 20:05:59 +02:00
if ( IsTouchEvent ( extra ) ) {
2018-06-21 21:01:51 +02:00
break ;
}
}
2019-06-15 12:11:30 +02:00
FALLTHROUGH ;
2014-02-10 02:10:30 +01:00
case WM_MBUTTONDOWN :
case WM_MBUTTONUP :
case WM_RBUTTONDOWN :
case WM_RBUTTONUP :
case WM_MOUSEWHEEL :
2016-02-04 17:16:22 +01:00
case WM_MOUSEHWHEEL :
2014-02-10 02:10:30 +01:00
case WM_LBUTTONDBLCLK :
2018-01-10 21:55:00 +01:00
case WM_MBUTTONDBLCLK :
case WM_RBUTTONDBLCLK :
2018-07-05 16:50:30 +02:00
case WM_XBUTTONDBLCLK :
case WM_XBUTTONDOWN :
case WM_XBUTTONUP : {
Ref < InputEventMouseButton > mb ;
mb . instance ( ) ;
switch ( uMsg ) {
case WM_LBUTTONDOWN : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 1 ) ;
2014-02-10 02:10:30 +01:00
} break ;
2018-07-05 16:50:30 +02:00
case WM_LBUTTONUP : {
mb - > set_pressed ( false ) ;
mb - > set_button_index ( 1 ) ;
} break ;
case WM_MBUTTONDOWN : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 3 ) ;
} break ;
case WM_MBUTTONUP : {
mb - > set_pressed ( false ) ;
mb - > set_button_index ( 3 ) ;
} break ;
case WM_RBUTTONDOWN : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 2 ) ;
} break ;
case WM_RBUTTONUP : {
mb - > set_pressed ( false ) ;
mb - > set_button_index ( 2 ) ;
} break ;
case WM_LBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 1 ) ;
mb - > set_doubleclick ( true ) ;
} break ;
case WM_RBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 2 ) ;
mb - > set_doubleclick ( true ) ;
} break ;
case WM_MBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
mb - > set_button_index ( 3 ) ;
mb - > set_doubleclick ( true ) ;
} break ;
case WM_MOUSEWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
if ( ! motion )
return 0 ;
2021-08-06 15:06:11 +02:00
if ( motion > 0 ) {
2018-07-05 16:50:30 +02:00
mb - > set_button_index ( BUTTON_WHEEL_UP ) ;
2021-08-06 15:06:11 +02:00
} else {
2018-07-05 16:50:30 +02:00
mb - > set_button_index ( BUTTON_WHEEL_DOWN ) ;
2021-08-06 15:06:11 +02:00
}
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2018-07-05 16:50:30 +02:00
} break ;
case WM_MOUSEHWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
if ( ! motion )
return 0 ;
if ( motion < 0 ) {
mb - > set_button_index ( BUTTON_WHEEL_LEFT ) ;
} else {
mb - > set_button_index ( BUTTON_WHEEL_RIGHT ) ;
2014-02-10 02:10:30 +01:00
}
2021-08-06 15:06:11 +02:00
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2018-07-05 16:50:30 +02:00
} break ;
case WM_XBUTTONDOWN : {
mb - > set_pressed ( true ) ;
if ( HIWORD ( wParam ) = = XBUTTON1 )
mb - > set_button_index ( BUTTON_XBUTTON1 ) ;
else
mb - > set_button_index ( BUTTON_XBUTTON2 ) ;
} break ;
case WM_XBUTTONUP : {
mb - > set_pressed ( false ) ;
if ( HIWORD ( wParam ) = = XBUTTON1 )
mb - > set_button_index ( BUTTON_XBUTTON1 ) ;
else
mb - > set_button_index ( BUTTON_XBUTTON2 ) ;
} break ;
case WM_XBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
if ( HIWORD ( wParam ) = = XBUTTON1 )
mb - > set_button_index ( BUTTON_XBUTTON1 ) ;
else
mb - > set_button_index ( BUTTON_XBUTTON2 ) ;
mb - > set_doubleclick ( true ) ;
} break ;
2019-04-09 17:08:36 +02:00
default : {
return 0 ;
}
2018-07-05 16:50:30 +02:00
}
2014-02-10 02:10:30 +01:00
2018-07-05 16:50:30 +02:00
mb - > set_control ( ( wParam & MK_CONTROL ) ! = 0 ) ;
mb - > set_shift ( ( wParam & MK_SHIFT ) ! = 0 ) ;
mb - > set_alt ( alt_mem ) ;
//mb->get_alt()=(wParam&MK_MENU)!=0;
2018-07-08 15:12:13 +02:00
if ( mb - > is_pressed ( ) )
last_button_state | = ( 1 < < ( mb - > get_button_index ( ) - 1 ) ) ;
else
last_button_state & = ~ ( 1 < < ( mb - > get_button_index ( ) - 1 ) ) ;
mb - > set_button_mask ( last_button_state ) ;
2018-07-05 16:50:30 +02:00
mb - > set_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
2018-09-23 11:29:15 +02:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & ! use_raw_input ) {
2018-07-05 16:50:30 +02:00
mb - > set_position ( Vector2 ( old_x , old_y ) ) ;
}
if ( uMsg ! = WM_MOUSEWHEEL & & uMsg ! = WM_MOUSEHWHEEL ) {
if ( mb - > is_pressed ( ) ) {
2018-11-09 20:30:08 +01:00
if ( + + pressrc > 0 & & mouse_mode ! = MOUSE_MODE_CAPTURED )
2018-07-05 16:50:30 +02:00
SetCapture ( hWnd ) ;
} else {
if ( - - pressrc < = 0 ) {
2018-11-09 20:30:08 +01:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
ReleaseCapture ( ) ;
}
2018-07-05 16:50:30 +02:00
pressrc = 0 ;
2017-03-05 16:44:50 +01:00
}
2014-02-10 02:10:30 +01:00
}
2019-08-05 16:03:33 +02:00
} else {
2019-05-19 12:34:40 +02:00
// for reasons unknown to mankind, wheel comes in screen coordinates
2018-07-05 16:50:30 +02:00
POINT coords ;
coords . x = mb - > get_position ( ) . x ;
coords . y = mb - > get_position ( ) . y ;
ScreenToClient ( hWnd , & coords ) ;
mb - > set_position ( Vector2 ( coords . x , coords . y ) ) ;
}
mb - > set_global_position ( mb - > get_position ( ) ) ;
if ( main_loop ) {
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mb ) ;
2018-07-05 16:50:30 +02:00
if ( mb - > is_pressed ( ) & & mb - > get_button_index ( ) > 3 & & mb - > get_button_index ( ) < 8 ) {
//send release for mouse wheel
Ref < InputEventMouseButton > mbd = mb - > duplicate ( ) ;
2018-07-08 15:12:13 +02:00
last_button_state & = ~ ( 1 < < ( mbd - > get_button_index ( ) - 1 ) ) ;
mbd - > set_button_mask ( last_button_state ) ;
2018-07-05 16:50:30 +02:00
mbd - > set_pressed ( false ) ;
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( mbd ) ;
2018-07-05 16:50:30 +02:00
}
2014-02-10 02:10:30 +01:00
}
2018-07-05 16:50:30 +02:00
} break ;
2014-02-10 02:10:30 +01:00
2018-11-30 04:36:48 +01:00
case WM_MOVE : {
if ( ! IsIconic ( hWnd ) ) {
int x = LOWORD ( lParam ) ;
int y = HIWORD ( lParam ) ;
last_pos = Point2 ( x , y ) ;
}
} break ;
2014-02-10 02:10:30 +01:00
case WM_SIZE : {
2018-11-30 04:36:48 +01:00
// Ignore size when a SIZE_MINIMIZED event is triggered
if ( wParam ! = SIZE_MINIMIZED ) {
int window_w = LOWORD ( lParam ) ;
int window_h = HIWORD ( lParam ) ;
if ( window_w > 0 & & window_h > 0 & & ! preserve_window_size ) {
video_mode . width = window_w ;
video_mode . height = window_h ;
} else {
preserve_window_size = false ;
set_window_size ( Size2 ( video_mode . width , video_mode . height ) ) ;
}
2016-06-26 23:31:31 +02:00
}
2018-11-30 04:36:48 +01:00
2018-02-26 21:38:00 +01:00
if ( wParam = = SIZE_MAXIMIZED ) {
maximized = true ;
minimized = false ;
} else if ( wParam = = SIZE_MINIMIZED ) {
maximized = false ;
minimized = true ;
} else if ( wParam = = SIZE_RESTORED ) {
maximized = false ;
minimized = false ;
}
2017-12-10 19:38:26 +01:00
//return 0; // Jump Back
2014-02-10 02:10:30 +01:00
} break ;
2016-07-01 15:31:37 +02:00
case WM_ENTERSIZEMOVE : {
2019-04-15 19:41:44 +02:00
input - > release_pressed_events ( ) ;
2017-03-05 16:44:50 +01:00
move_timer_id = SetTimer ( hWnd , 1 , USER_TIMER_MINIMUM , ( TIMERPROC ) NULL ) ;
2016-07-01 15:31:37 +02:00
} break ;
case WM_EXITSIZEMOVE : {
KillTimer ( hWnd , move_timer_id ) ;
} break ;
case WM_TIMER : {
if ( wParam = = move_timer_id ) {
process_key_events ( ) ;
2019-01-22 17:17:39 +01:00
if ( ! Main : : is_iterating ( ) ) {
Main : : iteration ( ) ;
}
2016-07-01 15:31:37 +02:00
}
} break ;
2014-02-10 02:10:30 +01:00
case WM_SYSKEYDOWN :
case WM_SYSKEYUP :
case WM_KEYUP :
case WM_KEYDOWN : {
2017-03-05 16:44:50 +01:00
if ( wParam = = VK_SHIFT )
2020-06-10 05:22:57 +02:00
shift_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2017-03-05 16:44:50 +01:00
if ( wParam = = VK_CONTROL )
2020-06-10 05:22:57 +02:00
control_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2017-03-05 16:44:50 +01:00
if ( wParam = = VK_MENU ) {
alt_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
if ( lParam & ( 1 < < 24 ) )
gr_mem = alt_mem ;
2014-02-10 02:10:30 +01:00
}
2019-01-23 14:59:54 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
if ( wParam = = VK_F4 & & alt_mem & & ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ) {
if ( main_loop )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_QUIT_REQUEST ) ;
}
}
2017-01-14 12:26:56 +01:00
/*
if ( wParam = = VK_WIN ) TODO wtf is this ?
meta_mem = uMsg = = WM_KEYDOWN ;
*/
2019-06-29 05:09:59 +02:00
FALLTHROUGH ;
}
2014-02-10 02:10:30 +01:00
case WM_CHAR : {
ERR_BREAK ( key_event_pos > = KEY_EVENT_BUFFER_SIZE ) ;
2015-01-18 08:06:20 +01:00
// Make sure we don't include modifiers for the modifier key itself.
2014-02-10 02:10:30 +01:00
KeyEvent ke ;
2017-05-20 17:38:03 +02:00
ke . shift = ( wParam ! = VK_SHIFT ) ? shift_mem : false ;
ke . alt = ( ! ( wParam = = VK_MENU & & ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ) ) ? alt_mem : false ;
ke . control = ( wParam ! = VK_CONTROL ) ? control_mem : false ;
ke . meta = meta_mem ;
2017-03-05 16:44:50 +01:00
ke . uMsg = uMsg ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ke . uMsg = = WM_SYSKEYDOWN )
ke . uMsg = WM_KEYDOWN ;
if ( ke . uMsg = = WM_SYSKEYUP )
ke . uMsg = WM_KEYUP ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
ke . wParam = wParam ;
ke . lParam = lParam ;
key_event_buffer [ key_event_pos + + ] = ke ;
2014-02-10 02:10:30 +01:00
} break ;
case WM_INPUTLANGCHANGEREQUEST : {
2018-08-24 09:35:07 +02:00
// FIXME: Do something?
2014-02-10 02:10:30 +01:00
} break ;
2014-09-22 05:50:48 +02:00
case WM_TOUCH : {
BOOL bHandled = FALSE ;
UINT cInputs = LOWORD ( wParam ) ;
PTOUCHINPUT pInputs = memnew_arr ( TOUCHINPUT , cInputs ) ;
2017-03-05 16:44:50 +01:00
if ( pInputs ) {
if ( GetTouchInputInfo ( ( HTOUCHINPUT ) lParam , cInputs , pInputs , sizeof ( TOUCHINPUT ) ) ) {
for ( UINT i = 0 ; i < cInputs ; i + + ) {
2014-09-22 05:50:48 +02:00
TOUCHINPUT ti = pInputs [ i ] ;
2018-06-21 21:01:51 +02:00
POINT touch_pos = {
TOUCH_COORD_TO_PIXEL ( ti . x ) ,
TOUCH_COORD_TO_PIXEL ( ti . y ) ,
} ;
ScreenToClient ( hWnd , & touch_pos ) ;
2014-09-22 05:50:48 +02:00
//do something with each touch input entry
if ( ti . dwFlags & TOUCHEVENTF_MOVE ) {
2018-06-21 21:01:51 +02:00
_drag_event ( touch_pos . x , touch_pos . y , ti . dwID ) ;
2014-09-22 05:50:48 +02:00
} else if ( ti . dwFlags & ( TOUCHEVENTF_UP | TOUCHEVENTF_DOWN ) ) {
2018-06-21 21:01:51 +02:00
_touch_event ( ti . dwFlags & TOUCHEVENTF_DOWN , touch_pos . x , touch_pos . y , ti . dwID ) ;
2014-09-22 05:50:48 +02:00
} ;
}
bHandled = TRUE ;
2017-03-05 16:44:50 +01:00
} else {
/* handle the error here */
2014-09-22 05:50:48 +02:00
}
memdelete_arr ( pInputs ) ;
2017-03-05 16:44:50 +01:00
} else {
2014-09-22 05:50:48 +02:00
/* handle the error here, probably out of memory */
}
if ( bHandled ) {
CloseTouchInputHandle ( ( HTOUCHINPUT ) lParam ) ;
return 0 ;
} ;
} break ;
2015-12-18 06:12:53 +01:00
case WM_DEVICECHANGE : {
2017-01-08 21:05:51 +01:00
joypad - > probe_joypads ( ) ;
2015-12-18 06:12:53 +01:00
} break ;
2016-05-09 15:18:08 +02:00
case WM_SETCURSOR : {
2017-03-05 16:44:50 +01:00
if ( LOWORD ( lParam ) = = HTCLIENT ) {
2022-07-29 19:41:29 +02:00
if ( window_has_focus & & ( mouse_mode = = MOUSE_MODE_HIDDEN | | mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) ) {
2016-05-09 15:18:08 +02:00
//Hide the cursor
2020-06-22 12:05:18 +02:00
if ( hCursor = = NULL ) {
2016-05-09 15:18:08 +02:00
hCursor = SetCursor ( NULL ) ;
2020-06-22 12:05:18 +02:00
} else {
2016-05-09 15:46:05 +02:00
SetCursor ( NULL ) ;
2020-06-22 12:05:18 +02:00
}
2017-03-05 16:44:50 +01:00
} else {
if ( hCursor ! = NULL ) {
2018-05-21 19:37:30 +02:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
2016-05-09 15:18:08 +02:00
hCursor = NULL ;
}
}
}
} break ;
2016-05-27 19:18:40 +02:00
case WM_DROPFILES : {
2017-08-21 21:15:36 +02:00
HDROP hDropInfo = ( HDROP ) wParam ;
2017-03-05 16:44:50 +01:00
const int buffsize = 4096 ;
2016-05-27 19:18:40 +02:00
wchar_t buf [ buffsize ] ;
2017-03-05 16:44:50 +01:00
int fcount = DragQueryFileW ( hDropInfo , 0xFFFFFFFF , NULL , 0 ) ;
2016-05-27 19:18:40 +02:00
Vector < String > files ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < fcount ; i + + ) {
2016-05-27 19:18:40 +02:00
DragQueryFileW ( hDropInfo , i , buf , buffsize ) ;
2017-03-05 16:44:50 +01:00
String file = buf ;
2016-05-27 19:18:40 +02:00
files . push_back ( file ) ;
}
if ( files . size ( ) & & main_loop ) {
2017-03-05 16:44:50 +01:00
main_loop - > drop_files ( files , 0 ) ;
2016-05-27 19:18:40 +02:00
}
} break ;
2014-02-10 02:10:30 +01:00
default : {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
} ;
} ;
}
2017-03-05 16:44:50 +01:00
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
OS_Windows * os_win = static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) ;
2014-02-10 02:10:30 +01:00
if ( os_win )
2017-03-05 16:44:50 +01:00
return os_win - > WndProc ( hWnd , uMsg , wParam , lParam ) ;
2014-02-10 02:10:30 +01:00
else
2017-03-05 16:44:50 +01:00
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
2014-02-10 02:10:30 +01:00
}
void OS_Windows : : process_key_events ( ) {
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < key_event_pos ; i + + ) {
2014-02-10 02:10:30 +01:00
KeyEvent & ke = key_event_buffer [ i ] ;
2017-03-05 16:44:50 +01:00
switch ( ke . uMsg ) {
2014-02-10 02:10:30 +01:00
case WM_CHAR : {
2020-05-18 14:47:54 +02:00
// extended keys should only be processed as WM_KEYDOWN message.
if ( ! KeyMappingWindows : : is_extended_key ( ke . wParam ) & & ( ( i = = 0 & & ke . uMsg = = WM_CHAR ) | | ( i > 0 & & key_event_buffer [ i - 1 ] . uMsg = = WM_CHAR ) ) ) {
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > k ;
k . instance ( ) ;
k - > set_shift ( ke . shift ) ;
k - > set_alt ( ke . alt ) ;
k - > set_control ( ke . control ) ;
k - > set_metakey ( ke . meta ) ;
k - > set_pressed ( true ) ;
2022-09-23 19:21:52 +02:00
k - > set_scancode ( KeyMappingWindows : : get_keysym ( MapVirtualKey ( ( ke . lParam > > 16 ) & 0xFF , MAPVK_VSC_TO_VK ) ) ) ;
2021-03-07 16:16:42 +01:00
k - > set_physical_scancode ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ;
2017-05-20 17:38:03 +02:00
k - > set_unicode ( ke . wParam ) ;
if ( k - > get_unicode ( ) & & gr_mem ) {
k - > set_alt ( false ) ;
k - > set_control ( false ) ;
2017-03-05 16:44:50 +01:00
}
2014-03-11 06:21:18 +01:00
2017-05-20 17:38:03 +02:00
if ( k - > get_unicode ( ) < 32 )
k - > set_unicode ( 0 ) ;
2014-03-11 06:21:18 +01:00
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( k ) ;
2017-03-05 16:44:50 +01:00
}
2014-02-10 02:10:30 +01:00
//do nothing
} break ;
case WM_KEYUP :
case WM_KEYDOWN : {
2017-05-20 17:38:03 +02:00
Ref < InputEventKey > k ;
k . instance ( ) ;
k - > set_shift ( ke . shift ) ;
k - > set_alt ( ke . alt ) ;
k - > set_control ( ke . control ) ;
k - > set_metakey ( ke . meta ) ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
k - > set_pressed ( ke . uMsg = = WM_KEYDOWN ) ;
2014-02-10 02:10:30 +01:00
2017-07-26 20:35:54 +02:00
if ( ( ke . lParam & ( 1 < < 24 ) ) & & ( ke . wParam = = VK_RETURN ) ) {
// Special case for Numpad Enter key
2017-08-06 15:26:07 +02:00
k - > set_scancode ( KEY_KP_ENTER ) ;
2017-07-26 20:35:54 +02:00
} else {
k - > set_scancode ( KeyMappingWindows : : get_keysym ( ke . wParam ) ) ;
}
2017-05-20 17:38:03 +02:00
2021-03-07 16:16:42 +01:00
k - > set_physical_scancode ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ;
2017-05-20 17:38:03 +02:00
if ( i + 1 < key_event_pos & & key_event_buffer [ i + 1 ] . uMsg = = WM_CHAR ) {
k - > set_unicode ( key_event_buffer [ i + 1 ] . wParam ) ;
}
if ( k - > get_unicode ( ) & & gr_mem ) {
k - > set_alt ( false ) ;
k - > set_control ( false ) ;
2014-02-10 02:10:30 +01:00
}
2017-05-20 17:38:03 +02:00
if ( k - > get_unicode ( ) < 32 )
k - > set_unicode ( 0 ) ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
k - > set_echo ( ( ke . uMsg = = WM_KEYDOWN & & ( ke . lParam & ( 1 < < 30 ) ) ) ) ;
2014-02-10 02:10:30 +01:00
2020-09-20 21:30:34 +02:00
input - > parse_input_event ( k ) ;
2014-02-10 02:10:30 +01:00
} break ;
}
}
2017-03-05 16:44:50 +01:00
key_event_pos = 0 ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
enum _MonitorDpiType {
MDT_Effective_DPI = 0 ,
MDT_Angular_DPI = 1 ,
MDT_Raw_DPI = 2 ,
MDT_Default = MDT_Effective_DPI
2016-06-01 17:43:30 +02:00
} ;
2017-03-05 16:44:50 +01:00
static int QueryDpiForMonitor ( HMONITOR hmon , _MonitorDpiType dpiType = MDT_Default ) {
2016-06-01 17:43:30 +02:00
int dpiX = 96 , dpiY = 96 ;
static HMODULE Shcore = NULL ;
2017-03-05 16:44:50 +01:00
typedef HRESULT ( WINAPI * GetDPIForMonitor_t ) ( HMONITOR hmonitor , _MonitorDpiType dpiType , UINT * dpiX , UINT * dpiY ) ;
2016-06-01 17:43:30 +02:00
static GetDPIForMonitor_t getDPIForMonitor = NULL ;
2017-03-05 16:44:50 +01:00
if ( Shcore = = NULL ) {
2016-06-01 17:43:30 +02:00
Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
getDPIForMonitor = Shcore ? ( GetDPIForMonitor_t ) GetProcAddress ( Shcore , " GetDpiForMonitor " ) : NULL ;
2017-03-05 16:44:50 +01:00
if ( ( Shcore = = NULL ) | | ( getDPIForMonitor = = NULL ) ) {
2016-06-01 17:43:30 +02:00
if ( Shcore )
FreeLibrary ( Shcore ) ;
Shcore = ( HMODULE ) INVALID_HANDLE_VALUE ;
}
}
UINT x = 0 , y = 0 ;
HRESULT hr = E_FAIL ;
2017-03-05 16:44:50 +01:00
if ( hmon & & ( Shcore ! = ( HMODULE ) INVALID_HANDLE_VALUE ) ) {
hr = getDPIForMonitor ( hmon , dpiType /*MDT_Effective_DPI*/ , & x , & y ) ;
if ( SUCCEEDED ( hr ) & & ( x > 0 ) & & ( y > 0 ) ) {
2016-06-01 17:43:30 +02:00
dpiX = ( int ) x ;
dpiY = ( int ) y ;
}
2017-03-05 16:44:50 +01:00
} else {
2016-06-01 17:43:30 +02:00
static int overallX = 0 , overallY = 0 ;
2017-03-05 16:44:50 +01:00
if ( overallX < = 0 | | overallY < = 0 ) {
2016-06-01 17:43:30 +02:00
HDC hdc = GetDC ( NULL ) ;
2017-03-05 16:44:50 +01:00
if ( hdc ) {
2016-06-01 17:43:30 +02:00
overallX = GetDeviceCaps ( hdc , LOGPIXELSX ) ;
overallY = GetDeviceCaps ( hdc , LOGPIXELSY ) ;
ReleaseDC ( NULL , hdc ) ;
}
}
2017-03-05 16:44:50 +01:00
if ( overallX > 0 & & overallY > 0 ) {
dpiX = overallX ;
dpiY = overallY ;
2016-06-01 17:43:30 +02:00
}
}
2017-03-05 16:44:50 +01:00
return ( dpiX + dpiY ) / 2 ;
2016-06-01 17:43:30 +02:00
}
2017-09-12 16:18:16 +02:00
typedef enum _SHC_PROCESS_DPI_AWARENESS {
2017-09-13 09:13:23 +02:00
SHC_PROCESS_DPI_UNAWARE = 0 ,
SHC_PROCESS_SYSTEM_DPI_AWARE = 1 ,
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
2017-09-12 16:18:16 +02:00
} SHC_PROCESS_DPI_AWARENESS ;
2018-07-19 23:58:15 +02:00
int OS_Windows : : get_current_video_driver ( ) const {
return video_driver_index ;
}
2018-01-03 18:26:44 +01:00
Error OS_Windows : : initialize ( const VideoMode & p_desired , int p_video_driver , int p_audio_driver ) {
2017-03-05 16:44:50 +01:00
main_loop = NULL ;
outside = true ;
window_has_focus = true ;
WNDCLASSEXW wc ;
2014-02-10 02:10:30 +01:00
2017-09-12 16:18:16 +02:00
if ( is_hidpi_allowed ( ) ) {
2017-09-13 09:13:23 +02:00
HMODULE Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
2017-09-12 16:18:16 +02:00
if ( Shcore ! = NULL ) {
2017-09-13 09:13:23 +02:00
typedef HRESULT ( WINAPI * SetProcessDpiAwareness_t ) ( SHC_PROCESS_DPI_AWARENESS ) ;
2017-09-12 16:18:16 +02:00
2017-09-13 09:13:23 +02:00
SetProcessDpiAwareness_t SetProcessDpiAwareness = ( SetProcessDpiAwareness_t ) GetProcAddress ( Shcore , " SetProcessDpiAwareness " ) ;
2017-09-12 16:18:16 +02:00
if ( SetProcessDpiAwareness ) {
SetProcessDpiAwareness ( SHC_PROCESS_SYSTEM_DPI_AWARE ) ;
}
}
}
2017-03-05 16:44:50 +01:00
video_mode = p_desired ;
2014-02-10 02:10:30 +01:00
//printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false");
RECT WindowRect ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
WindowRect . left = 0 ;
WindowRect . right = video_mode . width ;
WindowRect . top = 0 ;
WindowRect . bottom = video_mode . height ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
memset ( & wc , 0 , sizeof ( WNDCLASSEXW ) ) ;
wc . cbSize = sizeof ( WNDCLASSEXW ) ;
wc . style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS ;
2014-02-10 02:10:30 +01:00
wc . lpfnWndProc = ( WNDPROC ) : : WndProc ;
wc . cbClsExtra = 0 ;
2017-03-05 16:44:50 +01:00
wc . cbWndExtra = 0 ;
2014-02-10 02:10:30 +01:00
//wc.hInstance = hInstance;
wc . hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle ( NULL ) ;
2016-03-09 00:00:52 +01:00
wc . hIcon = LoadIcon ( NULL , IDI_WINLOGO ) ;
2017-03-05 16:44:50 +01:00
wc . hCursor = NULL ; //LoadCursor(NULL, IDC_ARROW);
2014-02-10 02:10:30 +01:00
wc . hbrBackground = NULL ;
2017-03-05 16:44:50 +01:00
wc . lpszMenuName = NULL ;
wc . lpszClassName = L " Engine " ;
2014-02-10 02:10:30 +01:00
if ( ! RegisterClassExW ( & wc ) ) {
2017-03-05 16:44:50 +01:00
MessageBox ( NULL , " Failed To Register The Window Class. " , " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2018-01-03 18:26:44 +01:00
return ERR_UNAVAILABLE ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
2022-05-23 14:02:12 +02:00
// Init TTS
2023-05-22 15:41:09 +02:00
bool tts_enabled = GLOBAL_GET ( " audio/general/text_to_speech " ) ;
if ( tts_enabled ) {
tts = memnew ( TTS_Windows ) ;
}
2022-05-23 14:02:12 +02:00
2018-07-27 18:32:27 +02:00
use_raw_input = true ;
RAWINPUTDEVICE Rid [ 1 ] ;
Rid [ 0 ] . usUsagePage = 0x01 ;
Rid [ 0 ] . usUsage = 0x02 ;
Rid [ 0 ] . dwFlags = 0 ;
Rid [ 0 ] . hwndTarget = 0 ;
if ( RegisterRawInputDevices ( Rid , 1 , sizeof ( Rid [ 0 ] ) ) = = FALSE ) {
//registration failed.
use_raw_input = false ;
}
2017-03-05 16:44:50 +01:00
pre_fs_valid = true ;
2014-02-10 02:10:30 +01:00
if ( video_mode . fullscreen ) {
2018-07-03 23:52:23 +02:00
/* this returns DPI unaware size, commenting
2014-02-10 02:10:30 +01:00
DEVMODE current ;
2017-03-05 16:44:50 +01:00
memset ( & current , 0 , sizeof ( current ) ) ;
2014-02-10 02:10:30 +01:00
EnumDisplaySettings ( NULL , ENUM_CURRENT_SETTINGS , & current ) ;
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
WindowRect . right = current . dmPelsWidth ;
2014-10-28 19:55:12 +01:00
WindowRect . bottom = current . dmPelsHeight ;
2014-02-10 02:10:30 +01:00
2018-07-03 23:52:23 +02:00
*/
2022-04-07 12:51:16 +02:00
// Get the primary monitor without providing hwnd
// Solution from https://devblogs.microsoft.com/oldnewthing/20070809-00/?p=25643
const POINT ptZero = { 0 , 0 } ;
EnumScreenData primary_data = { 0 , 0 , MonitorFromPoint ( ptZero , MONITOR_DEFAULTTOPRIMARY ) } ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcScreen , ( LPARAM ) & primary_data ) ;
EnumSizeData data = { 0 , primary_data . screen , Size2 ( ) } ;
2018-07-03 23:52:23 +02:00
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
WindowRect . right = data . size . width ;
WindowRect . bottom = data . size . height ;
2017-03-05 16:44:50 +01:00
/* DEVMODE dmScreenSettings;
2014-02-10 02:10:30 +01:00
memset ( & dmScreenSettings , 0 , sizeof ( dmScreenSettings ) ) ;
dmScreenSettings . dmSize = sizeof ( dmScreenSettings ) ;
dmScreenSettings . dmPelsWidth = video_mode . width ;
dmScreenSettings . dmPelsHeight = video_mode . height ;
dmScreenSettings . dmBitsPerPel = current . dmBitsPerPel ;
dmScreenSettings . dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT ;
LONG err = ChangeDisplaySettings ( & dmScreenSettings , CDS_FULLSCREEN ) ;
if ( err ! = DISP_CHANGE_SUCCESSFUL ) {
video_mode . fullscreen = false ;
2014-10-28 19:55:12 +01:00
} */
2017-03-05 16:44:50 +01:00
pre_fs_valid = false ;
2021-09-16 21:34:46 +02:00
// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.
// Save number of trails so we can restore when exiting, then turn off mouse trails
SystemParametersInfoA ( SPI_GETMOUSETRAILS , 0 , & restore_mouse_trails , 0 ) ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , 0 , 0 , 0 ) ;
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
DWORD dwExStyle ;
DWORD dwStyle ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( video_mode . fullscreen | | video_mode . borderless_window ) {
dwExStyle = WS_EX_APPWINDOW ;
dwStyle = WS_POPUP ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE ;
dwStyle = WS_OVERLAPPEDWINDOW ;
2014-03-27 13:09:18 +01:00
if ( ! video_mode . resizable ) {
dwStyle & = ~ WS_THICKFRAME ;
dwStyle & = ~ WS_MAXIMIZEBOX ;
}
2014-02-10 02:10:30 +01:00
}
AdjustWindowRectEx ( & WindowRect , dwStyle , FALSE , dwExStyle ) ;
2018-10-04 15:38:52 +02:00
char * windowid ;
# ifdef MINGW_ENABLED
windowid = getenv ( " GODOT_WINDOWID " ) ;
# else
size_t len ;
_dupenv_s ( & windowid , & len , " GODOT_WINDOWID " ) ;
# endif
2014-02-10 02:10:30 +01:00
if ( windowid ) {
2017-03-05 16:44:50 +01:00
// strtoull on mingw
# ifdef MINGW_ENABLED
2014-02-10 02:10:30 +01:00
hWnd = ( HWND ) strtoull ( windowid , NULL , 0 ) ;
2017-03-05 16:44:50 +01:00
# else
2014-02-10 02:10:30 +01:00
hWnd = ( HWND ) _strtoui64 ( windowid , NULL , 0 ) ;
2017-03-05 16:44:50 +01:00
# endif
2018-10-04 15:38:52 +02:00
free ( windowid ) ;
2014-02-10 02:10:30 +01:00
SetLastError ( 0 ) ;
user_proc = ( WNDPROC ) GetWindowLongPtr ( hWnd , GWLP_WNDPROC ) ;
SetWindowLongPtr ( hWnd , GWLP_WNDPROC , ( LONG_PTR ) ( WNDPROC ) : : WndProc ) ;
DWORD le = GetLastError ( ) ;
if ( user_proc = = 0 & & le ! = 0 ) {
printf ( " Error setting WNDPROC: %li \n " , le ) ;
} ;
2018-10-17 08:46:50 +02:00
GetWindowLongPtr ( hWnd , GWLP_WNDPROC ) ;
2014-02-10 02:10:30 +01:00
RECT rect ;
if ( ! GetClientRect ( hWnd , & rect ) ) {
2017-03-05 16:44:50 +01:00
MessageBoxW ( NULL , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2018-01-03 18:26:44 +01:00
return ERR_UNAVAILABLE ;
2014-02-10 02:10:30 +01:00
} ;
video_mode . width = rect . right ;
video_mode . height = rect . bottom ;
video_mode . fullscreen = false ;
} else {
2017-09-06 23:50:18 +02:00
hWnd = CreateWindowExW (
dwExStyle ,
L " Engine " , L " " ,
dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN ,
( GetSystemMetrics ( SM_CXSCREEN ) - WindowRect . right ) / 2 ,
( GetSystemMetrics ( SM_CYSCREEN ) - WindowRect . bottom ) / 2 ,
WindowRect . right - WindowRect . left ,
WindowRect . bottom - WindowRect . top ,
NULL , NULL , hInstance , NULL ) ;
if ( ! hWnd ) {
2017-03-05 16:44:50 +01:00
MessageBoxW ( NULL , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2018-01-03 18:26:44 +01:00
return ERR_UNAVAILABLE ;
2014-02-10 02:10:30 +01:00
}
} ;
2016-03-09 00:00:52 +01:00
2017-12-27 20:51:19 +01:00
if ( video_mode . always_on_top ) {
SetWindowPos ( hWnd , video_mode . always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST , 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE ) ;
}
2020-05-19 22:28:57 +02:00
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available ) {
2020-05-05 13:53:30 +02:00
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wtlc ) ;
wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-18 13:27:02 +02:00
wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2020-05-05 13:53:30 +02:00
wtlc . lcPktMode = 0 ;
wtlc . lcOutOrgX = 0 ;
wtlc . lcOutExtX = wtlc . lcInExtX ;
wtlc . lcOutOrgY = 0 ;
wtlc . lcOutExtY = - wtlc . lcInExtY ;
wtctx = wintab_WTOpen ( hWnd , & wtlc , false ) ;
if ( wtctx ) {
wintab_WTEnable ( wtctx , true ) ;
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
min_pressure = int ( pressure . axMin ) ;
max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
} else {
2020-05-07 14:51:19 +02:00
print_verbose ( " WinTab context creation failed. " ) ;
2020-05-05 13:53:30 +02:00
}
} else {
wtctx = 0 ;
}
last_pressure = 0 ;
last_pressure_update = 0 ;
last_tilt = Vector2 ( ) ;
2022-06-18 13:27:02 +02:00
last_pen_inverted = false ;
2020-05-05 13:53:30 +02:00
2016-12-21 06:29:58 +01:00
# if defined(OPENGL_ENABLED)
2018-08-25 00:04:25 +02:00
bool gles3_context = true ;
2018-03-06 14:53:30 +01:00
if ( p_video_driver = = VIDEO_DRIVER_GLES2 ) {
2018-08-25 00:04:25 +02:00
gles3_context = false ;
}
2016-12-21 06:29:58 +01:00
2018-08-25 00:04:25 +02:00
bool editor = Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
bool gl_initialization_error = false ;
gl_context = NULL ;
while ( ! gl_context ) {
2019-02-12 15:43:54 +01:00
gl_context = memnew ( ContextGL_Windows ( hWnd , gles3_context ) ) ;
2018-08-25 00:04:25 +02:00
if ( gl_context - > initialize ( ) ! = OK ) {
memdelete ( gl_context ) ;
gl_context = NULL ;
2019-03-05 13:46:51 +01:00
if ( GLOBAL_GET ( " rendering/quality/driver/fallback_to_gles2 " ) | | editor ) {
2018-08-25 00:04:25 +02:00
if ( p_video_driver = = VIDEO_DRIVER_GLES2 ) {
gl_initialization_error = true ;
break ;
}
p_video_driver = VIDEO_DRIVER_GLES2 ;
gles3_context = false ;
} else {
gl_initialization_error = true ;
break ;
}
}
}
2016-12-21 06:29:58 +01:00
2018-08-25 00:04:25 +02:00
while ( true ) {
if ( gles3_context ) {
if ( RasterizerGLES3 : : is_viable ( ) = = OK ) {
RasterizerGLES3 : : register_config ( ) ;
RasterizerGLES3 : : make_current ( ) ;
break ;
} else {
2019-03-05 13:46:51 +01:00
if ( GLOBAL_GET ( " rendering/quality/driver/fallback_to_gles2 " ) | | editor ) {
2018-08-25 00:04:25 +02:00
p_video_driver = VIDEO_DRIVER_GLES2 ;
gles3_context = false ;
continue ;
} else {
gl_initialization_error = true ;
break ;
}
}
} else {
if ( RasterizerGLES2 : : is_viable ( ) = = OK ) {
RasterizerGLES2 : : register_config ( ) ;
RasterizerGLES2 : : make_current ( ) ;
break ;
} else {
gl_initialization_error = true ;
break ;
}
}
}
if ( gl_initialization_error ) {
OS : : get_singleton ( ) - > alert ( " Your video card driver does not support any of the supported OpenGL versions. \n "
2021-08-05 18:23:33 +02:00
" Please update your drivers or if you have a very old or integrated GPU, upgrade it. " ,
2018-08-25 00:04:25 +02:00
" Unable to initialize Video driver " ) ;
return ERR_UNAVAILABLE ;
2018-03-06 14:53:30 +01:00
}
2017-12-08 17:58:28 +01:00
2018-08-25 00:04:25 +02:00
video_driver_index = p_video_driver ;
2018-07-19 23:58:15 +02:00
2017-12-08 17:58:28 +01:00
gl_context - > set_use_vsync ( video_mode . use_vsync ) ;
2019-11-07 03:18:55 +01:00
set_vsync_via_compositor ( video_mode . vsync_via_compositor ) ;
2014-02-10 02:10:30 +01:00
# endif
2018-11-01 19:22:15 +01:00
visual_server = memnew ( VisualServerRaster ) ;
2017-06-10 15:15:33 +02:00
if ( get_render_thread_mode ( ) ! = RENDER_THREAD_UNSAFE ) {
visual_server = memnew ( VisualServerWrapMT ( visual_server , get_render_thread_mode ( ) = = RENDER_SEPARATE_THREAD ) ) ;
2017-01-14 12:26:56 +01:00
}
2014-02-10 02:10:30 +01:00
2016-03-09 00:00:52 +01:00
visual_server - > init ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
input = memnew ( InputDefault ) ;
joypad = memnew ( JoypadWindows ( input , & hWnd ) ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
power_manager = memnew ( PowerWindows ) ;
2016-07-23 13:15:55 +02:00
2017-09-13 18:34:22 +02:00
AudioDriverManager : : initialize ( p_audio_driver ) ;
2014-02-10 02:10:30 +01:00
TRACKMOUSEEVENT tme ;
2017-03-05 16:44:50 +01:00
tme . cbSize = sizeof ( TRACKMOUSEEVENT ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
tme . dwHoverTime = HOVER_DEFAULT ;
2014-02-10 02:10:30 +01:00
TrackMouseEvent ( & tme ) ;
2017-12-01 21:06:42 +01:00
RegisterTouchWindow ( hWnd , 0 ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
DragAcceptFiles ( hWnd , true ) ;
2016-05-27 19:18:40 +02:00
2016-07-01 15:31:37 +02:00
move_timer_id = 1 ;
2017-11-29 01:49:36 +01:00
if ( ! is_no_window_mode_enabled ( ) ) {
ShowWindow ( hWnd , SW_SHOW ) ; // Show The Window
SetForegroundWindow ( hWnd ) ; // Slightly Higher Priority
SetFocus ( hWnd ) ; // Sets Keyboard Focus To
}
2018-01-03 18:26:44 +01:00
2019-06-04 10:21:29 +02:00
if ( p_desired . layered ) {
2017-12-10 19:38:26 +01:00
set_window_per_pixel_transparency_enabled ( true ) ;
}
2018-06-07 22:16:57 +02:00
// IME
im_himc = ImmGetContext ( hWnd ) ;
ImmReleaseContext ( hWnd , im_himc ) ;
im_position = Vector2 ( ) ;
set_ime_active ( false ) ;
2022-03-29 09:01:31 +02:00
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) & & ! OS : : get_singleton ( ) - > is_in_low_processor_usage_mode ( ) ) {
// Increase priority for projects that are not in low-processor mode (typically games)
// to reduce the risk of frame stuttering.
// This is not done for the editor to prevent importers or resource bakers
// from making the system unresponsive.
2019-01-22 20:03:49 +01:00
SetPriorityClass ( GetCurrentProcess ( ) , ABOVE_NORMAL_PRIORITY_CLASS ) ;
DWORD index = 0 ;
HANDLE handle = AvSetMmThreadCharacteristics ( " Games " , & index ) ;
if ( handle )
AvSetMmThreadPriority ( handle , AVRT_PRIORITY_CRITICAL ) ;
// This is needed to make sure that background work does not starve the main thread.
// This is only setting priority of this thread, not the whole process.
SetThreadPriority ( GetCurrentThread ( ) , THREAD_PRIORITY_TIME_CRITICAL ) ;
}
2019-02-24 16:54:03 +01:00
update_real_mouse_position ( ) ;
2018-01-03 18:26:44 +01:00
return OK ;
2014-02-10 02:10:30 +01:00
}
2021-09-24 14:07:44 +02:00
bool OS_Windows : : is_offscreen_gl_available ( ) const {
# if defined(OPENGL_ENABLED)
return gl_context - > is_offscreen_available ( ) ;
# else
return false ;
# endif
}
void OS_Windows : : set_offscreen_gl_current ( bool p_current ) {
# if defined(OPENGL_ENABLED)
if ( p_current ) {
return gl_context - > make_offscreen_current ( ) ;
} else {
return gl_context - > release_offscreen_current ( ) ;
}
# endif
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_clipboard ( const String & p_text ) {
2019-04-27 14:00:28 +02:00
// Convert LF line endings to CRLF in clipboard content
// Otherwise, line endings won't be visible when pasted in other software
2020-07-20 09:42:32 +02:00
String text = p_text . replace ( " \r \n " , " \n " ) . replace ( " \n " , " \r \n " ) ; // avoid \r\r\n
2019-04-27 14:00:28 +02:00
2014-02-10 02:10:30 +01:00
if ( ! OpenClipboard ( hWnd ) ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_MSG ( " Unable to open clipboard. " ) ;
}
2014-02-10 02:10:30 +01:00
EmptyClipboard ( ) ;
2019-04-27 14:00:28 +02:00
HGLOBAL mem = GlobalAlloc ( GMEM_MOVEABLE , ( text . length ( ) + 1 ) * sizeof ( CharType ) ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( mem = = NULL , " Unable to allocate memory for clipboard contents. " ) ;
2014-02-10 02:10:30 +01:00
LPWSTR lptstrCopy = ( LPWSTR ) GlobalLock ( mem ) ;
2019-04-27 14:00:28 +02:00
memcpy ( lptstrCopy , text . c_str ( ) , ( text . length ( ) + 1 ) * sizeof ( CharType ) ) ;
2014-02-10 02:10:30 +01:00
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_UNICODETEXT , mem ) ;
// set the CF_TEXT version (not needed?)
2019-04-27 14:00:28 +02:00
CharString utf8 = text . utf8 ( ) ;
2014-02-10 02:10:30 +01:00
mem = GlobalAlloc ( GMEM_MOVEABLE , utf8 . length ( ) + 1 ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( mem = = NULL , " Unable to allocate memory for clipboard contents. " ) ;
2014-02-10 02:10:30 +01:00
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
memcpy ( ptr , utf8 . get_data ( ) , utf8 . length ( ) ) ;
ptr [ utf8 . length ( ) ] = 0 ;
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_TEXT , mem ) ;
CloseClipboard ( ) ;
} ;
String OS_Windows : : get_clipboard ( ) const {
String ret ;
if ( ! OpenClipboard ( hWnd ) ) {
2019-08-09 06:49:33 +02:00
ERR_FAIL_V_MSG ( " " , " Unable to open clipboard. " ) ;
2014-02-10 02:10:30 +01:00
} ;
if ( IsClipboardFormatAvailable ( CF_UNICODETEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
if ( mem ! = NULL ) {
LPWSTR ptr = ( LPWSTR ) GlobalLock ( mem ) ;
if ( ptr ! = NULL ) {
2017-03-05 16:44:50 +01:00
ret = String ( ( CharType * ) ptr ) ;
2014-02-10 02:10:30 +01:00
GlobalUnlock ( mem ) ;
} ;
} ;
} else if ( IsClipboardFormatAvailable ( CF_TEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
if ( mem ! = NULL ) {
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
if ( ptr ! = NULL ) {
2017-03-05 16:44:50 +01:00
ret . parse_utf8 ( ( const char * ) ptr ) ;
2014-02-10 02:10:30 +01:00
GlobalUnlock ( mem ) ;
} ;
} ;
} ;
CloseClipboard ( ) ;
return ret ;
} ;
void OS_Windows : : delete_main_loop ( ) {
if ( main_loop )
memdelete ( main_loop ) ;
2017-03-05 16:44:50 +01:00
main_loop = NULL ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_main_loop ( MainLoop * p_main_loop ) {
2014-02-10 02:10:30 +01:00
input - > set_main_loop ( p_main_loop ) ;
2017-03-05 16:44:50 +01:00
main_loop = p_main_loop ;
2014-02-10 02:10:30 +01:00
}
void OS_Windows : : finalize ( ) {
2018-07-14 14:11:28 +02:00
# ifdef WINMIDI_ENABLED
driver_midi . close ( ) ;
# endif
2017-03-05 16:44:50 +01:00
if ( main_loop )
2014-02-10 02:10:30 +01:00
memdelete ( main_loop ) ;
2017-03-05 16:44:50 +01:00
main_loop = NULL ;
2015-11-14 15:14:17 +01:00
2017-01-08 21:05:51 +01:00
memdelete ( joypad ) ;
2015-11-14 15:14:17 +01:00
memdelete ( input ) ;
2017-11-30 19:55:38 +01:00
touch_state . clear ( ) ;
2015-11-14 15:14:17 +01:00
2022-05-23 09:33:42 +02:00
icon . unref ( ) ;
2019-07-24 19:57:59 +02:00
cursors_cache . clear ( ) ;
2014-02-10 02:10:30 +01:00
visual_server - > finish ( ) ;
memdelete ( visual_server ) ;
# ifdef OPENGL_ENABLED
if ( gl_context )
memdelete ( gl_context ) ;
# endif
if ( user_proc ) {
SetWindowLongPtr ( hWnd , GWLP_WNDPROC , ( LONG_PTR ) user_proc ) ;
} ;
2021-09-16 21:34:46 +02:00
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
}
2022-05-23 14:02:12 +02:00
if ( tts ) {
memdelete ( tts ) ;
}
CoUninitialize ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-02-19 20:42:36 +01:00
2014-02-10 02:10:30 +01:00
void OS_Windows : : finalize_core ( ) {
2018-05-01 09:47:34 +02:00
timeEndPeriod ( 1 ) ;
2014-02-10 02:10:30 +01:00
memdelete ( process_map ) ;
2018-09-13 15:47:00 +02:00
NetSocketPosix : : cleanup ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : alert ( const String & p_alert , const String & p_title ) {
2020-09-23 13:43:51 +02:00
if ( is_no_window_mode_enabled ( ) ) {
print_line ( " ALERT: " + p_title + " : " + p_alert ) ;
return ;
}
MessageBoxW ( NULL , p_alert . c_str ( ) , p_title . c_str ( ) , MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL ) ;
2014-02-10 02:10:30 +01:00
}
void OS_Windows : : set_mouse_mode ( MouseMode p_mode ) {
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = p_mode )
2014-02-10 02:10:30 +01:00
return ;
2018-11-09 20:30:08 +01:00
2017-03-05 16:44:50 +01:00
mouse_mode = p_mode ;
2020-06-22 12:05:18 +02:00
_set_mouse_mode_impl ( p_mode ) ;
2018-11-09 20:30:08 +01:00
}
void OS_Windows : : _set_mouse_mode_impl ( MouseMode p_mode ) {
2022-07-29 19:41:29 +02:00
if ( p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_CONFINED | | p_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
// Mouse is grabbed (captured or confined).
2014-02-10 02:10:30 +01:00
RECT clipRect ;
GetClientRect ( hWnd , & clipRect ) ;
2017-03-05 16:44:50 +01:00
ClientToScreen ( hWnd , ( POINT * ) & clipRect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & clipRect . right ) ;
2014-02-10 02:10:30 +01:00
ClipCursor ( & clipRect ) ;
2018-11-09 20:30:08 +01:00
if ( p_mode = = MOUSE_MODE_CAPTURED ) {
center = Point2i ( video_mode . width / 2 , video_mode . height / 2 ) ;
POINT pos = { ( int ) center . x , ( int ) center . y } ;
ClientToScreen ( hWnd , & pos ) ;
2017-01-25 19:21:41 +01:00
SetCursorPos ( pos . x , pos . y ) ;
2018-11-09 20:30:08 +01:00
SetCapture ( hWnd ) ;
}
2014-02-10 02:10:30 +01:00
} else {
2022-07-29 19:41:29 +02:00
// Mouse is free to move around (not captured or confined).
2014-02-10 02:10:30 +01:00
ReleaseCapture ( ) ;
ClipCursor ( NULL ) ;
}
2022-07-29 19:41:29 +02:00
if ( p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_HIDDEN | | p_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2020-06-22 12:05:18 +02:00
if ( hCursor = = NULL ) {
hCursor = SetCursor ( NULL ) ;
} else {
SetCursor ( NULL ) ;
}
2016-10-21 00:01:28 +02:00
} else {
2018-04-09 02:59:51 +02:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
2016-10-21 00:01:28 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
OS_Windows : : MouseMode OS_Windows : : get_mouse_mode ( ) const {
2014-02-10 02:10:30 +01:00
return mouse_mode ;
}
2017-09-10 15:37:49 +02:00
void OS_Windows : : warp_mouse_position ( const Point2 & p_to ) {
2017-03-05 16:44:50 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
old_x = p_to . x ;
old_y = p_to . y ;
2014-09-20 02:01:41 +02:00
} else {
2015-02-14 23:22:06 +01:00
POINT p ;
2017-03-05 16:44:50 +01:00
p . x = p_to . x ;
p . y = p_to . y ;
ClientToScreen ( hWnd , & p ) ;
2015-02-14 23:22:06 +01:00
2017-03-05 16:44:50 +01:00
SetCursorPos ( p . x , p . y ) ;
2014-09-20 02:01:41 +02:00
}
}
2017-03-29 17:29:38 +02:00
Point2 OS_Windows : : get_mouse_position ( ) const {
2014-02-10 02:10:30 +01:00
return Point2 ( old_x , old_y ) ;
}
2019-02-24 16:54:03 +01:00
void OS_Windows : : update_real_mouse_position ( ) {
POINT mouse_pos ;
if ( GetCursorPos ( & mouse_pos ) & & ScreenToClient ( hWnd , & mouse_pos ) ) {
if ( mouse_pos . x > 0 & & mouse_pos . y > 0 & & mouse_pos . x < = video_mode . width & & mouse_pos . y < = video_mode . height ) {
old_x = mouse_pos . x ;
old_y = mouse_pos . y ;
old_invalid = false ;
input - > set_mouse_position ( Point2i ( mouse_pos . x , mouse_pos . y ) ) ;
}
}
}
2014-02-10 02:10:30 +01:00
int OS_Windows : : get_mouse_button_state ( ) const {
return last_button_state ;
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_title ( const String & p_title ) {
SetWindowTextW ( hWnd , p_title . c_str ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2020-06-29 11:31:13 +02:00
void OS_Windows : : set_window_mouse_passthrough ( const PoolVector2Array & p_region ) {
mpath . clear ( ) ;
for ( int i = 0 ; i < p_region . size ( ) ; i + + ) {
mpath . push_back ( p_region [ i ] ) ;
}
_update_window_mouse_passthrough ( ) ;
}
void OS_Windows : : _update_window_mouse_passthrough ( ) {
if ( mpath . size ( ) = = 0 ) {
SetWindowRgn ( hWnd , NULL , TRUE ) ;
} else {
POINT * points = ( POINT * ) memalloc ( sizeof ( POINT ) * mpath . size ( ) ) ;
for ( int i = 0 ; i < mpath . size ( ) ; i + + ) {
if ( video_mode . borderless_window ) {
points [ i ] . x = mpath [ i ] . x ;
points [ i ] . y = mpath [ i ] . y ;
} else {
points [ i ] . x = mpath [ i ] . x + GetSystemMetrics ( SM_CXSIZEFRAME ) ;
points [ i ] . y = mpath [ i ] . y + GetSystemMetrics ( SM_CYSIZEFRAME ) + GetSystemMetrics ( SM_CYCAPTION ) ;
}
}
HRGN region = CreatePolygonRgn ( points , mpath . size ( ) , ALTERNATE ) ;
SetWindowRgn ( hWnd , region , TRUE ) ;
DeleteObject ( region ) ;
memfree ( points ) ;
}
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_video_mode ( const VideoMode & p_video_mode , int p_screen ) {
2014-02-10 02:10:30 +01:00
}
2014-12-17 02:31:57 +01:00
2014-02-10 02:10:30 +01:00
OS : : VideoMode OS_Windows : : get_video_mode ( int p_screen ) const {
return video_mode ;
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : get_fullscreen_mode_list ( List < VideoMode > * p_list , int p_screen ) const {
2014-02-10 02:10:30 +01:00
}
2017-08-14 18:53:53 +02:00
static BOOL CALLBACK _MonitorEnumProcCount ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
int * data = ( int * ) dwData ;
( * data ) + + ;
return TRUE ;
}
2015-03-22 23:00:50 +01:00
int OS_Windows : : get_screen_count ( ) const {
2017-08-14 18:53:53 +02:00
int data = 0 ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcCount , ( LPARAM ) & data ) ;
return data ;
2015-03-22 23:00:50 +01:00
}
2017-08-14 18:53:53 +02:00
int OS_Windows : : get_current_screen ( ) const {
EnumScreenData data = { 0 , 0 , MonitorFromWindow ( hWnd , MONITOR_DEFAULTTONEAREST ) } ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcScreen , ( LPARAM ) & data ) ;
return data . screen ;
}
void OS_Windows : : set_current_screen ( int p_screen ) {
2022-07-19 10:39:21 +02:00
if ( video_mode . fullscreen ) {
int cs = get_current_screen ( ) ;
if ( cs = = p_screen ) {
return ;
}
Point2 pos = get_screen_position ( p_screen ) ;
Size2 size = get_screen_size ( p_screen ) ;
MoveWindow ( hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
} else {
Vector2 ofs = get_window_position ( ) - get_screen_position ( get_current_screen ( ) ) ;
set_window_position ( ofs + get_screen_position ( p_screen ) ) ;
}
2015-03-22 23:00:50 +01:00
}
2017-08-14 18:53:53 +02:00
static BOOL CALLBACK _MonitorEnumProcPos ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumPosData * data = ( EnumPosData * ) dwData ;
if ( data - > count = = data - > screen ) {
data - > pos . x = lprcMonitor - > left ;
data - > pos . y = lprcMonitor - > top ;
}
data - > count + + ;
return TRUE ;
}
2017-03-05 16:44:50 +01:00
Point2 OS_Windows : : get_screen_position ( int p_screen ) const {
2017-08-20 21:12:29 +02:00
EnumPosData data = { 0 , p_screen = = - 1 ? get_current_screen ( ) : p_screen , Point2 ( ) } ;
2017-08-14 18:53:53 +02:00
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcPos , ( LPARAM ) & data ) ;
return data . pos ;
2015-03-22 23:00:50 +01:00
}
2017-08-14 18:53:53 +02:00
2017-03-05 16:44:50 +01:00
Size2 OS_Windows : : get_screen_size ( int p_screen ) const {
2017-08-20 21:12:29 +02:00
EnumSizeData data = { 0 , p_screen = = - 1 ? get_current_screen ( ) : p_screen , Size2 ( ) } ;
2017-08-14 18:53:53 +02:00
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
return data . size ;
}
typedef struct {
int count ;
int screen ;
int dpi ;
} EnumDpiData ;
2022-03-06 00:34:56 +01:00
typedef struct {
int count ;
int screen ;
float rate ;
} EnumRefreshRateData ;
2017-08-14 18:53:53 +02:00
static BOOL CALLBACK _MonitorEnumProcDpi ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumDpiData * data = ( EnumDpiData * ) dwData ;
if ( data - > count = = data - > screen ) {
data - > dpi = QueryDpiForMonitor ( hMonitor ) ;
}
data - > count + + ;
return TRUE ;
2016-05-29 18:40:00 +02:00
}
2022-03-06 00:34:56 +01:00
static BOOL CALLBACK _MonitorEnumProcRefreshRate ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumRefreshRateData * data = ( EnumRefreshRateData * ) dwData ;
if ( data - > count = = data - > screen ) {
MONITORINFOEXW minfo ;
memset ( & minfo , 0 , sizeof ( minfo ) ) ;
minfo . cbSize = sizeof ( minfo ) ;
GetMonitorInfoW ( hMonitor , & minfo ) ;
DEVMODEW dm ;
memset ( & dm , 0 , sizeof ( dm ) ) ;
dm . dmSize = sizeof ( dm ) ;
EnumDisplaySettingsW ( minfo . szDevice , ENUM_CURRENT_SETTINGS , & dm ) ;
data - > rate = dm . dmDisplayFrequency ;
}
data - > count + + ;
return TRUE ;
}
2016-05-29 18:40:00 +02:00
int OS_Windows : : get_screen_dpi ( int p_screen ) const {
2022-03-06 00:34:56 +01:00
EnumDpiData data = {
0 ,
p_screen = = - 1 ? get_current_screen ( ) : p_screen ,
72 ,
} ;
2017-08-14 18:53:53 +02:00
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcDpi , ( LPARAM ) & data ) ;
return data . dpi ;
2015-03-22 23:00:50 +01:00
}
2017-08-14 18:53:53 +02:00
2022-03-06 00:34:56 +01:00
float OS_Windows : : get_screen_refresh_rate ( int p_screen ) const {
EnumRefreshRateData data = {
0 ,
p_screen = = - 1 ? get_current_screen ( ) : p_screen ,
OS : : get_singleton ( ) - > SCREEN_REFRESH_RATE_FALLBACK ,
} ;
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcRefreshRate , ( LPARAM ) & data ) ;
return data . rate ;
}
2017-03-05 16:44:50 +01:00
Point2 OS_Windows : : get_window_position ( ) const {
2018-11-30 04:36:48 +01:00
if ( minimized ) {
return last_pos ;
}
2015-03-22 23:00:50 +01:00
RECT r ;
2017-03-05 16:44:50 +01:00
GetWindowRect ( hWnd , & r ) ;
return Point2 ( r . left , r . top ) ;
2015-03-22 23:00:50 +01:00
}
2017-08-14 18:53:53 +02:00
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_position ( const Point2 & p_position ) {
2021-05-04 14:28:27 +02:00
if ( video_mode . fullscreen )
return ;
2015-03-22 23:00:50 +01:00
RECT r ;
2017-03-05 16:44:50 +01:00
GetWindowRect ( hWnd , & r ) ;
MoveWindow ( hWnd , p_position . x , p_position . y , r . right - r . left , r . bottom - r . top , TRUE ) ;
2018-09-05 23:45:23 +02:00
// Don't let the mouse leave the window when moved
2022-07-29 19:41:29 +02:00
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2018-09-05 23:45:23 +02:00
RECT rect ;
GetClientRect ( hWnd , & rect ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . right ) ;
ClipCursor ( & rect ) ;
}
2018-11-30 04:36:48 +01:00
last_pos = p_position ;
2019-02-24 16:54:03 +01:00
update_real_mouse_position ( ) ;
2015-03-22 23:00:50 +01:00
}
2019-06-13 14:31:08 +02:00
2017-03-05 16:44:50 +01:00
Size2 OS_Windows : : get_window_size ( ) const {
2018-11-30 04:36:48 +01:00
if ( minimized ) {
return Size2 ( video_mode . width , video_mode . height ) ;
}
2015-03-22 23:00:50 +01:00
RECT r ;
2018-10-17 08:46:50 +02:00
if ( GetClientRect ( hWnd , & r ) ) { // Only area inside of window border
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
2015-03-22 23:00:50 +01:00
}
2019-06-13 14:31:08 +02:00
Size2 OS_Windows : : get_max_window_size ( ) const {
return max_size ;
}
Size2 OS_Windows : : get_min_window_size ( ) const {
return min_size ;
}
void OS_Windows : : set_min_window_size ( const Size2 p_size ) {
if ( ( p_size ! = Size2 ( ) ) & & ( max_size ! = Size2 ( ) ) & & ( ( p_size . x > max_size . x ) | | ( p_size . y > max_size . y ) ) ) {
2019-07-30 14:50:52 +02:00
ERR_PRINT ( " Minimum window size can't be larger than maximum window size! " ) ;
2019-06-13 14:31:08 +02:00
return ;
}
min_size = p_size ;
}
void OS_Windows : : set_max_window_size ( const Size2 p_size ) {
if ( ( p_size ! = Size2 ( ) ) & & ( ( p_size . x < min_size . x ) | | ( p_size . y < min_size . y ) ) ) {
2019-07-30 14:50:52 +02:00
ERR_PRINT ( " Maximum window size can't be smaller than minimum window size! " ) ;
2019-06-13 14:31:08 +02:00
return ;
}
max_size = p_size ;
}
2018-02-12 18:17:29 +01:00
Size2 OS_Windows : : get_real_window_size ( ) const {
RECT r ;
2018-10-17 08:46:50 +02:00
if ( GetWindowRect ( hWnd , & r ) ) { // Includes area of the window border
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
2018-02-12 18:17:29 +01:00
}
2019-06-13 14:31:08 +02:00
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_size ( const Size2 p_size ) {
2018-07-03 23:52:23 +02:00
int w = p_size . width ;
int h = p_size . height ;
video_mode . width = w ;
video_mode . height = h ;
2015-03-24 00:47:53 +01:00
if ( video_mode . fullscreen ) {
return ;
}
RECT rect ;
2017-03-05 16:44:50 +01:00
GetWindowRect ( hWnd , & rect ) ;
2015-03-24 00:47:53 +01:00
2018-10-06 22:20:41 +02:00
if ( ! video_mode . borderless_window ) {
2017-07-10 02:48:22 +02:00
RECT crect ;
GetClientRect ( hWnd , & crect ) ;
2015-03-24 00:47:53 +01:00
2017-07-10 02:48:22 +02:00
w + = ( rect . right - rect . left ) - ( crect . right - crect . left ) ;
h + = ( rect . bottom - rect . top ) - ( crect . bottom - crect . top ) ;
}
2015-03-24 00:47:53 +01:00
2017-07-10 02:48:22 +02:00
MoveWindow ( hWnd , rect . left , rect . top , w , h , TRUE ) ;
2018-05-21 00:07:25 +02:00
// Don't let the mouse leave the window when resizing to a smaller resolution
2022-07-29 19:41:29 +02:00
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2019-06-15 12:11:30 +02:00
RECT crect ;
GetClientRect ( hWnd , & crect ) ;
ClientToScreen ( hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
2018-05-21 00:07:25 +02:00
}
2015-03-22 23:00:50 +01:00
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_fullscreen ( bool p_enabled ) {
if ( video_mode . fullscreen = = p_enabled )
2015-03-22 23:00:50 +01:00
return ;
2017-12-10 19:38:26 +01:00
if ( layered_window )
set_window_per_pixel_transparency_enabled ( false ) ;
2015-03-22 23:00:50 +01:00
if ( p_enabled ) {
2019-10-27 16:44:47 +01:00
was_maximized = maximized ;
2015-03-24 00:47:53 +01:00
if ( pre_fs_valid ) {
2017-03-05 16:44:50 +01:00
GetWindowRect ( hWnd , & pre_fs_rect ) ;
2015-03-24 00:47:53 +01:00
}
2015-03-22 23:00:50 +01:00
int cs = get_current_screen ( ) ;
Point2 pos = get_screen_position ( cs ) ;
Size2 size = get_screen_size ( cs ) ;
2017-03-05 16:44:50 +01:00
video_mode . fullscreen = true ;
2015-03-22 23:00:50 +01:00
2017-07-10 02:48:22 +02:00
_update_window_style ( false ) ;
MoveWindow ( hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
2021-09-16 21:34:46 +02:00
SystemParametersInfoA ( SPI_GETMOUSETRAILS , 0 , & restore_mouse_trails , 0 ) ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , 0 , 0 , 0 ) ;
}
2015-03-22 23:00:50 +01:00
} else {
2015-03-24 00:47:53 +01:00
RECT rect ;
2017-07-10 02:48:22 +02:00
video_mode . fullscreen = false ;
2015-03-24 00:47:53 +01:00
if ( pre_fs_valid ) {
2017-03-05 16:44:50 +01:00
rect = pre_fs_rect ;
2015-03-24 00:47:53 +01:00
} else {
2017-03-05 16:44:50 +01:00
rect . left = 0 ;
rect . right = video_mode . width ;
rect . top = 0 ;
rect . bottom = video_mode . height ;
2015-03-24 00:47:53 +01:00
}
2019-10-27 16:44:47 +01:00
_update_window_style ( false , was_maximized ) ;
2015-03-24 00:47:53 +01:00
2017-07-10 02:48:22 +02:00
MoveWindow ( hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
2015-03-24 00:47:53 +01:00
2017-03-05 16:44:50 +01:00
pre_fs_valid = true ;
2021-09-16 21:34:46 +02:00
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
}
2015-03-22 23:00:50 +01:00
}
}
2017-03-05 16:44:50 +01:00
bool OS_Windows : : is_window_fullscreen ( ) const {
2015-03-22 23:00:50 +01:00
return video_mode . fullscreen ;
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_resizable ( bool p_enabled ) {
if ( video_mode . resizable = = p_enabled )
2015-03-22 23:00:50 +01:00
return ;
2015-03-24 00:47:53 +01:00
2017-03-05 16:44:50 +01:00
video_mode . resizable = p_enabled ;
2017-07-10 02:48:22 +02:00
_update_window_style ( ) ;
2015-03-22 23:00:50 +01:00
}
2017-03-05 16:44:50 +01:00
bool OS_Windows : : is_window_resizable ( ) const {
2015-03-22 23:00:50 +01:00
return video_mode . resizable ;
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_minimized ( bool p_enabled ) {
2020-11-18 00:33:39 +01:00
if ( is_no_window_mode_enabled ( ) ) {
return ;
}
2015-03-22 23:00:50 +01:00
if ( p_enabled ) {
2017-03-05 16:44:50 +01:00
maximized = false ;
minimized = true ;
ShowWindow ( hWnd , SW_MINIMIZE ) ;
2015-03-22 23:00:50 +01:00
} else {
2017-03-05 16:44:50 +01:00
ShowWindow ( hWnd , SW_RESTORE ) ;
maximized = false ;
minimized = false ;
2015-03-22 23:00:50 +01:00
}
}
2017-03-05 16:44:50 +01:00
bool OS_Windows : : is_window_minimized ( ) const {
2015-03-22 23:00:50 +01:00
return minimized ;
}
2017-03-05 16:44:50 +01:00
void OS_Windows : : set_window_maximized ( bool p_enabled ) {
2020-11-18 00:33:39 +01:00
if ( is_no_window_mode_enabled ( ) ) {
return ;
}
2015-03-22 23:00:50 +01:00
if ( p_enabled ) {
2017-03-05 16:44:50 +01:00
maximized = true ;
minimized = false ;
ShowWindow ( hWnd , SW_MAXIMIZE ) ;
2015-03-22 23:00:50 +01:00
} else {
2017-03-05 16:44:50 +01:00
ShowWindow ( hWnd , SW_RESTORE ) ;
maximized = false ;
minimized = false ;
2015-03-22 23:00:50 +01:00
}
}
2017-03-05 16:44:50 +01:00
bool OS_Windows : : is_window_maximized ( ) const {
2015-03-22 23:00:50 +01:00
return maximized ;
}
2017-12-27 20:51:19 +01:00
void OS_Windows : : set_window_always_on_top ( bool p_enabled ) {
if ( video_mode . always_on_top = = p_enabled )
return ;
video_mode . always_on_top = p_enabled ;
_update_window_style ( ) ;
}
bool OS_Windows : : is_window_always_on_top ( ) const {
return video_mode . always_on_top ;
}
2019-11-28 13:41:07 +01:00
bool OS_Windows : : is_window_focused ( ) const {
return window_focused ;
}
2017-12-10 19:38:26 +01:00
bool OS_Windows : : get_window_per_pixel_transparency_enabled ( ) const {
2021-05-04 14:28:27 +02:00
if ( ! is_layered_allowed ( ) )
return false ;
2017-12-10 19:38:26 +01:00
return layered_window ;
}
void OS_Windows : : set_window_per_pixel_transparency_enabled ( bool p_enabled ) {
2021-05-04 14:28:27 +02:00
if ( ! is_layered_allowed ( ) )
return ;
2017-12-10 19:38:26 +01:00
if ( layered_window ! = p_enabled ) {
if ( p_enabled ) {
//enable per-pixel alpha
2020-05-10 14:40:19 +02:00
DWM_BLURBEHIND bb = { 0 } ;
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = TRUE ;
DwmEnableBlurBehindWindow ( hWnd , & bb ) ;
2017-12-10 19:38:26 +01:00
layered_window = true ;
} else {
//disable per-pixel alpha
layered_window = false ;
2020-05-10 14:40:19 +02:00
DWM_BLURBEHIND bb = { 0 } ;
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = FALSE ;
DwmEnableBlurBehindWindow ( hWnd , & bb ) ;
2017-12-10 19:38:26 +01:00
}
}
}
2017-12-14 19:15:46 +01:00
void OS_Windows : : set_borderless_window ( bool p_borderless ) {
2017-07-10 02:48:22 +02:00
if ( video_mode . borderless_window = = p_borderless )
return ;
2016-01-03 05:18:28 +01:00
video_mode . borderless_window = p_borderless ;
2017-07-10 02:48:22 +02:00
2018-05-21 00:07:25 +02:00
preserve_window_size = true ;
2017-07-10 02:48:22 +02:00
_update_window_style ( ) ;
2020-06-29 11:31:13 +02:00
_update_window_mouse_passthrough ( ) ;
2016-01-03 05:18:28 +01:00
}
bool OS_Windows : : get_borderless_window ( ) {
return video_mode . borderless_window ;
}
2019-10-27 16:44:47 +01:00
void OS_Windows : : _update_window_style ( bool p_repaint , bool p_maximized ) {
2017-07-10 02:48:22 +02:00
if ( video_mode . fullscreen | | video_mode . borderless_window ) {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE ) ;
} else {
if ( video_mode . resizable ) {
2019-10-27 16:44:47 +01:00
if ( p_maximized ) {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE ) ;
} else {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE ) ;
}
2017-07-10 02:48:22 +02:00
} else {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE ) ;
}
}
2022-05-10 10:12:04 +02:00
if ( icon . is_valid ( ) ) {
set_icon ( icon ) ;
}
2018-05-21 00:07:25 +02:00
SetWindowPos ( hWnd , video_mode . always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST , 0 , 0 , 0 , 0 , SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE ) ;
2017-12-27 20:51:19 +01:00
2019-10-27 16:44:47 +01:00
if ( p_repaint ) {
2017-07-10 02:48:22 +02:00
RECT rect ;
GetWindowRect ( hWnd , & rect ) ;
MoveWindow ( hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
}
}
2017-11-30 14:00:10 +01:00
Error OS_Windows : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_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 ) ) {
//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 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 " ) ;
bool has_dll_directory_api = ( ( add_dll_directory ! = NULL ) & & ( remove_dll_directory ! = NULL ) ) ;
2018-01-26 11:48:20 +01:00
DLL_DIRECTORY_COOKIE cookie = NULL ;
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 ) {
2018-01-04 19:42:29 +01:00
cookie = add_dll_directory ( path . get_base_dir ( ) . c_str ( ) ) ;
2017-11-30 14:00:10 +01:00
}
2018-01-04 19:42:29 +01:00
p_library_handle = ( void * ) LoadLibraryExW ( path . c_str ( ) , NULL , ( 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
}
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 ;
}
2016-07-05 17:29:08 +02:00
void OS_Windows : : request_attention ( ) {
FLASHWINFO info ;
info . cbSize = sizeof ( FLASHWINFO ) ;
info . hwnd = hWnd ;
info . dwFlags = FLASHW_TRAY ;
info . dwTimeout = 0 ;
info . uCount = 2 ;
FlashWindowEx ( & info ) ;
}
2020-10-03 14:13:39 +02:00
void * OS_Windows : : get_native_handle ( int p_handle_type ) {
switch ( p_handle_type ) {
2021-05-04 14:35:44 +02:00
case APPLICATION_HANDLE :
return hInstance ;
case DISPLAY_HANDLE :
return NULL ; // Do we have a value to return here?
case WINDOW_HANDLE :
return hWnd ;
case WINDOW_VIEW :
return gl_context - > get_hdc ( ) ;
case OPENGL_CONTEXT :
return gl_context - > get_hglrc ( ) ;
default :
return NULL ;
2020-10-03 14:13:39 +02:00
}
}
2019-05-20 19:36:24 +02:00
String OS_Windows : : get_name ( ) const {
2014-02-10 02:10:30 +01:00
return " Windows " ;
}
2015-06-07 15:06:13 +02:00
OS : : Date OS_Windows : : get_date ( bool utc ) const {
2014-02-10 02:10:30 +01:00
SYSTEMTIME systemtime ;
2015-06-07 15:06:13 +02:00
if ( utc )
2015-06-06 03:40:56 +02:00
GetSystemTime ( & systemtime ) ;
else
GetLocalTime ( & systemtime ) ;
2015-06-07 15:06:13 +02:00
2022-03-17 11:28:08 +01:00
// Get DST information from Windows, but only if utc is false.
TIME_ZONE_INFORMATION info ;
bool daylight = false ;
if ( ! utc & & GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT ) {
daylight = true ;
}
2014-02-10 02:10:30 +01:00
Date date ;
2017-03-05 16:44:50 +01:00
date . day = systemtime . wDay ;
date . month = Month ( systemtime . wMonth ) ;
date . weekday = Weekday ( systemtime . wDayOfWeek ) ;
date . year = systemtime . wYear ;
2022-03-17 11:28:08 +01:00
date . dst = daylight ;
2014-02-10 02:10:30 +01:00
return date ;
}
2015-06-06 03:40:56 +02:00
OS : : Time OS_Windows : : get_time ( bool utc ) const {
2014-02-10 02:10:30 +01:00
SYSTEMTIME systemtime ;
2015-06-06 03:40:56 +02:00
if ( utc )
GetSystemTime ( & systemtime ) ;
else
GetLocalTime ( & systemtime ) ;
2014-02-10 02:10:30 +01:00
Time time ;
2017-03-05 16:44:50 +01:00
time . hour = systemtime . wHour ;
time . min = systemtime . wMinute ;
time . sec = systemtime . wSecond ;
2014-02-10 02:10:30 +01:00
return time ;
}
2015-06-06 05:35:38 +02:00
OS : : TimeZoneInfo OS_Windows : : get_time_zone_info ( ) const {
TIME_ZONE_INFORMATION info ;
bool daylight = false ;
2015-06-07 16:10:33 +02:00
if ( GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT )
2015-06-06 05:35:38 +02:00
daylight = true ;
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
2022-03-17 11:28:08 +01:00
// For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
ret . bias = - ret . bias ;
2015-06-06 05:35:38 +02:00
return ret ;
}
2014-02-10 02:10:30 +01:00
2015-06-07 15:06:13 +02:00
uint64_t OS_Windows : : get_unix_time ( ) const {
2014-02-10 02:10:30 +01:00
FILETIME ft ;
SYSTEMTIME st ;
GetSystemTime ( & st ) ;
SystemTimeToFileTime ( & st , & ft ) ;
SYSTEMTIME ep ;
ep . wYear = 1970 ;
ep . wMonth = 1 ;
ep . wDayOfWeek = 4 ;
ep . wDay = 1 ;
ep . wHour = 0 ;
ep . wMinute = 0 ;
ep . wSecond = 0 ;
ep . wMilliseconds = 0 ;
FILETIME fep ;
SystemTimeToFileTime ( & ep , & fep ) ;
2019-07-30 00:52:59 +02:00
// Type punning through unions (rather than pointer cast) as per:
2019-06-15 12:11:30 +02:00
// https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
2019-07-30 00:52:59 +02:00
ULARGE_INTEGER ft_punning ;
ft_punning . LowPart = ft . dwLowDateTime ;
ft_punning . HighPart = ft . dwHighDateTime ;
ULARGE_INTEGER fep_punning ;
fep_punning . LowPart = fep . dwLowDateTime ;
fep_punning . HighPart = fep . dwHighDateTime ;
return ( ft_punning . QuadPart - fep_punning . QuadPart ) / 10000000 ;
2014-02-10 02:10:30 +01:00
} ;
2016-01-10 22:24:55 +01:00
uint64_t OS_Windows : : get_system_time_secs ( ) const {
2018-12-19 22:18:52 +01:00
return get_system_time_msecs ( ) / 1000 ;
}
uint64_t OS_Windows : : get_system_time_msecs ( ) const {
const uint64_t WINDOWS_TICK = 10000 ;
const uint64_t MSEC_TO_UNIX_EPOCH = 11644473600000LL ;
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 ) ;
2016-01-10 22:24:55 +01:00
uint64_t ret ;
2017-03-05 16:44:50 +01:00
ret = ft . dwHighDateTime ;
ret < < = 32 ;
ret | = ft . dwLowDateTime ;
2016-06-11 19:09:21 +02:00
2018-12-19 22:18:52 +01:00
return ( uint64_t ) ( ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH ) ;
2015-08-06 19:29:33 +02:00
}
2022-08-08 11:45:32 +02:00
double OS_Windows : : get_subsecond_unix_time ( ) const {
// 1 Windows tick is 100ns
const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000 ;
const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL ;
SYSTEMTIME st ;
GetSystemTime ( & st ) ;
FILETIME ft ;
SystemTimeToFileTime ( & st , & ft ) ;
uint64_t ticks_time ;
ticks_time = ft . dwHighDateTime ;
ticks_time < < = 32 ;
ticks_time | = ft . dwLowDateTime ;
return ( double ) ( ticks_time - TICKS_TO_UNIX_EPOCH ) / WINDOWS_TICKS_PER_SECOND ;
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : delay_usec ( uint32_t p_usec ) const {
2017-03-05 16:44:50 +01:00
if ( p_usec < 1000 )
Sleep ( 1 ) ;
else
Sleep ( p_usec / 1000 ) ;
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
}
void OS_Windows : : process_events ( ) {
MSG msg ;
2019-03-03 21:12:19 +01:00
if ( ! drop_events ) {
joypad - > process_joypads ( ) ;
}
2016-03-09 00:00:52 +01:00
2017-03-05 16:44:50 +01:00
while ( PeekMessageW ( & msg , NULL , 0 , 0 , PM_REMOVE ) ) {
2014-02-10 02:10:30 +01:00
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
2019-03-03 21:12:19 +01:00
if ( ! drop_events ) {
process_key_events ( ) ;
2021-08-03 18:59:20 +02:00
input - > flush_buffered_events ( ) ;
2019-03-03 21:12:19 +01:00
}
2014-02-10 02:10:30 +01:00
}
void OS_Windows : : set_cursor_shape ( CursorShape p_shape ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( cursor_shape = = p_shape )
2014-02-10 02:10:30 +01:00
return ;
2018-05-21 00:07:25 +02:00
if ( mouse_mode ! = MOUSE_MODE_VISIBLE & & mouse_mode ! = MOUSE_MODE_CONFINED ) {
2018-04-09 02:59:51 +02:00
cursor_shape = p_shape ;
return ;
}
2017-03-05 16:44:50 +01:00
static const LPCTSTR win_cursors [ CURSOR_MAX ] = {
2014-02-10 02:10:30 +01:00
IDC_ARROW ,
IDC_IBEAM ,
2017-03-05 16:44:50 +01:00
IDC_HAND , //finger
2014-02-10 02:10:30 +01:00
IDC_CROSS ,
IDC_WAIT ,
IDC_APPSTARTING ,
IDC_ARROW ,
IDC_ARROW ,
IDC_NO ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_SIZENESW ,
IDC_SIZENWSE ,
IDC_SIZEALL ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_HELP
} ;
2017-11-10 11:50:11 +01:00
if ( cursors [ p_shape ] ! = NULL ) {
SetCursor ( cursors [ p_shape ] ) ;
} else {
SetCursor ( LoadCursor ( hInstance , win_cursors [ p_shape ] ) ) ;
}
2018-04-09 02:59:51 +02:00
2017-03-05 16:44:50 +01:00
cursor_shape = p_shape ;
2014-02-10 02:10:30 +01:00
}
2019-04-15 17:30:20 +02:00
OS : : CursorShape OS_Windows : : get_cursor_shape ( ) const {
return cursor_shape ;
}
2017-11-10 11:50:11 +01:00
void OS_Windows : : set_custom_mouse_cursor ( const RES & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
if ( p_cursor . is_valid ( ) ) {
2021-05-04 14:20:36 +02:00
Map < CursorShape , Vector < Variant > > : : Element * cursor_c = cursors_cache . find ( p_shape ) ;
2019-07-08 22:37:18 +02:00
if ( cursor_c ) {
if ( cursor_c - > get ( ) [ 0 ] = = p_cursor & & cursor_c - > get ( ) [ 1 ] = = p_hotspot ) {
set_cursor_shape ( p_shape ) ;
return ;
}
cursors_cache . erase ( p_shape ) ;
}
2017-11-10 11:50:11 +01:00
Ref < Texture > texture = p_cursor ;
2018-05-08 16:07:43 +02:00
Ref < AtlasTexture > atlas_texture = p_cursor ;
Ref < Image > image ;
Size2 texture_size ;
Rect2 atlas_rect ;
2017-11-10 11:50:11 +01:00
2018-05-08 16:07:43 +02:00
if ( texture . is_valid ( ) ) {
image = texture - > get_data ( ) ;
}
if ( ! image . is_valid ( ) & & atlas_texture . is_valid ( ) ) {
texture = atlas_texture - > get_atlas ( ) ;
atlas_rect . size . width = texture - > get_width ( ) ;
atlas_rect . size . height = texture - > get_height ( ) ;
atlas_rect . position . x = atlas_texture - > get_region ( ) . position . x ;
atlas_rect . position . y = atlas_texture - > get_region ( ) . position . y ;
texture_size . width = atlas_texture - > get_region ( ) . size . x ;
texture_size . height = atlas_texture - > get_region ( ) . size . y ;
} else if ( image . is_valid ( ) ) {
texture_size . width = texture - > get_width ( ) ;
texture_size . height = texture - > get_height ( ) ;
}
2017-11-10 11:50:11 +01:00
2018-05-08 16:07:43 +02:00
ERR_FAIL_COND ( ! texture . is_valid ( ) ) ;
2018-09-04 04:42:23 +02:00
ERR_FAIL_COND ( p_hotspot . x < 0 | | p_hotspot . y < 0 ) ;
2018-05-08 16:07:43 +02:00
ERR_FAIL_COND ( texture_size . width > 256 | | texture_size . height > 256 ) ;
2018-09-03 14:21:13 +02:00
ERR_FAIL_COND ( p_hotspot . x > texture_size . width | | p_hotspot . y > texture_size . height ) ;
2018-05-08 16:07:43 +02:00
image = texture - > get_data ( ) ;
2018-05-28 14:36:30 +02:00
ERR_FAIL_COND ( ! image . is_valid ( ) ) ;
2018-05-08 16:07:43 +02:00
UINT image_size = texture_size . width * texture_size . height ;
2017-11-10 11:50:11 +01:00
// Create the BITMAP with alpha channel
2018-05-26 20:58:12 +02:00
COLORREF * buffer = ( COLORREF * ) memalloc ( sizeof ( COLORREF ) * image_size ) ;
2017-11-10 11:50:11 +01:00
image - > lock ( ) ;
for ( UINT index = 0 ; index < image_size ; index + + ) {
2018-05-08 16:07:43 +02:00
int row_index = floor ( index / texture_size . width ) + atlas_rect . position . y ;
int column_index = ( index % int ( texture_size . width ) ) + atlas_rect . position . x ;
if ( atlas_texture . is_valid ( ) ) {
column_index = MIN ( column_index , atlas_rect . size . width - 1 ) ;
row_index = MIN ( row_index , atlas_rect . size . height - 1 ) ;
}
2017-11-10 11:50:11 +01:00
2018-04-05 18:07:44 +02:00
* ( buffer + index ) = image - > get_pixel ( column_index , row_index ) . to_argb32 ( ) ;
2017-11-10 11:50:11 +01:00
}
image - > unlock ( ) ;
// Using 4 channels, so 4 * 8 bits
2018-05-08 16:07:43 +02:00
HBITMAP bitmap = CreateBitmap ( texture_size . width , texture_size . height , 1 , 4 * 8 , buffer ) ;
2017-11-10 11:50:11 +01:00
COLORREF clrTransparent = - 1 ;
// Create the AND and XOR masks for the bitmap
HBITMAP hAndMask = NULL ;
HBITMAP hXorMask = NULL ;
GetMaskBitmaps ( bitmap , clrTransparent , hAndMask , hXorMask ) ;
if ( NULL = = hAndMask | | NULL = = hXorMask ) {
2018-05-26 20:58:12 +02:00
memfree ( buffer ) ;
DeleteObject ( bitmap ) ;
2017-11-10 11:50:11 +01:00
return ;
}
// Finally, create the icon
2019-06-15 12:11:30 +02:00
ICONINFO iconinfo ;
2017-11-10 11:50:11 +01:00
iconinfo . fIcon = FALSE ;
iconinfo . xHotspot = p_hotspot . x ;
iconinfo . yHotspot = p_hotspot . y ;
iconinfo . hbmMask = hAndMask ;
iconinfo . hbmColor = hXorMask ;
2019-01-23 00:58:18 +01:00
if ( cursors [ p_shape ] )
DestroyIcon ( cursors [ p_shape ] ) ;
2017-11-10 11:50:11 +01:00
cursors [ p_shape ] = CreateIconIndirect ( & iconinfo ) ;
2019-07-08 22:37:18 +02:00
Vector < Variant > params ;
params . push_back ( p_cursor ) ;
params . push_back ( p_hotspot ) ;
cursors_cache . insert ( p_shape , params ) ;
2018-09-13 19:53:20 +02:00
if ( p_shape = = cursor_shape ) {
2018-09-17 16:37:54 +02:00
if ( mouse_mode = = MOUSE_MODE_VISIBLE | | mouse_mode = = MOUSE_MODE_CONFINED ) {
SetCursor ( cursors [ p_shape ] ) ;
}
2017-11-10 11:50:11 +01:00
}
if ( hAndMask ! = NULL ) {
DeleteObject ( hAndMask ) ;
}
if ( hXorMask ! = NULL ) {
DeleteObject ( hXorMask ) ;
}
2018-05-26 20:58:12 +02:00
memfree ( buffer ) ;
DeleteObject ( bitmap ) ;
2018-05-11 00:17:00 +02:00
} else {
// Reset to default system cursor
2019-10-03 13:02:11 +02:00
if ( cursors [ p_shape ] ) {
DestroyIcon ( cursors [ p_shape ] ) ;
cursors [ p_shape ] = NULL ;
}
2018-09-13 19:53:20 +02:00
CursorShape c = cursor_shape ;
2018-05-11 00:17:00 +02:00
cursor_shape = CURSOR_MAX ;
2018-09-13 19:53:20 +02:00
set_cursor_shape ( c ) ;
2019-10-03 13:02:11 +02:00
cursors_cache . erase ( p_shape ) ;
2017-11-10 11:50:11 +01:00
}
}
void OS_Windows : : GetMaskBitmaps ( HBITMAP hSourceBitmap , COLORREF clrTransparent , OUT HBITMAP & hAndMaskBitmap , OUT HBITMAP & hXorMaskBitmap ) {
// Get the system display DC
HDC hDC = GetDC ( NULL ) ;
// Create helper DC
HDC hMainDC = CreateCompatibleDC ( hDC ) ;
HDC hAndMaskDC = CreateCompatibleDC ( hDC ) ;
HDC hXorMaskDC = CreateCompatibleDC ( hDC ) ;
// Get the dimensions of the source bitmap
BITMAP bm ;
GetObject ( hSourceBitmap , sizeof ( BITMAP ) , & bm ) ;
// Create the mask bitmaps
hAndMaskBitmap = CreateCompatibleBitmap ( hDC , bm . bmWidth , bm . bmHeight ) ; // color
hXorMaskBitmap = CreateCompatibleBitmap ( hDC , bm . bmWidth , bm . bmHeight ) ; // color
// Release the system display DC
ReleaseDC ( NULL , hDC ) ;
// Select the bitmaps to helper DC
HBITMAP hOldMainBitmap = ( HBITMAP ) SelectObject ( hMainDC , hSourceBitmap ) ;
HBITMAP hOldAndMaskBitmap = ( HBITMAP ) SelectObject ( hAndMaskDC , hAndMaskBitmap ) ;
HBITMAP hOldXorMaskBitmap = ( HBITMAP ) SelectObject ( hXorMaskDC , hXorMaskBitmap ) ;
// Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
// with 'clrTransparent' will be white pixels of the monochrome bitmap
SetBkColor ( hMainDC , clrTransparent ) ;
BitBlt ( hAndMaskDC , 0 , 0 , bm . bmWidth , bm . bmHeight , hMainDC , 0 , 0 , SRCCOPY ) ;
// Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
// with 'clrTransparent' will be black and rest the pixels same as corresponding
// pixels of the source bitmap
SetBkColor ( hXorMaskDC , RGB ( 0 , 0 , 0 ) ) ;
SetTextColor ( hXorMaskDC , RGB ( 255 , 255 , 255 ) ) ;
BitBlt ( hXorMaskDC , 0 , 0 , bm . bmWidth , bm . bmHeight , hAndMaskDC , 0 , 0 , SRCCOPY ) ;
BitBlt ( hXorMaskDC , 0 , 0 , bm . bmWidth , bm . bmHeight , hMainDC , 0 , 0 , SRCAND ) ;
// Deselect bitmaps from the helper DC
SelectObject ( hMainDC , hOldMainBitmap ) ;
SelectObject ( hAndMaskDC , hOldAndMaskBitmap ) ;
SelectObject ( hXorMaskDC , hOldXorMaskBitmap ) ;
// Delete the helper DC
DeleteDC ( hXorMaskDC ) ;
DeleteDC ( hAndMaskDC ) ;
DeleteDC ( hMainDC ) ;
}
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 + + ) {
CharType c = p_text [ i ] ;
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 . 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 12:08:09 +01:00
Error OS_Windows : : execute ( const String & p_path , const List < String > & p_arguments , bool p_blocking , ProcessID * r_child_id , 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 ( " / " , " \\ " ) ;
2014-02-10 02:10:30 +01:00
2020-10-22 12:17:21 +02:00
String cmdline = _quote_command_line_argument ( path ) ;
2017-03-05 16:44:50 +01:00
const List < String > : : Element * I = p_arguments . front ( ) ;
2014-02-10 02:10:30 +01:00
while ( I ) {
2020-05-19 15:34:15 +02:00
cmdline + = " " + _quote_command_line_argument ( I - > get ( ) ) ;
2014-02-10 02:10:30 +01:00
I = I - > next ( ) ;
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
2020-05-19 15:34:15 +02:00
Vector < CharType > modstr ; // Windows wants to change this no idea why.
2014-02-15 06:01:39 +01:00
modstr . resize ( cmdline . size ( ) ) ;
2020-05-19 15:34:15 +02:00
for ( int i = 0 ; i < cmdline . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
modstr . write [ i ] = cmdline [ i ] ;
2020-05-19 15:34:15 +02:00
}
2021-12-16 12:08:09 +01:00
bool inherit_handles = false ;
HANDLE pipe [ 2 ] = { NULL , NULL } ;
if ( p_blocking & & r_pipe ) {
// Create pipe for StdOut and StdErr.
SECURITY_ATTRIBUTES sa ;
sa . nLength = sizeof ( SECURITY_ATTRIBUTES ) ;
sa . bInheritHandle = true ;
sa . lpSecurityDescriptor = NULL ;
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 ] ;
if ( read_stderr ) {
pi . si . hStdError = pipe [ 1 ] ;
}
inherit_handles = true ;
}
DWORD creaton_flags = NORMAL_PRIORITY_CLASS ;
if ( p_open_console ) {
creaton_flags | = CREATE_NEW_CONSOLE ;
} else {
creaton_flags | = CREATE_NO_WINDOW ;
2021-12-14 13:38:42 +01:00
}
2021-12-16 12:08:09 +01:00
int ret = CreateProcessW ( NULL , modstr . ptrw ( ) , NULL , NULL , inherit_handles , creaton_flags , NULL , NULL , si_w , & pi . pi ) ;
if ( ! ret & & r_pipe ) {
CloseHandle ( pipe [ 0 ] ) ; // Cleanup pipe handles.
CloseHandle ( pipe [ 1 ] ) ;
}
2024-09-19 02:03:29 +02:00
ERR_FAIL_COND_V_MSG ( ret = = 0 , ERR_CANT_FORK , " Could not create child process: " + String ( modstr . ptr ( ) ) ) ;
2014-02-10 02:10:30 +01:00
if ( p_blocking ) {
2021-12-16 12:08:09 +01:00
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 12:08:09 +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 12:08:09 +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 ;
}
2021-12-16 12:08:09 +01:00
}
2022-05-10 12:27:45 +02:00
if ( newline_index = = - 1 ) {
bytes_in_buffer + = read ;
continue ;
2021-12-16 12:08:09 +01: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 ) ;
}
2021-12-16 12:08:09 +01:00
CloseHandle ( pipe [ 0 ] ) ; // Close pipe read handle.
} else {
WaitForSingleObject ( pi . pi . hProcess , INFINITE ) ;
}
2020-05-19 15:34:15 +02:00
if ( r_exitcode ) {
2020-11-16 18:26:57 +01:00
DWORD ret2 ;
GetExitCodeProcess ( pi . pi . hProcess , & ret2 ) ;
2019-06-15 12:11:30 +02:00
* r_exitcode = ret2 ;
2020-05-19 15:34:15 +02:00
}
2014-02-10 02:10:30 +01:00
2018-07-30 09:55:33 +02:00
CloseHandle ( pi . pi . hProcess ) ;
CloseHandle ( pi . pi . hThread ) ;
2014-02-10 02:10:30 +01:00
} else {
ProcessID pid = pi . pi . dwProcessId ;
if ( r_child_id ) {
* r_child_id = pid ;
2020-05-19 15:34:15 +02:00
}
2014-02-10 02:10:30 +01:00
process_map - > insert ( pid , pi ) ;
2020-05-19 15:34:15 +02:00
}
2014-02-10 02:10:30 +01:00
return OK ;
2022-05-10 12:27:45 +02: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 ;
} ;
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 ) {
if ( _wchdir ( p_cwd . c_str ( ) ) ! = 0 )
2014-02-10 02:10:30 +01:00
return ERR_CANT_OPEN ;
return OK ;
}
2014-02-15 06:01:39 +01:00
String OS_Windows : : get_executable_path ( ) const {
wchar_t bufname [ 4096 ] ;
2017-03-05 16:44:50 +01:00
GetModuleFileNameW ( NULL , bufname , 4096 ) ;
String s = bufname ;
2020-10-22 12:17:21 +02:00
return s . replace ( " \\ " , " / " ) ;
2014-02-15 06:01:39 +01:00
}
2019-05-17 15:43:56 +02:00
void OS_Windows : : set_native_icon ( const String & p_filename ) {
FileAccess * f = FileAccess : : open ( p_filename , FileAccess : : READ ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! f , " Cannot open file with icon ' " + p_filename + " '. " ) ;
2019-05-17 15:43:56 +02:00
ICONDIR * icon_dir = ( ICONDIR * ) memalloc ( sizeof ( ICONDIR ) ) ;
int pos = 0 ;
icon_dir - > idReserved = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
icon_dir - > idType = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( icon_dir - > idType ! = 1 , " Invalid icon file format! " ) ;
2019-05-17 15:43:56 +02:00
icon_dir - > idCount = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
2023-01-19 22:32:00 +01:00
icon_dir = ( ICONDIR * ) memrealloc ( icon_dir , sizeof ( ICONDIR ) + ( icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ) ;
2019-05-17 15:43:56 +02:00
f - > get_buffer ( ( uint8_t * ) & icon_dir - > idEntries [ 0 ] , icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ;
int small_icon_index = - 1 ; // Select 16x16 with largest color count
int small_icon_cc = 0 ;
int big_icon_index = - 1 ; // Select largest
int big_icon_width = 16 ;
int big_icon_cc = 0 ;
for ( int i = 0 ; i < icon_dir - > idCount ; i + + ) {
int colors = ( icon_dir - > idEntries [ i ] . bColorCount = = 0 ) ? 32768 : icon_dir - > idEntries [ i ] . bColorCount ;
int width = ( icon_dir - > idEntries [ i ] . bWidth = = 0 ) ? 256 : icon_dir - > idEntries [ i ] . bWidth ;
if ( width = = 16 ) {
if ( colors > = small_icon_cc ) {
small_icon_index = i ;
small_icon_cc = colors ;
}
}
if ( width > = big_icon_width ) {
if ( colors > = big_icon_cc ) {
big_icon_index = i ;
big_icon_width = width ;
big_icon_cc = colors ;
}
}
}
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( big_icon_index = = - 1 , " No valid icons found! " ) ;
2019-05-17 15:43:56 +02:00
if ( small_icon_index = = - 1 ) {
2021-06-18 13:26:58 +02:00
WARN_PRINT ( " No small icon found, reusing " + itos ( big_icon_width ) + " x " + itos ( big_icon_width ) + " @ " + itos ( big_icon_cc ) + " icon! " ) ;
2019-05-17 15:43:56 +02:00
small_icon_index = big_icon_index ;
small_icon_cc = big_icon_cc ;
}
// Read the big icon
DWORD bytecount_big = icon_dir - > idEntries [ big_icon_index ] . dwBytesInRes ;
Vector < uint8_t > data_big ;
data_big . resize ( bytecount_big ) ;
pos = icon_dir - > idEntries [ big_icon_index ] . dwImageOffset ;
f - > seek ( pos ) ;
f - > get_buffer ( ( uint8_t * ) & data_big . write [ 0 ] , bytecount_big ) ;
HICON icon_big = CreateIconFromResource ( ( PBYTE ) & data_big . write [ 0 ] , bytecount_big , TRUE , 0x00030000 ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( ! icon_big , " Could not create " + itos ( big_icon_width ) + " x " + itos ( big_icon_width ) + " @ " + itos ( big_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
2019-05-17 15:43:56 +02:00
// Read the small icon
DWORD bytecount_small = icon_dir - > idEntries [ small_icon_index ] . dwBytesInRes ;
Vector < uint8_t > data_small ;
data_small . resize ( bytecount_small ) ;
pos = icon_dir - > idEntries [ small_icon_index ] . dwImageOffset ;
f - > seek ( pos ) ;
f - > get_buffer ( ( uint8_t * ) & data_small . write [ 0 ] , bytecount_small ) ;
HICON icon_small = CreateIconFromResource ( ( PBYTE ) & data_small . write [ 0 ] , bytecount_small , TRUE , 0x00030000 ) ;
2019-08-09 06:49:33 +02:00
ERR_FAIL_COND_MSG ( ! icon_small , " Could not create 16x16 @ " + itos ( small_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
2019-05-17 15:43:56 +02:00
// Online tradition says to be sure last error is cleared and set the small icon first
int err = 0 ;
SetLastError ( err ) ;
SendMessage ( hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) icon_small ) ;
err = GetLastError ( ) ;
2019-08-09 13:10:06 +02:00
ERR_FAIL_COND_MSG ( err , " Error setting ICON_SMALL: " + format_error_message ( err ) + " . " ) ;
2019-05-17 15:43:56 +02:00
SendMessage ( hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) icon_big ) ;
err = GetLastError ( ) ;
2019-08-09 13:10:06 +02:00
ERR_FAIL_COND_MSG ( err , " Error setting ICON_BIG: " + format_error_message ( err ) + " . " ) ;
2019-05-17 15:43:56 +02:00
memdelete ( f ) ;
memdelete ( icon_dir ) ;
}
2017-05-17 12:36:47 +02:00
void OS_Windows : : set_icon ( const Ref < Image > & p_icon ) {
ERR_FAIL_COND ( ! p_icon . is_valid ( ) ) ;
2022-05-10 10:12:04 +02:00
if ( icon ! = p_icon ) {
icon = p_icon - > duplicate ( ) ;
if ( icon - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
icon - > convert ( Image : : FORMAT_RGBA8 ) ;
}
}
2017-05-17 12:36:47 +02:00
int w = icon - > get_width ( ) ;
int h = icon - > get_height ( ) ;
2014-02-10 02:10:30 +01:00
/* Create temporary bitmap buffer */
int icon_len = 40 + h * w * 4 ;
2014-02-13 22:03:28 +01:00
Vector < BYTE > v ;
v . resize ( icon_len ) ;
2018-07-25 03:11:03 +02:00
BYTE * icon_bmp = v . ptrw ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
encode_uint32 ( 40 , & icon_bmp [ 0 ] ) ;
encode_uint32 ( w , & icon_bmp [ 4 ] ) ;
encode_uint32 ( h * 2 , & icon_bmp [ 8 ] ) ;
encode_uint16 ( 1 , & icon_bmp [ 12 ] ) ;
encode_uint16 ( 32 , & icon_bmp [ 14 ] ) ;
encode_uint32 ( BI_RGB , & icon_bmp [ 16 ] ) ;
encode_uint32 ( w * h * 4 , & icon_bmp [ 20 ] ) ;
encode_uint32 ( 0 , & icon_bmp [ 24 ] ) ;
encode_uint32 ( 0 , & icon_bmp [ 28 ] ) ;
encode_uint32 ( 0 , & icon_bmp [ 32 ] ) ;
encode_uint32 ( 0 , & icon_bmp [ 36 ] ) ;
uint8_t * wr = & icon_bmp [ 40 ] ;
2017-05-17 12:36:47 +02:00
PoolVector < uint8_t > : : Read r = icon - > get_data ( ) . read ( ) ;
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < h ; i + + ) {
for ( int j = 0 ; j < w ; j + + ) {
const uint8_t * rpx = & r [ ( ( h - i - 1 ) * w + j ) * 4 ] ;
uint8_t * wpx = & wr [ ( i * w + j ) * 4 ] ;
wpx [ 0 ] = rpx [ 2 ] ;
wpx [ 1 ] = rpx [ 1 ] ;
wpx [ 2 ] = rpx [ 0 ] ;
wpx [ 3 ] = rpx [ 3 ] ;
2014-02-10 02:10:30 +01:00
}
}
HICON hicon = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
/* Set the icon for the window */
2017-03-05 16:44:50 +01:00
SendMessage ( hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) hicon ) ;
2014-02-10 02:10:30 +01:00
/* Set the icon in the task manager (should we do this?) */
2017-03-05 16:44:50 +01:00
SendMessage ( hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) hicon ) ;
2014-02-10 02:10:30 +01:00
}
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
2017-11-30 14:00:10 +01:00
return _wgetenv ( p_var . c_str ( ) ) ! = NULL ;
2018-10-04 15:38:52 +02:00
# else
wchar_t * env ;
size_t len ;
_wdupenv_s ( & env , & len , p_var . c_str ( ) ) ;
const bool has_env = env ! = NULL ;
free ( env ) ;
return has_env ;
# endif
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 {
2015-05-04 18:12:05 +02:00
wchar_t wval [ 0x7Fff ] ; // MSDN says 32767 char is the maximum
2017-03-05 16:44:50 +01:00
int wlen = GetEnvironmentVariableW ( p_var . c_str ( ) , wval , 0x7Fff ) ;
if ( wlen > 0 ) {
2015-05-04 18:12:05 +02:00
return wval ;
}
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 {
return ( bool ) SetEnvironmentVariableW ( p_var . c_str ( ) , p_value . c_str ( ) ) ;
}
2022-12-21 00:30:55 +01:00
String OS_Windows : : get_stdin_string ( ) {
char buff [ 1024 ] ;
return fgets ( buff , 1024 , stdin ) ;
2014-02-10 02:10:30 +01:00
}
2016-09-14 04:02:18 +02:00
void OS_Windows : : enable_for_stealing_focus ( ProcessID pid ) {
AllowSetForegroundWindow ( pid ) ;
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : move_window_to_foreground ( ) {
SetForegroundWindow ( hWnd ) ;
}
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 , p_uri . c_str ( ) , 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 ) {
if ( wl - > main_lang = = lang & & wl - > sublang = = SUBLANG_NEUTRAL )
neutral = wl - > locale ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( lang = = wl - > main_lang & & sublang = = wl - > sublang )
2020-07-25 22:42:11 +02:00
return String ( wl - > locale ) . replace ( " - " , " _ " ) ;
2014-02-10 02:10:30 +01:00
wl + + ;
}
2017-03-05 16:44:50 +01:00
if ( neutral ! = " " )
2020-07-25 22:42:11 +02:00
return String ( neutral ) . replace ( " - " , " _ " ) ;
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 ;
}
2022-02-15 22:25:51 +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 . c_str ( ) ) , 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 ( ( const wchar_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. " ) ) ;
}
}
2016-07-02 20:52:36 +02:00
OS : : LatinKeyboardVariant OS_Windows : : get_latin_keyboard_variant ( ) const {
2017-03-05 16:44:50 +01:00
unsigned long azerty [ ] = {
2016-07-02 20:52:36 +02:00
0x00020401 , // Arabic (102) AZERTY
0x0001080c , // Belgian (Comma)
0x0000080c , // Belgian French
0x0000040c , // French
0 // <--- STOP MARK
} ;
unsigned long qwertz [ ] = {
0x0000041a , // Croation
0x00000405 , // Czech
0x00000407 , // German
0x00010407 , // German (IBM)
0x0000040e , // Hungarian
0x0000046e , // Luxembourgish
0x00010415 , // Polish (214)
0x00000418 , // Romanian (Legacy)
0x0000081a , // Serbian (Latin)
0x0000041b , // Slovak
0x00000424 , // Slovenian
0x0001042e , // Sorbian Extended
0x0002042e , // Sorbian Standard
0x0000042e , // Sorbian Standard (Legacy)
0x0000100c , // Swiss French
0x00000807 , // Swiss German
0 // <--- STOP MARK
} ;
unsigned long dvorak [ ] = {
0x00010409 , // US-Dvorak
0x00030409 , // US-Dvorak for left hand
0x00040409 , // US-Dvorak for right hand
0 // <--- STOP MARK
} ;
2017-03-05 16:44:50 +01:00
char name [ KL_NAMELENGTH + 1 ] ;
name [ 0 ] = 0 ;
GetKeyboardLayoutNameA ( name ) ;
2016-07-02 20:52:36 +02:00
unsigned long hex = strtoul ( name , NULL , 16 ) ;
2017-03-05 16:44:50 +01:00
int i = 0 ;
while ( azerty [ i ] ! = 0 ) {
2021-05-04 14:28:27 +02:00
if ( azerty [ i ] = = hex )
return LATIN_KEYBOARD_AZERTY ;
2016-07-02 20:52:36 +02:00
i + + ;
}
i = 0 ;
2017-03-05 16:44:50 +01:00
while ( qwertz [ i ] ! = 0 ) {
2021-05-04 14:28:27 +02:00
if ( qwertz [ i ] = = hex )
return LATIN_KEYBOARD_QWERTZ ;
2016-07-02 20:52:36 +02:00
i + + ;
}
2017-03-05 16:44:50 +01:00
2016-07-02 20:52:36 +02:00
i = 0 ;
2017-03-05 16:44:50 +01:00
while ( dvorak [ i ] ! = 0 ) {
2021-05-04 14:28:27 +02:00
if ( dvorak [ i ] = = hex )
return LATIN_KEYBOARD_DVORAK ;
2017-03-05 16:44:50 +01:00
i + + ;
2016-07-02 20:52:36 +02:00
}
return LATIN_KEYBOARD_QWERTY ;
}
2020-06-12 14:51:51 +02:00
int OS_Windows : : keyboard_get_layout_count ( ) const {
return GetKeyboardLayoutList ( 0 , NULL ) ;
}
int OS_Windows : : keyboard_get_current_layout ( ) const {
HKL cur_layout = GetKeyboardLayout ( 0 ) ;
int layout_count = GetKeyboardLayoutList ( 0 , NULL ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
for ( int i = 0 ; i < layout_count ; i + + ) {
if ( cur_layout = = layouts [ i ] ) {
memfree ( layouts ) ;
return i ;
}
}
memfree ( layouts ) ;
return - 1 ;
}
void OS_Windows : : keyboard_set_current_layout ( int p_index ) {
int layout_count = GetKeyboardLayoutList ( 0 , NULL ) ;
ERR_FAIL_INDEX ( p_index , layout_count ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
ActivateKeyboardLayout ( layouts [ p_index ] , KLF_SETFORPROCESS ) ;
memfree ( layouts ) ;
}
String OS_Windows : : keyboard_get_layout_language ( int p_index ) const {
int layout_count = GetKeyboardLayoutList ( 0 , NULL ) ;
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
wchar_t buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( wchar_t ) ) ;
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
memfree ( layouts ) ;
return String ( buf ) . substr ( 0 , 2 ) ;
}
2021-12-17 11:39:40 +01:00
uint32_t OS_Windows : : keyboard_get_scancode_from_physical ( uint32_t p_scancode ) const {
unsigned int modifiers = p_scancode & KEY_MODIFIER_MASK ;
uint32_t scancode_no_mod = ( uint32_t ) ( p_scancode & KEY_CODE_MASK ) ;
if ( scancode_no_mod = = KEY_PRINT | |
scancode_no_mod = = KEY_KP_ADD | |
scancode_no_mod = = KEY_KP_5 | |
( scancode_no_mod > = KEY_0 & & scancode_no_mod < = KEY_9 ) ) {
return p_scancode ;
}
unsigned int scancode = KeyMappingWindows : : get_scancode ( scancode_no_mod ) ;
if ( scancode = = 0 ) {
return p_scancode ;
}
HKL current_layout = GetKeyboardLayout ( 0 ) ;
UINT vk = MapVirtualKeyEx ( scancode , MAPVK_VSC_TO_VK , current_layout ) ;
if ( vk = = 0 ) {
return p_scancode ;
}
UINT char_code = MapVirtualKeyEx ( vk , MAPVK_VK_TO_CHAR , current_layout ) & 0x7FFF ;
// Unlike a similar Linux/BSD check which matches full Latin-1 range,
// we limit these to ASCII to fix some layouts, including Arabic ones
if ( char_code > = 32 & & char_code < = 127 ) {
// Godot uses 'braces' instead of 'brackets'
if ( char_code = = KEY_BRACKETLEFT | | char_code = = KEY_BRACKETRIGHT ) {
char_code + = 32 ;
}
return ( uint32_t ) ( char_code | modifiers ) ;
}
return ( uint32_t ) ( KeyMappingWindows : : get_keysym ( vk ) | modifiers ) ;
}
2020-06-12 14:51:51 +02:00
String _get_full_layout_name_from_registry ( HKL p_layout ) {
String id = " SYSTEM \\ CurrentControlSet \\ Control \\ Keyboard Layouts \\ " + String : : num_int64 ( ( int64_t ) p_layout , 16 , false ) . lpad ( 8 , " 0 " ) ;
String ret ;
HKEY hkey ;
wchar_t layout_text [ 1024 ] ;
memset ( layout_text , 0 , 1024 * sizeof ( wchar_t ) ) ;
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) id . c_str ( ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
return ret ;
}
DWORD buffer = 1024 ;
DWORD vtype = REG_SZ ;
if ( RegQueryValueExW ( hkey , L " Layout Text " , NULL , & vtype , ( LPBYTE ) layout_text , & buffer ) = = ERROR_SUCCESS ) {
ret = String ( layout_text ) ;
}
RegCloseKey ( hkey ) ;
return ret ;
}
String OS_Windows : : keyboard_get_layout_name ( int p_index ) const {
int layout_count = GetKeyboardLayoutList ( 0 , NULL ) ;
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
String ret = _get_full_layout_name_from_registry ( layouts [ p_index ] ) ; // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine).
if ( ret = = String ( ) ) {
wchar_t buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( wchar_t ) ) ;
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
wchar_t name [ 1024 ] ;
memset ( name , 0 , 1024 * sizeof ( wchar_t ) ) ;
GetLocaleInfoEx ( buf , LOCALE_SLOCALIZEDDISPLAYNAME , ( LPWSTR ) & name , 1024 ) ;
ret = String ( name ) ;
}
memfree ( layouts ) ;
return ret ;
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : release_rendering_thread ( ) {
gl_context - > release_current ( ) ;
}
void OS_Windows : : make_rendering_thread ( ) {
gl_context - > make_current ( ) ;
}
void OS_Windows : : swap_buffers ( ) {
gl_context - > swap_buffers ( ) ;
}
2017-12-14 12:59:46 +01:00
void OS_Windows : : force_process_input ( ) {
process_events ( ) ; // get rid of pending events
}
2014-02-10 02:10:30 +01:00
void OS_Windows : : run ( ) {
if ( ! main_loop )
return ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
main_loop - > init ( ) ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
while ( ! force_quit ) {
process_events ( ) ; // get rid of pending events
2018-10-03 19:40:37 +02:00
if ( Main : : iteration ( ) )
2014-02-10 02:10:30 +01:00
break ;
} ;
2016-03-09 00:00:52 +01:00
2014-02-10 02:10:30 +01:00
main_loop - > finish ( ) ;
}
MainLoop * OS_Windows : : get_main_loop ( ) const {
return main_loop ;
}
2022-04-28 09:24:29 +02:00
uint64_t OS_Windows : : get_embedded_pck_offset ( ) const {
FileAccessRef f = FileAccess : : open ( get_executable_path ( ) , FileAccess : : READ ) ;
if ( ! f ) {
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-07 19:02:35 +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-07 19:02:35 +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-07 19:02:35 +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 = = String ( ) ) {
2022-10-07 19:03: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 = = String ( ) & & has_environment ( " TEMP " ) ) {
cache_path_cache = get_environment ( " TEMP " ) . replace ( " \\ " , " / " ) ;
}
if ( cache_path_cache = = String ( ) ) {
cache_path_cache = get_config_path ( ) ;
2021-05-07 19:02:35 +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 ;
HRESULT res = SHGetKnownFolderPath ( id , 0 , NULL , & 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 ( 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 {
2017-11-26 19:00:53 +01:00
String appname = get_safe_dir_name ( ProjectSettings : : get_singleton ( ) - > get ( " application/config/name " ) ) ;
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
if ( appname ! = " " ) {
2017-11-26 19:00:53 +01: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 ) ;
if ( custom_dir = = " " ) {
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-17 17:11:41 +01:00
} else {
2017-11-26 19:00:53 +01:00
return get_data_path ( ) . plus_file ( get_godot_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( appname ) . replace ( " \\ " , " / " ) ;
2014-02-10 02:10:30 +01:00
}
}
2021-11-18 01:36:13 +01:00
return get_data_path ( ) . plus_file ( get_godot_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( " [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 {
HW_PROFILE_INFO HwProfInfo ;
ERR_FAIL_COND_V ( ! GetCurrentHwProfile ( & HwProfInfo ) , " " ) ;
return String ( HwProfInfo . szHwProfileGuid ) ;
}
2018-06-07 22:16:57 +02:00
void OS_Windows : : set_ime_active ( const bool p_active ) {
if ( p_active ) {
ImmAssociateContext ( hWnd , im_himc ) ;
set_ime_position ( im_position ) ;
} else {
ImmAssociateContext ( hWnd , ( HIMC ) 0 ) ;
}
}
2018-01-20 14:40:52 +01:00
void OS_Windows : : set_ime_position ( const Point2 & p_pos ) {
2018-06-07 22:16:57 +02:00
im_position = p_pos ;
2018-01-20 14:40:52 +01:00
HIMC himc = ImmGetContext ( hWnd ) ;
2018-06-07 22:16:57 +02:00
if ( himc = = ( HIMC ) 0 )
return ;
2018-01-20 14:40:52 +01:00
COMPOSITIONFORM cps ;
cps . dwStyle = CFS_FORCE_POSITION ;
2018-06-07 22:16:57 +02:00
cps . ptCurrentPos . x = im_position . x ;
cps . ptCurrentPos . y = im_position . y ;
2018-01-20 14:40:52 +01:00
ImmSetCompositionWindow ( himc , & cps ) ;
ImmReleaseContext ( hWnd , himc ) ;
}
2016-01-08 00:40:41 +01:00
bool OS_Windows : : is_joy_known ( int p_device ) {
return input - > is_joy_mapped ( p_device ) ;
}
String OS_Windows : : get_joy_guid ( int p_device ) const {
return input - > get_joy_guid_remapped ( p_device ) ;
}
2014-02-10 02:10:30 +01:00
2017-12-16 21:09:25 +01:00
void OS_Windows : : _set_use_vsync ( bool p_enable ) {
2016-06-06 00:14:33 +02:00
if ( gl_context )
gl_context - > set_use_vsync ( p_enable ) ;
}
2017-12-16 21:09:25 +01:00
/*
2017-03-05 16:44:50 +01:00
bool OS_Windows : : is_vsync_enabled ( ) const {
2016-06-06 00:14:33 +02:00
if ( gl_context )
return gl_context - > is_using_vsync ( ) ;
return true ;
2017-12-16 21:09:25 +01:00
} */
2016-06-06 00:14:33 +02:00
2017-09-12 21:09:06 +02:00
OS : : PowerState OS_Windows : : get_power_state ( ) {
2016-07-23 13:15:55 +02:00
return power_manager - > get_power_state ( ) ;
}
int OS_Windows : : get_power_seconds_left ( ) {
return power_manager - > get_power_seconds_left ( ) ;
}
int OS_Windows : : get_power_percent_left ( ) {
return power_manager - > get_power_percent_left ( ) ;
}
2017-07-19 22:00:46 +02:00
bool OS_Windows : : _check_internal_feature_support ( const String & p_feature ) {
2019-02-26 15:58:47 +01:00
return p_feature = = " pc " ;
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 ( ) ;
}
2019-03-03 21:12:19 +01:00
void OS_Windows : : process_and_drop_events ( ) {
2019-03-03 21:53:59 +01:00
drop_events = true ;
2019-03-03 21:12:19 +01:00
process_events ( ) ;
2019-03-03 21:53:59 +01:00
drop_events = false ;
2019-03-03 21:12:19 +01:00
}
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 ;
2018-08-18 00:30:22 +02:00
WCHAR * from = new WCHAR [ p_path . length ( ) + 2 ] ;
2018-10-04 15:38:52 +02:00
wcscpy_s ( from , p_path . length ( ) + 1 , p_path . c_str ( ) ) ;
2018-08-18 00:30:22 +02:00
from [ p_path . length ( ) + 1 ] = 0 ;
2017-09-25 15:15:11 +02:00
sf . hwnd = hWnd ;
sf . wFunc = FO_DELETE ;
2018-08-18 00:30:22 +02:00
sf . pFrom = from ;
2017-09-25 15:15:11 +02:00
sf . pTo = NULL ;
sf . fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION ;
sf . fAnyOperationsAborted = FALSE ;
sf . hNameMappings = NULL ;
sf . lpszProgressTitle = NULL ;
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 ) {
2021-06-16 12:56:25 +02:00
ERR_PRINT ( " SHFileOperation error: " + itos ( ret ) ) ;
2017-09-25 15:15:11 +02:00
return FAILED ;
}
return OK ;
}
2020-05-19 22:28:57 +02:00
int OS_Windows : : get_tablet_driver_count ( ) const {
return tablet_drivers . size ( ) ;
}
2020-05-22 09:44:23 +02:00
String OS_Windows : : get_tablet_driver_name ( int p_driver ) const {
2020-05-19 22:28:57 +02:00
if ( p_driver < 0 | | p_driver > = tablet_drivers . size ( ) ) {
return " " ;
} else {
2020-05-22 09:44:23 +02:00
return tablet_drivers [ p_driver ] ;
2020-05-19 22:28:57 +02:00
}
}
String OS_Windows : : get_current_tablet_driver ( ) const {
return tablet_driver ;
}
void OS_Windows : : set_current_tablet_driver ( const String & p_driver ) {
2020-05-26 20:03:45 +02:00
if ( get_tablet_driver_count ( ) = = 0 ) {
return ;
}
2020-05-19 22:28:57 +02:00
bool found = false ;
for ( int i = 0 ; i < get_tablet_driver_count ( ) ; i + + ) {
if ( p_driver = = get_tablet_driver_name ( i ) ) {
found = true ;
}
}
if ( found ) {
if ( hWnd ) {
2020-05-22 09:44:23 +02:00
block_mm = false ;
2020-05-19 22:28:57 +02:00
if ( ( tablet_driver = = " wintab " ) & & wintab_available & & wtctx ) {
wintab_WTEnable ( wtctx , false ) ;
wintab_WTClose ( wtctx ) ;
wtctx = 0 ;
}
if ( ( p_driver = = " wintab " ) & & wintab_available ) {
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wtlc ) ;
wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-18 13:27:02 +02:00
wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2020-05-19 22:28:57 +02:00
wtlc . lcPktMode = 0 ;
wtlc . lcOutOrgX = 0 ;
wtlc . lcOutExtX = wtlc . lcInExtX ;
wtlc . lcOutOrgY = 0 ;
wtlc . lcOutExtY = - wtlc . lcInExtY ;
wtctx = wintab_WTOpen ( hWnd , & wtlc , false ) ;
if ( wtctx ) {
wintab_WTEnable ( wtctx , true ) ;
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
min_pressure = int ( pressure . axMin ) ;
max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
wintab_WTEnable ( wtctx , true ) ;
} else {
print_verbose ( " WinTab context creation failed. " ) ;
}
}
}
tablet_driver = p_driver ;
} else {
ERR_PRINT ( " Unknown tablet driver " + p_driver + " . " ) ;
}
} ;
2014-02-10 02:10:30 +01:00
OS_Windows : : OS_Windows ( HINSTANCE _hInstance ) {
2019-03-03 21:12:19 +01:00
drop_events = false ;
2017-03-05 16:44:50 +01:00
key_event_pos = 0 ;
2017-12-10 19:38:26 +01:00
layered_window = false ;
2017-03-05 16:44:50 +01:00
force_quit = false ;
alt_mem = false ;
gr_mem = false ;
shift_mem = false ;
control_mem = false ;
meta_mem = false ;
2014-02-10 02:10:30 +01:00
minimized = false ;
2019-10-27 16:44:47 +01:00
was_maximized = false ;
2019-11-28 13:41:07 +01:00
window_focused = true ;
2014-02-10 02:10:30 +01:00
2020-05-05 13:53:30 +02:00
//Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
HMODULE wintab_lib = LoadLibraryW ( L " wintab32.dll " ) ;
if ( wintab_lib ) {
wintab_WTOpen = ( WTOpenPtr ) GetProcAddress ( wintab_lib , " WTOpenW " ) ;
wintab_WTClose = ( WTClosePtr ) GetProcAddress ( wintab_lib , " WTClose " ) ;
wintab_WTInfo = ( WTInfoPtr ) GetProcAddress ( wintab_lib , " WTInfoW " ) ;
wintab_WTPacket = ( WTPacketPtr ) GetProcAddress ( wintab_lib , " WTPacket " ) ;
wintab_WTEnable = ( WTEnablePtr ) GetProcAddress ( wintab_lib , " WTEnable " ) ;
wintab_available = wintab_WTOpen & & wintab_WTClose & & wintab_WTInfo & & wintab_WTPacket & & wintab_WTEnable ;
}
2020-05-19 22:28:57 +02:00
if ( wintab_available ) {
tablet_drivers . push_back ( " wintab " ) ;
}
2020-05-05 13:53:30 +02:00
//Note: Windows Ink API for pen input, available on Windows 8+ only.
2018-12-13 21:32:11 +01:00
HMODULE user32_lib = LoadLibraryW ( L " user32.dll " ) ;
if ( user32_lib ) {
win8p_GetPointerType = ( GetPointerTypePtr ) GetProcAddress ( user32_lib , " GetPointerType " ) ;
win8p_GetPointerPenInfo = ( GetPointerPenInfoPtr ) GetProcAddress ( user32_lib , " GetPointerPenInfo " ) ;
2020-05-19 22:28:57 +02:00
winink_available = win8p_GetPointerType & & win8p_GetPointerPenInfo ;
}
if ( winink_available ) {
tablet_drivers . push_back ( " winink " ) ;
2018-12-13 21:32:11 +01:00
}
2017-03-05 16:44:50 +01:00
hInstance = _hInstance ;
pressrc = 0 ;
old_invalid = true ;
mouse_mode = MOUSE_MODE_VISIBLE ;
2014-02-10 02:10:30 +01:00
# ifdef STDOUT_FILE
2017-03-05 16:44:50 +01:00
stdo = fopen ( " stdout.txt " , " wb " ) ;
2014-02-10 02:10:30 +01:00
# endif
user_proc = NULL ;
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
2022-09-21 16:14:10 +02: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 ;
2022-09-21 16:14:10 +02: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. " ) ;
}
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 ( ) {
2020-05-05 13:53:30 +02:00
if ( wintab_available & & wtctx ) {
wintab_WTClose ( wtctx ) ;
wtctx = 0 ;
}
2014-02-10 02:10:30 +01:00
# ifdef STDOUT_FILE
fclose ( stdo ) ;
# endif
}