2022-03-15 13:29:32 +01:00
/*************************************************************************/
/* os_windows.cpp */
/*************************************************************************/
/* This file is part of: */
2022-03-23 20:46:05 +01:00
/* PANDEMONIUM ENGINE */
2022-03-15 13:29:32 +01:00
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
# include "os_windows.h"
# include "core/io/marshalls.h"
# include "core/math/geometry.h"
# include "core/version_generated.gen.h"
# include "drivers/gles2/rasterizer_gles2.h"
# include "drivers/unix/net_socket_posix.h"
# include "drivers/windows/dir_access_windows.h"
# include "drivers/windows/file_access_windows.h"
# include "joypad_windows.h"
# include "lang_table.h"
# include "main/main.h"
2022-03-18 19:00:13 +01:00
# include "scene/resources/texture.h"
2022-03-15 13:29:32 +01:00
# include "servers/audio_server.h"
2022-08-19 21:24:36 +02:00
# include "servers/rendering/rendering_server_raster.h"
# include "servers/rendering/rendering_server_wrap_mt.h"
2022-03-15 13:29:32 +01:00
# include "windows_terminal_logger.h"
2023-09-10 13:30:56 +02:00
# include "sub_process_windows.h"
2022-03-15 13:29:32 +01:00
# include <avrt.h>
# include <direct.h>
# include <knownfolders.h>
# include <process.h>
# include <regstr.h>
# include <shlobj.h>
static const WORD MAX_CONSOLE_LINES = 1500 ;
extern " C " {
__declspec ( dllexport ) DWORD NvOptimusEnablement = 1 ;
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
}
// Workaround mingw-w64 < 4.0 bug
# ifndef WM_TOUCH
# define WM_TOUCH 576
# endif
# 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 ;
2022-03-15 13:29:32 +01: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 ;
}
2022-03-15 13:29:32 +01: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 ;
}
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 ,
NULL , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , NULL ) ;
2022-08-15 22:54:03 +02:00
String msg = " Error " + itos ( id ) + " : " + String : : utf16 ( ( const char16_t * ) messageBuffer , size ) ;
2022-03-15 13:29:32 +01:00
LocalFree ( messageBuffer ) ;
return msg ;
}
2022-03-23 20:46:05 +01:00
extern HINSTANCE pandemonium_hinstance ;
2022-03-15 13:29:32 +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.
}
}
}
void RedirectIOToConsole ( ) {
if ( AttachConsole ( ATTACH_PARENT_PROCESS ) ) {
RedirectStream ( " CONIN$ " , " r " , stdin , STD_INPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stdout , STD_OUTPUT_HANDLE ) ;
RedirectStream ( " CONOUT$ " , " w " , stderr , STD_ERROR_HANDLE ) ;
printf ( " \n " ) ; // Make sure our output is starting from the new line.
}
}
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 ;
}
}
// 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
bool OS_Windows : : winink_available = false ;
GetPointerTypePtr OS_Windows : : win8p_GetPointerType = NULL ;
GetPointerPenInfoPtr OS_Windows : : win8p_GetPointerPenInfo = NULL ;
void OS_Windows : : initialize_debugging ( ) {
SetConsoleCtrlHandler ( HandlerRoutine , TRUE ) ;
}
void OS_Windows : : initialize_core ( ) {
crash_handler . initialize ( ) ;
last_button_state = 0 ;
restore_mouse_trails = 0 ;
# ifndef WINDOWS_SUBSYSTEM_CONSOLE
RedirectIOToConsole ( ) ;
# endif
maximized = false ;
minimized = false ;
borderless = false ;
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 ) ;
2023-09-10 14:06:00 +02:00
SubProcess : : make_default < SubProcessWindows > ( ) ;
2023-09-10 13:30:56 +02:00
2022-03-15 13:29:32 +01:00
NetSocketPosix : : make_default ( ) ;
// We need to know how often the clock is updated
QueryPerformanceFrequency ( ( LARGE_INTEGER * ) & ticks_per_second ) ;
QueryPerformanceCounter ( ( LARGE_INTEGER * ) & ticks_start ) ;
// 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 ) ;
2023-01-15 19:12:50 +01:00
process_map = memnew ( ( RBMap < ProcessID , ProcessInfo > ) ) ;
2022-03-15 13:29:32 +01:00
2022-03-23 20:46:05 +01:00
// Add current Pandemonium PID to the list of known PIDs
2022-03-15 13:29:32 +01:00
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 ) ;
IP_Unix : : make_default ( ) ;
cursor_shape = CURSOR_ARROW ;
}
bool OS_Windows : : can_draw ( ) const {
return ! minimized ;
} ;
# define MI_WP_SIGNATURE 0xFF515700
# define SIGNATURE_MASK 0xFFFFFF00
// Keeping the name suggested by Microsoft, but this macro really answers:
// Is this mouse event emulated from touch or pen input?
# define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
// This one tells whether the event comes from touchscreen (and not from pen)
# define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
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 ) ;
}
Ref < InputEventScreenTouch > event ;
event . instance ( ) ;
event - > set_index ( idx ) ;
event - > set_pressed ( p_pressed ) ;
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
if ( main_loop ) {
input - > parse_input_event ( event ) ;
}
} ;
void OS_Windows : : _drag_event ( float p_x , float p_y , int idx ) {
2023-01-15 19:12:50 +01:00
RBMap < int , Vector2 > : : Element * curr = touch_state . find ( idx ) ;
2022-03-15 13:29:32 +01:00
// Defensive
if ( ! curr )
return ;
if ( curr - > get ( ) = = Vector2 ( p_x , p_y ) )
return ;
Ref < InputEventScreenDrag > event ;
event . instance ( ) ;
event - > set_index ( idx ) ;
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
event - > set_relative ( Vector2 ( p_x , p_y ) - curr - > get ( ) ) ;
if ( main_loop )
input - > parse_input_event ( event ) ;
curr - > get ( ) = Vector2 ( p_x , p_y ) ;
} ;
LRESULT OS_Windows : : WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
if ( drop_events ) {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
} else {
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
} ;
switch ( uMsg ) // Check For Windows Messages
{
case WM_SETFOCUS : {
window_has_focus = true ;
// Restore mouse mode
_set_mouse_mode_impl ( mouse_mode ) ;
break ;
}
case WM_KILLFOCUS : {
window_has_focus = false ;
// Release capture unconditionally because it can be set due to dragging, in addition to captured mode
ReleaseCapture ( ) ;
// Release every touch to avoid sticky points
2023-01-15 19:12:50 +01:00
for ( RBMap < int , Vector2 > : : Element * E = touch_state . front ( ) ; E ; E = E - > next ( ) ) {
2022-03-15 13:29:32 +01:00
_touch_event ( false , E - > get ( ) . x , E - > get ( ) . y , E - > key ( ) ) ;
}
touch_state . clear ( ) ;
break ;
}
case WM_ACTIVATE : // Watch For Window Activate Message
{
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 ) ;
window_focused = true ;
alt_mem = false ;
control_mem = false ;
shift_mem = false ;
2023-01-20 14:04:55 +01:00
gr_mem = false ;
2022-03-15 13:29:32 +01:00
} else { // WM_INACTIVE
input - > release_pressed_events ( ) ;
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_FOCUS_OUT ) ;
window_focused = false ;
alt_mem = false ;
} ;
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
wintab_WTEnable ( wtctx , GET_WM_ACTIVATE_STATE ( wParam , lParam ) ) ;
}
return 0 ; // Return To The Message Loop
}
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 ;
}
}
case WM_PAINT :
Main : : force_redraw ( ) ;
break ;
case WM_SYSCOMMAND : // Intercept System Commands
{
switch ( wParam ) // Check System Calls
{
case SC_SCREENSAVE : // Screensaver Trying To Start?
case SC_MONITORPOWER : // Monitor Trying To Enter Powersave?
return 0 ; // Prevent From Happening
case SC_KEYMENU :
if ( ( lParam > > 16 ) < = 0 )
return 0 ;
}
break ; // Exit
}
case WM_CLOSE : // Did We Receive A Close Message?
{
if ( main_loop )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_QUIT_REQUEST ) ;
//force_quit=true;
return 0 ; // Jump Back
}
case WM_MOUSELEAVE : {
old_invalid = true ;
outside = true ;
if ( main_loop & & mouse_mode ! = MOUSE_MODE_CAPTURED )
main_loop - > notification ( MainLoop : : NOTIFICATION_WM_MOUSE_EXIT ) ;
} break ;
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 ) ;
mm - > set_pressure ( ( raw - > data . mouse . ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN ) ? 1.0f : 0.0f ) ;
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 ) ) ;
if ( raw - > data . mouse . usFlags = = MOUSE_MOVE_RELATIVE ) {
mm - > set_relative ( Vector2 ( raw - > data . mouse . lLastX , raw - > data . mouse . lLastY ) ) ;
} 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 (
( double ( raw - > data . mouse . lLastX ) - 65536.0 / ( nScreenWidth ) ) * nScreenWidth / 65536.0 + nScreenLeft ,
( double ( raw - > data . mouse . lLastY ) - 65536.0 / ( nScreenHeight ) ) * nScreenHeight / 65536.0 + nScreenTop ) ;
POINT coords ; //client coords
coords . x = abs_pos . x ;
coords . y = abs_pos . y ;
ScreenToClient ( hWnd , & coords ) ;
mm - > set_relative ( Vector2 ( coords . x - old_x , coords . y - old_y ) ) ;
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 ) ) ;
*/
}
if ( window_has_focus & & main_loop & & mm - > get_relative ( ) ! = Vector2 ( ) )
input - > parse_input_event ( mm ) ;
}
delete [ ] lpb ;
} break ;
case WT_CSRCHANGE :
case WT_PROXIMITY : {
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
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 : {
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
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 ( ) ;
}
2022-07-29 20:31:16 +02:00
last_pen_inverted = packet . pkStatus & TPS_INVERT ;
2022-03-15 13:29:32 +01:00
POINT coords ;
GetCursorPos ( & coords ) ;
ScreenToClient ( hWnd , & coords ) ;
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED )
break ;
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_control ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift ( GetKeyState ( VK_SHIFT ) < 0 ) ;
mm - > set_alt ( alt_mem ) ;
2022-07-29 20:31:16 +02:00
mm - > set_pen_inverted ( last_pen_inverted ) ;
2022-03-15 13:29:32 +01: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 )
input - > parse_input_event ( mm ) ;
}
return 0 ;
}
} break ;
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 ;
case WM_POINTERUPDATE : {
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 ;
}
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.
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED )
break ;
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
2022-07-29 20:31:16 +02:00
mm - > set_pen_inverted ( pen_info . penFlags & ( PEN_FLAG_INVERTED | PEN_FLAG_ERASER ) ) ;
2022-03-15 13:29:32 +01: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 ) ) ;
}
mm - > set_control ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift ( GetKeyState ( VK_SHIFT ) < 0 ) ;
mm - > set_alt ( alt_mem ) ;
mm - > set_button_mask ( last_button_state ) ;
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 ) ) ;
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 )
input - > parse_input_event ( mm ) ;
return 0 ;
} break ;
case WM_MOUSEMOVE : {
if ( block_mm ) {
break ;
}
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
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.
if ( ! window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED )
break ;
Ref < InputEventMouseMotion > mm ;
mm . instance ( ) ;
mm - > set_control ( ( wParam & MK_CONTROL ) ! = 0 ) ;
mm - > set_shift ( ( wParam & MK_SHIFT ) ! = 0 ) ;
mm - > set_alt ( alt_mem ) ;
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available & & wtctx ) {
// 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-07-29 20:31:16 +02:00
last_pen_inverted = false ;
2022-03-15 13:29:32 +01:00
}
} else {
last_tilt = Vector2 ( ) ;
last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-07-29 20:31:16 +02:00
last_pen_inverted = false ;
2022-03-15 13:29:32 +01:00
}
mm - > set_pressure ( last_pressure ) ;
mm - > set_tilt ( last_tilt ) ;
2022-07-29 20:31:16 +02:00
mm - > set_pen_inverted ( last_pen_inverted ) ;
2022-03-15 13:29:32 +01:00
mm - > set_button_mask ( last_button_state ) ;
mm - > set_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
mm - > set_global_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
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 )
input - > parse_input_event ( mm ) ;
} break ;
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
if ( input - > is_emulating_mouse_from_touch ( ) ) {
// Universal translation enabled; ignore OS translations for left button
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
FALLTHROUGH ;
case WM_MBUTTONDOWN :
case WM_MBUTTONUP :
case WM_RBUTTONDOWN :
case WM_RBUTTONUP :
case WM_MOUSEWHEEL :
case WM_MOUSEHWHEEL :
case WM_LBUTTONDBLCLK :
case WM_MBUTTONDBLCLK :
case WM_RBUTTONDBLCLK :
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 ) ;
} break ;
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 ;
if ( motion > 0 ) {
mb - > set_button_index ( BUTTON_WHEEL_UP ) ;
} else {
mb - > set_button_index ( BUTTON_WHEEL_DOWN ) ;
}
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
} 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 ) ;
}
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
} 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 ;
default : {
return 0 ;
}
}
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;
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 ) ;
mb - > set_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & ! use_raw_input ) {
mb - > set_position ( Vector2 ( old_x , old_y ) ) ;
}
if ( uMsg ! = WM_MOUSEWHEEL & & uMsg ! = WM_MOUSEHWHEEL ) {
if ( mb - > is_pressed ( ) ) {
if ( + + pressrc > 0 & & mouse_mode ! = MOUSE_MODE_CAPTURED )
SetCapture ( hWnd ) ;
} else {
if ( - - pressrc < = 0 ) {
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
ReleaseCapture ( ) ;
}
pressrc = 0 ;
}
}
} else {
// for reasons unknown to mankind, wheel comes in screen coordinates
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 ) {
input - > parse_input_event ( mb ) ;
if ( mb - > is_pressed ( ) & & mb - > get_button_index ( ) > 3 & & mb - > get_button_index ( ) < 8 ) {
//send release for mouse wheel
Ref < InputEventMouseButton > mbd = mb - > duplicate ( ) ;
last_button_state & = ~ ( 1 < < ( mbd - > get_button_index ( ) - 1 ) ) ;
mbd - > set_button_mask ( last_button_state ) ;
mbd - > set_pressed ( false ) ;
input - > parse_input_event ( mbd ) ;
}
}
} break ;
case WM_MOVE : {
if ( ! IsIconic ( hWnd ) ) {
int x = LOWORD ( lParam ) ;
int y = HIWORD ( lParam ) ;
last_pos = Point2 ( x , y ) ;
}
} break ;
case WM_SIZE : {
// 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 ) ) ;
}
}
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 ;
}
//return 0; // Jump Back
} break ;
case WM_ENTERSIZEMOVE : {
input - > release_pressed_events ( ) ;
move_timer_id = SetTimer ( hWnd , 1 , USER_TIMER_MINIMUM , ( TIMERPROC ) NULL ) ;
} break ;
case WM_EXITSIZEMOVE : {
KillTimer ( hWnd , move_timer_id ) ;
} break ;
case WM_TIMER : {
if ( wParam = = move_timer_id ) {
process_key_events ( ) ;
if ( ! Main : : is_iterating ( ) ) {
Main : : iteration ( ) ;
}
}
} break ;
case WM_SYSKEYDOWN :
case WM_SYSKEYUP :
case WM_KEYUP :
case WM_KEYDOWN : {
if ( wParam = = VK_SHIFT )
shift_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
if ( wParam = = VK_CONTROL )
control_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
if ( wParam = = VK_MENU ) {
alt_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
if ( lParam & ( 1 < < 24 ) )
gr_mem = alt_mem ;
}
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 ) ;
}
}
/*
if ( wParam = = VK_WIN ) TODO wtf is this ?
meta_mem = uMsg = = WM_KEYDOWN ;
*/
FALLTHROUGH ;
}
case WM_CHAR : {
ERR_BREAK ( key_event_pos > = KEY_EVENT_BUFFER_SIZE ) ;
// Make sure we don't include modifiers for the modifier key itself.
KeyEvent ke ;
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 ;
ke . uMsg = uMsg ;
if ( ke . uMsg = = WM_SYSKEYDOWN )
ke . uMsg = WM_KEYDOWN ;
if ( ke . uMsg = = WM_SYSKEYUP )
ke . uMsg = WM_KEYUP ;
ke . wParam = wParam ;
ke . lParam = lParam ;
key_event_buffer [ key_event_pos + + ] = ke ;
} break ;
case WM_INPUTLANGCHANGEREQUEST : {
// FIXME: Do something?
} break ;
case WM_TOUCH : {
BOOL bHandled = FALSE ;
UINT cInputs = LOWORD ( wParam ) ;
PTOUCHINPUT pInputs = memnew_arr ( TOUCHINPUT , cInputs ) ;
if ( pInputs ) {
if ( GetTouchInputInfo ( ( HTOUCHINPUT ) lParam , cInputs , pInputs , sizeof ( TOUCHINPUT ) ) ) {
for ( UINT i = 0 ; i < cInputs ; i + + ) {
TOUCHINPUT ti = pInputs [ i ] ;
POINT touch_pos = {
TOUCH_COORD_TO_PIXEL ( ti . x ) ,
TOUCH_COORD_TO_PIXEL ( ti . y ) ,
} ;
ScreenToClient ( hWnd , & touch_pos ) ;
//do something with each touch input entry
if ( ti . dwFlags & TOUCHEVENTF_MOVE ) {
_drag_event ( touch_pos . x , touch_pos . y , ti . dwID ) ;
} else if ( ti . dwFlags & ( TOUCHEVENTF_UP | TOUCHEVENTF_DOWN ) ) {
_touch_event ( ti . dwFlags & TOUCHEVENTF_DOWN , touch_pos . x , touch_pos . y , ti . dwID ) ;
} ;
}
bHandled = TRUE ;
} else {
/* handle the error here */
}
memdelete_arr ( pInputs ) ;
} else {
/* handle the error here, probably out of memory */
}
if ( bHandled ) {
CloseTouchInputHandle ( ( HTOUCHINPUT ) lParam ) ;
return 0 ;
} ;
} break ;
case WM_DEVICECHANGE : {
joypad - > probe_joypads ( ) ;
} break ;
case WM_SETCURSOR : {
if ( LOWORD ( lParam ) = = HTCLIENT ) {
if ( window_has_focus & & ( mouse_mode = = MOUSE_MODE_HIDDEN | | mouse_mode = = MOUSE_MODE_CAPTURED ) ) {
//Hide the cursor
if ( hCursor = = NULL ) {
hCursor = SetCursor ( NULL ) ;
} else {
SetCursor ( NULL ) ;
}
} else {
if ( hCursor ! = NULL ) {
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
hCursor = NULL ;
}
}
}
} break ;
case WM_DROPFILES : {
HDROP hDropInfo = ( HDROP ) wParam ;
const int buffsize = 4096 ;
2022-08-15 22:54:03 +02:00
WCHAR buf [ buffsize ] ;
2022-03-15 13:29:32 +01:00
int fcount = DragQueryFileW ( hDropInfo , 0xFFFFFFFF , NULL , 0 ) ;
Vector < String > files ;
for ( int i = 0 ; i < fcount ; i + + ) {
DragQueryFileW ( hDropInfo , i , buf , buffsize ) ;
2022-08-15 22:54:03 +02:00
String file = String : : utf16 ( ( const char16_t * ) buf ) ;
2022-03-15 13:29:32 +01:00
files . push_back ( file ) ;
}
if ( files . size ( ) & & main_loop ) {
main_loop - > drop_files ( files , 0 ) ;
}
} break ;
default : {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
} ;
} ;
}
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
OS_Windows * os_win = static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) ;
if ( os_win )
return os_win - > WndProc ( hWnd , uMsg , wParam , lParam ) ;
else
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
void OS_Windows : : process_key_events ( ) {
for ( int i = 0 ; i < key_event_pos ; i + + ) {
KeyEvent & ke = key_event_buffer [ i ] ;
switch ( ke . uMsg ) {
case WM_CHAR : {
// 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 ) ) ) {
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 ) ) ) ;
2022-03-15 13:29:32 +01:00
k - > set_physical_scancode ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ;
k - > set_unicode ( ke . wParam ) ;
if ( k - > get_unicode ( ) & & gr_mem ) {
k - > set_alt ( false ) ;
k - > set_control ( false ) ;
}
if ( k - > get_unicode ( ) < 32 )
k - > set_unicode ( 0 ) ;
input - > parse_input_event ( k ) ;
}
//do nothing
} break ;
case WM_KEYUP :
case WM_KEYDOWN : {
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 ( ke . uMsg = = WM_KEYDOWN ) ;
if ( ( ke . lParam & ( 1 < < 24 ) ) & & ( ke . wParam = = VK_RETURN ) ) {
// Special case for Numpad Enter key
k - > set_scancode ( KEY_KP_ENTER ) ;
} else {
k - > set_scancode ( KeyMappingWindows : : get_keysym ( ke . wParam ) ) ;
}
k - > set_physical_scancode ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ;
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 ) ;
}
if ( k - > get_unicode ( ) < 32 )
k - > set_unicode ( 0 ) ;
k - > set_echo ( ( ke . uMsg = = WM_KEYDOWN & & ( ke . lParam & ( 1 < < 30 ) ) ) ) ;
input - > parse_input_event ( k ) ;
} break ;
}
}
key_event_pos = 0 ;
}
enum _MonitorDpiType {
MDT_Effective_DPI = 0 ,
MDT_Angular_DPI = 1 ,
MDT_Raw_DPI = 2 ,
MDT_Default = MDT_Effective_DPI
} ;
static int QueryDpiForMonitor ( HMONITOR hmon , _MonitorDpiType dpiType = MDT_Default ) {
int dpiX = 96 , dpiY = 96 ;
static HMODULE Shcore = NULL ;
typedef HRESULT ( WINAPI * GetDPIForMonitor_t ) ( HMONITOR hmonitor , _MonitorDpiType dpiType , UINT * dpiX , UINT * dpiY ) ;
static GetDPIForMonitor_t getDPIForMonitor = NULL ;
if ( Shcore = = NULL ) {
Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
getDPIForMonitor = Shcore ? ( GetDPIForMonitor_t ) GetProcAddress ( Shcore , " GetDpiForMonitor " ) : NULL ;
if ( ( Shcore = = NULL ) | | ( getDPIForMonitor = = NULL ) ) {
if ( Shcore )
FreeLibrary ( Shcore ) ;
Shcore = ( HMODULE ) INVALID_HANDLE_VALUE ;
}
}
UINT x = 0 , y = 0 ;
HRESULT hr = E_FAIL ;
if ( hmon & & ( Shcore ! = ( HMODULE ) INVALID_HANDLE_VALUE ) ) {
hr = getDPIForMonitor ( hmon , dpiType /*MDT_Effective_DPI*/ , & x , & y ) ;
if ( SUCCEEDED ( hr ) & & ( x > 0 ) & & ( y > 0 ) ) {
dpiX = ( int ) x ;
dpiY = ( int ) y ;
}
} else {
static int overallX = 0 , overallY = 0 ;
if ( overallX < = 0 | | overallY < = 0 ) {
HDC hdc = GetDC ( NULL ) ;
if ( hdc ) {
overallX = GetDeviceCaps ( hdc , LOGPIXELSX ) ;
overallY = GetDeviceCaps ( hdc , LOGPIXELSY ) ;
ReleaseDC ( NULL , hdc ) ;
}
}
if ( overallX > 0 & & overallY > 0 ) {
dpiX = overallX ;
dpiY = overallY ;
}
}
return ( dpiX + dpiY ) / 2 ;
}
typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_DPI_UNAWARE = 0 ,
SHC_PROCESS_SYSTEM_DPI_AWARE = 1 ,
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
} SHC_PROCESS_DPI_AWARENESS ;
int OS_Windows : : get_current_video_driver ( ) const {
return video_driver_index ;
}
Error OS_Windows : : initialize ( const VideoMode & p_desired , int p_video_driver , int p_audio_driver ) {
main_loop = NULL ;
outside = true ;
window_has_focus = true ;
WNDCLASSEXW wc ;
if ( is_hidpi_allowed ( ) ) {
HMODULE Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
if ( Shcore ! = NULL ) {
typedef HRESULT ( WINAPI * SetProcessDpiAwareness_t ) ( SHC_PROCESS_DPI_AWARENESS ) ;
SetProcessDpiAwareness_t SetProcessDpiAwareness = ( SetProcessDpiAwareness_t ) GetProcAddress ( Shcore , " SetProcessDpiAwareness " ) ;
if ( SetProcessDpiAwareness ) {
SetProcessDpiAwareness ( SHC_PROCESS_SYSTEM_DPI_AWARE ) ;
}
}
}
video_mode = p_desired ;
//printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false");
RECT WindowRect ;
WindowRect . left = 0 ;
WindowRect . right = video_mode . width ;
WindowRect . top = 0 ;
WindowRect . bottom = video_mode . height ;
memset ( & wc , 0 , sizeof ( WNDCLASSEXW ) ) ;
wc . cbSize = sizeof ( WNDCLASSEXW ) ;
wc . style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS ;
wc . lpfnWndProc = ( WNDPROC ) : : WndProc ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = 0 ;
//wc.hInstance = hInstance;
2022-03-23 20:46:05 +01:00
wc . hInstance = pandemonium_hinstance ? pandemonium_hinstance : GetModuleHandle ( NULL ) ;
2022-03-15 13:29:32 +01:00
wc . hIcon = LoadIcon ( NULL , IDI_WINLOGO ) ;
wc . hCursor = NULL ; //LoadCursor(NULL, IDC_ARROW);
wc . hbrBackground = NULL ;
wc . lpszMenuName = NULL ;
wc . lpszClassName = L " Engine " ;
if ( ! RegisterClassExW ( & wc ) ) {
MessageBox ( NULL , " Failed To Register The Window Class. " , " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
return ERR_UNAVAILABLE ;
}
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 ;
}
pre_fs_valid = true ;
if ( video_mode . fullscreen ) {
/* this returns DPI unaware size, commenting
DEVMODE current ;
memset ( & current , 0 , sizeof ( current ) ) ;
EnumDisplaySettings ( NULL , ENUM_CURRENT_SETTINGS , & current ) ;
WindowRect . right = current . dmPelsWidth ;
WindowRect . bottom = current . dmPelsHeight ;
*/
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 ( ) } ;
2022-03-15 13:29:32 +01:00
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
WindowRect . right = data . size . width ;
WindowRect . bottom = data . size . height ;
/* DEVMODE dmScreenSettings;
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 ;
} */
pre_fs_valid = false ;
// 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 ) ;
}
}
DWORD dwExStyle ;
DWORD dwStyle ;
if ( video_mode . fullscreen | | video_mode . borderless_window ) {
dwExStyle = WS_EX_APPWINDOW ;
dwStyle = WS_POPUP ;
} else {
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE ;
dwStyle = WS_OVERLAPPEDWINDOW ;
if ( ! video_mode . resizable ) {
dwStyle & = ~ WS_THICKFRAME ;
dwStyle & = ~ WS_MAXIMIZEBOX ;
}
}
AdjustWindowRectEx ( & WindowRect , dwStyle , FALSE , dwExStyle ) ;
char * windowid ;
# ifdef MINGW_ENABLED
2022-03-23 20:46:05 +01:00
windowid = getenv ( " PANDEMONIUM_WINDOWID " ) ;
2022-03-15 13:29:32 +01:00
# else
size_t len ;
2022-03-23 20:46:05 +01:00
_dupenv_s ( & windowid , & len , " PANDEMONIUM_WINDOWID " ) ;
2022-03-15 13:29:32 +01:00
# endif
if ( windowid ) {
// strtoull on mingw
# ifdef MINGW_ENABLED
hWnd = ( HWND ) strtoull ( windowid , NULL , 0 ) ;
# else
hWnd = ( HWND ) _strtoui64 ( windowid , NULL , 0 ) ;
# endif
free ( windowid ) ;
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 ) ;
} ;
GetWindowLongPtr ( hWnd , GWLP_WNDPROC ) ;
RECT rect ;
if ( ! GetClientRect ( hWnd , & rect ) ) {
MessageBoxW ( NULL , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
return ERR_UNAVAILABLE ;
} ;
video_mode . width = rect . right ;
video_mode . height = rect . bottom ;
video_mode . fullscreen = false ;
} else {
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 ) {
MessageBoxW ( NULL , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
return ERR_UNAVAILABLE ;
}
} ;
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 ) ;
}
if ( ( get_current_tablet_driver ( ) = = " wintab " ) & & wintab_available ) {
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wtlc ) ;
wtlc . lcOptions | = CXO_MESSAGES ;
2022-07-29 20:31:16 +02:00
wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2022-03-15 13:29:32 +01: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 {
print_verbose ( " WinTab context creation failed. " ) ;
}
} else {
wtctx = 0 ;
}
last_pressure = 0 ;
last_pressure_update = 0 ;
last_tilt = Vector2 ( ) ;
2022-07-29 20:31:16 +02:00
last_pen_inverted = false ;
2022-03-15 13:29:32 +01:00
# if defined(OPENGL_ENABLED)
2022-03-15 17:01:21 +01:00
bool gles2_context = true ;
//if (p_video_driver == VIDEO_DRIVER_GLES2) {
// gles2_context = true;
//}
2022-03-15 13:29:32 +01:00
bool editor = Engine : : get_singleton ( ) - > is_editor_hint ( ) ;
bool gl_initialization_error = false ;
gl_context = NULL ;
while ( ! gl_context ) {
2022-03-15 17:01:21 +01:00
gl_context = memnew ( ContextGL_Windows ( hWnd , ! gles2_context ) ) ;
2022-03-15 13:29:32 +01:00
if ( gl_context - > initialize ( ) ! = OK ) {
memdelete ( gl_context ) ;
gl_context = NULL ;
2022-03-15 17:01:21 +01:00
gl_initialization_error = true ;
break ;
2022-03-15 13:29:32 +01:00
}
}
while ( true ) {
2022-03-15 17:01:21 +01:00
if ( gles2_context ) {
2022-03-15 13:29:32 +01:00
if ( RasterizerGLES2 : : is_viable ( ) = = OK ) {
RasterizerGLES2 : : register_config ( ) ;
RasterizerGLES2 : : make_current ( ) ;
break ;
} else {
gl_initialization_error = true ;
break ;
}
2022-03-15 17:01:21 +01:00
} else {
break ;
2022-03-15 13:29:32 +01:00
}
}
if ( gl_initialization_error ) {
OS : : get_singleton ( ) - > alert ( " Your video card driver does not support any of the supported OpenGL versions. \n "
" Please update your drivers or if you have a very old or integrated GPU, upgrade it. " ,
" Unable to initialize Video driver " ) ;
return ERR_UNAVAILABLE ;
}
video_driver_index = p_video_driver ;
gl_context - > set_use_vsync ( video_mode . use_vsync ) ;
set_vsync_via_compositor ( video_mode . vsync_via_compositor ) ;
# endif
2022-08-19 21:24:36 +02:00
rendering_server = memnew ( RenderingServerRaster ) ;
2022-03-15 13:29:32 +01:00
if ( get_render_thread_mode ( ) ! = RENDER_THREAD_UNSAFE ) {
2022-08-19 21:24:36 +02:00
rendering_server = memnew ( RenderingServerWrapMT ( rendering_server , get_render_thread_mode ( ) = = RENDER_SEPARATE_THREAD ) ) ;
2022-03-15 13:29:32 +01:00
}
2022-08-19 21:24:36 +02:00
rendering_server - > init ( ) ;
2022-03-15 13:29:32 +01:00
input = memnew ( InputDefault ) ;
joypad = memnew ( JoypadWindows ( input , & hWnd ) ) ;
power_manager = memnew ( PowerWindows ) ;
AudioDriverManager : : initialize ( p_audio_driver ) ;
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( TRACKMOUSEEVENT ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
tme . dwHoverTime = HOVER_DEFAULT ;
TrackMouseEvent ( & tme ) ;
RegisterTouchWindow ( hWnd , 0 ) ;
DragAcceptFiles ( hWnd , true ) ;
move_timer_id = 1 ;
if ( ! is_no_window_mode_enabled ( ) ) {
ShowWindow ( hWnd , SW_SHOW ) ; // Show The Window
SetForegroundWindow ( hWnd ) ; // Slightly Higher Priority
SetFocus ( hWnd ) ; // Sets Keyboard Focus To
}
if ( p_desired . layered ) {
set_window_per_pixel_transparency_enabled ( true ) ;
}
// 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.
2022-03-15 13:29:32 +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 ) ;
}
update_real_mouse_position ( ) ;
return OK ;
}
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
}
void OS_Windows : : set_clipboard ( const String & p_text ) {
// Convert LF line endings to CRLF in clipboard content
// Otherwise, line endings won't be visible when pasted in other software
String text = p_text . replace ( " \r \n " , " \n " ) . replace ( " \n " , " \r \n " ) ; // avoid \r\r\n
if ( ! OpenClipboard ( hWnd ) ) {
ERR_FAIL_MSG ( " Unable to open clipboard. " ) ;
}
EmptyClipboard ( ) ;
2022-08-15 22:54:03 +02:00
Char16String utf16 = text . utf16 ( ) ;
HGLOBAL mem = GlobalAlloc ( GMEM_MOVEABLE , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND_MSG ( mem = = NULL , " Unable to allocate memory for clipboard contents. " ) ;
LPWSTR lptstrCopy = ( LPWSTR ) GlobalLock ( mem ) ;
2022-08-15 22:54:03 +02:00
memcpy ( lptstrCopy , utf16 . get_data ( ) , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_UNICODETEXT , mem ) ;
// set the CF_TEXT version (not needed?)
CharString utf8 = text . utf8 ( ) ;
mem = GlobalAlloc ( GMEM_MOVEABLE , utf8 . length ( ) + 1 ) ;
ERR_FAIL_COND_MSG ( mem = = NULL , " Unable to allocate memory for clipboard contents. " ) ;
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 ) ) {
ERR_FAIL_V_MSG ( " " , " Unable to open clipboard. " ) ;
} ;
if ( IsClipboardFormatAvailable ( CF_UNICODETEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
if ( mem ! = NULL ) {
LPWSTR ptr = ( LPWSTR ) GlobalLock ( mem ) ;
if ( ptr ! = NULL ) {
2022-08-15 22:54:03 +02:00
ret = String : : utf16 ( ( const char16_t * ) ptr ) ;
2022-03-15 13:29:32 +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 ) {
ret . parse_utf8 ( ( const char * ) ptr ) ;
GlobalUnlock ( mem ) ;
} ;
} ;
} ;
CloseClipboard ( ) ;
return ret ;
} ;
void OS_Windows : : delete_main_loop ( ) {
if ( main_loop )
memdelete ( main_loop ) ;
main_loop = NULL ;
}
void OS_Windows : : set_main_loop ( MainLoop * p_main_loop ) {
input - > set_main_loop ( p_main_loop ) ;
main_loop = p_main_loop ;
}
void OS_Windows : : finalize ( ) {
# ifdef WINMIDI_ENABLED
driver_midi . close ( ) ;
# endif
if ( main_loop )
memdelete ( main_loop ) ;
main_loop = NULL ;
memdelete ( joypad ) ;
memdelete ( input ) ;
touch_state . clear ( ) ;
2022-05-23 09:33:42 +02:00
icon . unref ( ) ;
2022-03-15 13:29:32 +01:00
cursors_cache . clear ( ) ;
2022-08-19 21:24:36 +02:00
rendering_server - > finish ( ) ;
memdelete ( rendering_server ) ;
2022-03-15 13:29:32 +01:00
# ifdef OPENGL_ENABLED
if ( gl_context )
memdelete ( gl_context ) ;
# endif
if ( user_proc ) {
SetWindowLongPtr ( hWnd , GWLP_WNDPROC , ( LONG_PTR ) user_proc ) ;
} ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
}
}
void OS_Windows : : finalize_core ( ) {
timeEndPeriod ( 1 ) ;
memdelete ( process_map ) ;
NetSocketPosix : : cleanup ( ) ;
}
void OS_Windows : : alert ( const String & p_alert , const String & p_title ) {
if ( is_no_window_mode_enabled ( ) ) {
print_line ( " ALERT: " + p_title + " : " + p_alert ) ;
return ;
}
2022-08-15 22:54:03 +02:00
MessageBoxW ( nullptr , ( LPCWSTR ) ( p_alert . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) ( p_title . utf16 ( ) . get_data ( ) ) , MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL ) ;
2022-03-15 13:29:32 +01:00
}
void OS_Windows : : set_mouse_mode ( MouseMode p_mode ) {
if ( mouse_mode = = p_mode )
return ;
mouse_mode = p_mode ;
_set_mouse_mode_impl ( p_mode ) ;
}
void OS_Windows : : _set_mouse_mode_impl ( MouseMode p_mode ) {
if ( p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_CONFINED ) {
RECT clipRect ;
GetClientRect ( hWnd , & clipRect ) ;
ClientToScreen ( hWnd , ( POINT * ) & clipRect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & clipRect . right ) ;
ClipCursor ( & clipRect ) ;
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 ) ;
SetCursorPos ( pos . x , pos . y ) ;
SetCapture ( hWnd ) ;
}
} else {
ReleaseCapture ( ) ;
ClipCursor ( NULL ) ;
}
if ( p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_HIDDEN ) {
if ( hCursor = = NULL ) {
hCursor = SetCursor ( NULL ) ;
} else {
SetCursor ( NULL ) ;
}
} else {
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
}
}
OS_Windows : : MouseMode OS_Windows : : get_mouse_mode ( ) const {
return mouse_mode ;
}
void OS_Windows : : warp_mouse_position ( const Point2 & p_to ) {
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
old_x = p_to . x ;
old_y = p_to . y ;
} else {
POINT p ;
p . x = p_to . x ;
p . y = p_to . y ;
ClientToScreen ( hWnd , & p ) ;
SetCursorPos ( p . x , p . y ) ;
}
}
Point2 OS_Windows : : get_mouse_position ( ) const {
return Point2 ( old_x , old_y ) ;
}
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 ) ) ;
}
}
}
int OS_Windows : : get_mouse_button_state ( ) const {
return last_button_state ;
}
void OS_Windows : : set_window_title ( const String & p_title ) {
2022-08-15 22:54:03 +02:00
SetWindowTextW ( hWnd , ( LPCWSTR ) ( p_title . utf16 ( ) . get_data ( ) ) ) ;
2022-03-15 13:29:32 +01: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 ) ;
}
}
void OS_Windows : : set_video_mode ( const VideoMode & p_video_mode , int p_screen ) {
}
OS : : VideoMode OS_Windows : : get_video_mode ( int p_screen ) const {
return video_mode ;
}
void OS_Windows : : get_fullscreen_mode_list ( List < VideoMode > * p_list , int p_screen ) const {
}
static BOOL CALLBACK _MonitorEnumProcCount ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
int * data = ( int * ) dwData ;
( * data ) + + ;
return TRUE ;
}
int OS_Windows : : get_screen_count ( ) const {
int data = 0 ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcCount , ( LPARAM ) & data ) ;
return data ;
}
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 ) ) ;
}
2022-03-15 13:29:32 +01: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 ;
}
Point2 OS_Windows : : get_screen_position ( int p_screen ) const {
EnumPosData data = { 0 , p_screen = = - 1 ? get_current_screen ( ) : p_screen , Point2 ( ) } ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcPos , ( LPARAM ) & data ) ;
return data . pos ;
}
Size2 OS_Windows : : get_screen_size ( int p_screen ) const {
EnumSizeData data = { 0 , p_screen = = - 1 ? get_current_screen ( ) : p_screen , Size2 ( ) } ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
return data . size ;
}
typedef struct {
int count ;
int screen ;
int dpi ;
} EnumDpiData ;
typedef struct {
int count ;
int screen ;
float rate ;
} EnumRefreshRateData ;
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 ;
}
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 ;
}
int OS_Windows : : get_screen_dpi ( int p_screen ) const {
EnumDpiData data = {
0 ,
p_screen = = - 1 ? get_current_screen ( ) : p_screen ,
72 ,
} ;
EnumDisplayMonitors ( NULL , NULL , _MonitorEnumProcDpi , ( LPARAM ) & data ) ;
return data . dpi ;
}
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 ;
}
Point2 OS_Windows : : get_window_position ( ) const {
if ( minimized ) {
return last_pos ;
}
RECT r ;
GetWindowRect ( hWnd , & r ) ;
return Point2 ( r . left , r . top ) ;
}
void OS_Windows : : set_window_position ( const Point2 & p_position ) {
if ( video_mode . fullscreen )
return ;
RECT r ;
GetWindowRect ( hWnd , & r ) ;
MoveWindow ( hWnd , p_position . x , p_position . y , r . right - r . left , r . bottom - r . top , TRUE ) ;
// Don't let the mouse leave the window when moved
if ( mouse_mode = = MOUSE_MODE_CONFINED ) {
RECT rect ;
GetClientRect ( hWnd , & rect ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . right ) ;
ClipCursor ( & rect ) ;
}
last_pos = p_position ;
update_real_mouse_position ( ) ;
}
Size2 OS_Windows : : get_window_size ( ) const {
if ( minimized ) {
return Size2 ( video_mode . width , video_mode . height ) ;
}
RECT r ;
if ( GetClientRect ( hWnd , & r ) ) { // Only area inside of window border
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
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 ) ) ) {
ERR_PRINT ( " Minimum window size can't be larger than maximum window size! " ) ;
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 ) ) ) {
ERR_PRINT ( " Maximum window size can't be smaller than minimum window size! " ) ;
return ;
}
max_size = p_size ;
}
Size2 OS_Windows : : get_real_window_size ( ) const {
RECT r ;
if ( GetWindowRect ( hWnd , & r ) ) { // Includes area of the window border
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
void OS_Windows : : set_window_size ( const Size2 p_size ) {
int w = p_size . width ;
int h = p_size . height ;
video_mode . width = w ;
video_mode . height = h ;
if ( video_mode . fullscreen ) {
return ;
}
RECT rect ;
GetWindowRect ( hWnd , & rect ) ;
if ( ! video_mode . borderless_window ) {
RECT crect ;
GetClientRect ( hWnd , & crect ) ;
w + = ( rect . right - rect . left ) - ( crect . right - crect . left ) ;
h + = ( rect . bottom - rect . top ) - ( crect . bottom - crect . top ) ;
}
MoveWindow ( hWnd , rect . left , rect . top , w , h , TRUE ) ;
// Don't let the mouse leave the window when resizing to a smaller resolution
if ( mouse_mode = = MOUSE_MODE_CONFINED ) {
RECT crect ;
GetClientRect ( hWnd , & crect ) ;
ClientToScreen ( hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
}
}
void OS_Windows : : set_window_fullscreen ( bool p_enabled ) {
if ( video_mode . fullscreen = = p_enabled )
return ;
if ( layered_window )
set_window_per_pixel_transparency_enabled ( false ) ;
if ( p_enabled ) {
was_maximized = maximized ;
if ( pre_fs_valid ) {
GetWindowRect ( hWnd , & pre_fs_rect ) ;
}
int cs = get_current_screen ( ) ;
Point2 pos = get_screen_position ( cs ) ;
Size2 size = get_screen_size ( cs ) ;
video_mode . fullscreen = true ;
_update_window_style ( false ) ;
MoveWindow ( hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
SystemParametersInfoA ( SPI_GETMOUSETRAILS , 0 , & restore_mouse_trails , 0 ) ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , 0 , 0 , 0 ) ;
}
} else {
RECT rect ;
video_mode . fullscreen = false ;
if ( pre_fs_valid ) {
rect = pre_fs_rect ;
} else {
rect . left = 0 ;
rect . right = video_mode . width ;
rect . top = 0 ;
rect . bottom = video_mode . height ;
}
_update_window_style ( false , was_maximized ) ;
MoveWindow ( hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
pre_fs_valid = true ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
}
}
}
bool OS_Windows : : is_window_fullscreen ( ) const {
return video_mode . fullscreen ;
}
void OS_Windows : : set_window_resizable ( bool p_enabled ) {
if ( video_mode . resizable = = p_enabled )
return ;
video_mode . resizable = p_enabled ;
_update_window_style ( ) ;
}
bool OS_Windows : : is_window_resizable ( ) const {
return video_mode . resizable ;
}
void OS_Windows : : set_window_minimized ( bool p_enabled ) {
if ( is_no_window_mode_enabled ( ) ) {
return ;
}
if ( p_enabled ) {
maximized = false ;
minimized = true ;
ShowWindow ( hWnd , SW_MINIMIZE ) ;
} else {
ShowWindow ( hWnd , SW_RESTORE ) ;
maximized = false ;
minimized = false ;
}
}
bool OS_Windows : : is_window_minimized ( ) const {
return minimized ;
}
void OS_Windows : : set_window_maximized ( bool p_enabled ) {
if ( is_no_window_mode_enabled ( ) ) {
return ;
}
if ( p_enabled ) {
maximized = true ;
minimized = false ;
ShowWindow ( hWnd , SW_MAXIMIZE ) ;
} else {
ShowWindow ( hWnd , SW_RESTORE ) ;
maximized = false ;
minimized = false ;
}
}
bool OS_Windows : : is_window_maximized ( ) const {
return maximized ;
}
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 ;
}
bool OS_Windows : : is_window_focused ( ) const {
return window_focused ;
}
bool OS_Windows : : get_window_per_pixel_transparency_enabled ( ) const {
if ( ! is_layered_allowed ( ) )
return false ;
return layered_window ;
}
void OS_Windows : : set_window_per_pixel_transparency_enabled ( bool p_enabled ) {
if ( ! is_layered_allowed ( ) )
return ;
if ( layered_window ! = p_enabled ) {
if ( p_enabled ) {
//enable per-pixel alpha
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 ) ;
layered_window = true ;
} else {
//disable per-pixel alpha
layered_window = false ;
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 ) ;
}
}
}
void OS_Windows : : set_borderless_window ( bool p_borderless ) {
if ( video_mode . borderless_window = = p_borderless )
return ;
video_mode . borderless_window = p_borderless ;
preserve_window_size = true ;
_update_window_style ( ) ;
_update_window_mouse_passthrough ( ) ;
}
bool OS_Windows : : get_borderless_window ( ) {
return video_mode . borderless_window ;
}
void OS_Windows : : _update_window_style ( bool p_repaint , bool p_maximized ) {
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 ) {
if ( p_maximized ) {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE ) ;
} else {
SetWindowLongPtr ( hWnd , GWL_STYLE , WS_OVERLAPPEDWINDOW | WS_VISIBLE ) ;
}
} 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 ) ;
}
2022-03-15 13:29:32 +01:00
SetWindowPos ( hWnd , video_mode . always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST , 0 , 0 , 0 , 0 , SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE ) ;
if ( p_repaint ) {
RECT rect ;
GetWindowRect ( hWnd , & rect ) ;
MoveWindow ( hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
}
}
Error OS_Windows : : open_dynamic_library ( const String p_path , void * & p_library_handle , bool p_also_set_library_path ) {
String path = p_path . replace ( " / " , " \\ " ) ;
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 ( ) ) ;
}
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 ) ) ;
DLL_DIRECTORY_COOKIE cookie = NULL ;
if ( p_also_set_library_path & & has_dll_directory_api ) {
2022-08-15 22:54:03 +02:00
cookie = add_dll_directory ( ( LPCWSTR ) ( path . get_base_dir ( ) . utf16 ( ) . get_data ( ) ) ) ;
2022-03-15 13:29:32 +01:00
}
2022-08-15 22:54:03 +02:00
p_library_handle = ( void * ) LoadLibraryExW ( ( LPCWSTR ) ( path . utf16 ( ) . get_data ( ) ) , nullptr , ( p_also_set_library_path & & has_dll_directory_api ) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0 ) ;
2022-03-15 13:29:32 +01:00
ERR_FAIL_COND_V_MSG ( ! p_library_handle , ERR_CANT_OPEN , " Can't open dynamic library: " + p_path + " , error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
if ( cookie ) {
remove_dll_directory ( cookie ) ;
}
return OK ;
}
Error OS_Windows : : close_dynamic_library ( void * p_library_handle ) {
if ( ! FreeLibrary ( ( HMODULE ) p_library_handle ) ) {
return FAILED ;
}
return OK ;
}
Error OS_Windows : : get_dynamic_library_symbol_handle ( void * p_library_handle , const String p_name , void * & p_symbol_handle , bool p_optional ) {
p_symbol_handle = ( void * ) GetProcAddress ( ( HMODULE ) p_library_handle , p_name . utf8 ( ) . get_data ( ) ) ;
if ( ! p_symbol_handle ) {
if ( ! p_optional ) {
ERR_FAIL_V_MSG ( ERR_CANT_RESOLVE , " Can't resolve symbol " + p_name + " , error: " + String : : num ( GetLastError ( ) ) + " . " ) ;
} else {
return ERR_CANT_RESOLVE ;
}
}
return OK ;
}
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 ) ;
}
void * OS_Windows : : get_native_handle ( int p_handle_type ) {
switch ( p_handle_type ) {
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 ;
}
}
String OS_Windows : : get_name ( ) const {
return " Windows " ;
}
OS : : Date OS_Windows : : get_date ( bool utc ) const {
SYSTEMTIME systemtime ;
if ( utc )
GetSystemTime ( & systemtime ) ;
else
GetLocalTime ( & systemtime ) ;
2022-03-20 18:59:27 +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 ;
}
2022-03-15 13:29:32 +01:00
Date date ;
date . day = systemtime . wDay ;
date . month = Month ( systemtime . wMonth ) ;
date . weekday = Weekday ( systemtime . wDayOfWeek ) ;
date . year = systemtime . wYear ;
2022-03-20 18:59:27 +01:00
date . dst = daylight ;
2022-03-15 13:29:32 +01:00
return date ;
}
OS : : Time OS_Windows : : get_time ( bool utc ) const {
SYSTEMTIME systemtime ;
if ( utc )
GetSystemTime ( & systemtime ) ;
else
GetLocalTime ( & systemtime ) ;
Time time ;
time . hour = systemtime . wHour ;
time . min = systemtime . wMinute ;
time . sec = systemtime . wSecond ;
return time ;
}
OS : : TimeZoneInfo OS_Windows : : get_time_zone_info ( ) const {
TIME_ZONE_INFORMATION info ;
bool daylight = false ;
if ( GetTimeZoneInformation ( & info ) = = TIME_ZONE_ID_DAYLIGHT )
daylight = true ;
2022-03-20 18:59:27 +01:00
// Daylight Bias needs to be added to the bias if DST is in effect, or else it will not properly update.
2022-03-15 13:29:32 +01:00
TimeZoneInfo ret ;
if ( daylight ) {
ret . name = info . DaylightName ;
2022-03-20 18:59:27 +01:00
ret . bias = info . Bias + info . DaylightBias ;
2022-03-15 13:29:32 +01:00
} else {
ret . name = info . StandardName ;
2022-03-20 18:59:27 +01:00
ret . bias = info . Bias + info . StandardBias ;
2022-03-15 13:29:32 +01:00
}
// Bias value returned by GetTimeZoneInformation is inverted of what we expect
2022-03-20 18:59:27 +01:00
// For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180
ret . bias = - ret . bias ;
2022-03-15 13:29:32 +01:00
return ret ;
}
uint64_t OS_Windows : : get_unix_time ( ) const {
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 ) ;
// Type punning through unions (rather than pointer cast) as per:
// https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks
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 ;
} ;
uint64_t OS_Windows : : get_system_time_secs ( ) const {
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 ;
SYSTEMTIME st ;
GetSystemTime ( & st ) ;
FILETIME ft ;
SystemTimeToFileTime ( & st , & ft ) ;
uint64_t ret ;
ret = ft . dwHighDateTime ;
ret < < = 32 ;
ret | = ft . dwLowDateTime ;
return ( uint64_t ) ( ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH ) ;
}
void OS_Windows : : delay_usec ( uint32_t p_usec ) const {
if ( p_usec < 1000 )
Sleep ( 1 ) ;
else
Sleep ( p_usec / 1000 ) ;
}
uint64_t OS_Windows : : get_ticks_usec ( ) const {
uint64_t ticks ;
// This is the number of clock ticks since start
QueryPerformanceCounter ( ( LARGE_INTEGER * ) & ticks ) ;
// Subtract the ticks at game start to get
// the ticks since the game started
ticks - = ticks_start ;
// Divide by frequency to get the time in seconds
// 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 ;
return time ;
}
void OS_Windows : : process_events ( ) {
MSG msg ;
if ( ! drop_events ) {
joypad - > process_joypads ( ) ;
}
while ( PeekMessageW ( & msg , NULL , 0 , 0 , PM_REMOVE ) ) {
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
if ( ! drop_events ) {
process_key_events ( ) ;
input - > flush_buffered_events ( ) ;
}
}
void OS_Windows : : set_cursor_shape ( CursorShape p_shape ) {
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
if ( cursor_shape = = p_shape )
return ;
if ( mouse_mode ! = MOUSE_MODE_VISIBLE & & mouse_mode ! = MOUSE_MODE_CONFINED ) {
cursor_shape = p_shape ;
return ;
}
static const LPCTSTR win_cursors [ CURSOR_MAX ] = {
IDC_ARROW ,
IDC_IBEAM ,
IDC_HAND , //finger
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
} ;
if ( cursors [ p_shape ] ! = NULL ) {
SetCursor ( cursors [ p_shape ] ) ;
} else {
SetCursor ( LoadCursor ( hInstance , win_cursors [ p_shape ] ) ) ;
}
cursor_shape = p_shape ;
}
OS : : CursorShape OS_Windows : : get_cursor_shape ( ) const {
return cursor_shape ;
}
void OS_Windows : : set_custom_mouse_cursor ( const RES & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
if ( p_cursor . is_valid ( ) ) {
2023-01-15 19:12:50 +01:00
RBMap < CursorShape , Vector < Variant > > : : Element * cursor_c = cursors_cache . find ( p_shape ) ;
2022-03-15 13:29:32 +01: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 ) ;
}
Ref < Texture > texture = p_cursor ;
Ref < AtlasTexture > atlas_texture = p_cursor ;
Ref < Image > image ;
Size2 texture_size ;
Rect2 atlas_rect ;
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 ( ) ;
}
ERR_FAIL_COND ( ! texture . is_valid ( ) ) ;
ERR_FAIL_COND ( p_hotspot . x < 0 | | p_hotspot . y < 0 ) ;
ERR_FAIL_COND ( texture_size . width > 256 | | texture_size . height > 256 ) ;
ERR_FAIL_COND ( p_hotspot . x > texture_size . width | | p_hotspot . y > texture_size . height ) ;
image = texture - > get_data ( ) ;
ERR_FAIL_COND ( ! image . is_valid ( ) ) ;
UINT image_size = texture_size . width * texture_size . height ;
// Create the BITMAP with alpha channel
COLORREF * buffer = ( COLORREF * ) memalloc ( sizeof ( COLORREF ) * image_size ) ;
image - > lock ( ) ;
for ( UINT index = 0 ; index < image_size ; index + + ) {
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 ) ;
}
* ( buffer + index ) = image - > get_pixel ( column_index , row_index ) . to_argb32 ( ) ;
}
image - > unlock ( ) ;
// Using 4 channels, so 4 * 8 bits
HBITMAP bitmap = CreateBitmap ( texture_size . width , texture_size . height , 1 , 4 * 8 , buffer ) ;
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 ) {
memfree ( buffer ) ;
DeleteObject ( bitmap ) ;
return ;
}
// Finally, create the icon
ICONINFO iconinfo ;
iconinfo . fIcon = FALSE ;
iconinfo . xHotspot = p_hotspot . x ;
iconinfo . yHotspot = p_hotspot . y ;
iconinfo . hbmMask = hAndMask ;
iconinfo . hbmColor = hXorMask ;
if ( cursors [ p_shape ] )
DestroyIcon ( cursors [ p_shape ] ) ;
cursors [ p_shape ] = CreateIconIndirect ( & iconinfo ) ;
Vector < Variant > params ;
params . push_back ( p_cursor ) ;
params . push_back ( p_hotspot ) ;
cursors_cache . insert ( p_shape , params ) ;
if ( p_shape = = cursor_shape ) {
if ( mouse_mode = = MOUSE_MODE_VISIBLE | | mouse_mode = = MOUSE_MODE_CONFINED ) {
SetCursor ( cursors [ p_shape ] ) ;
}
}
if ( hAndMask ! = NULL ) {
DeleteObject ( hAndMask ) ;
}
if ( hXorMask ! = NULL ) {
DeleteObject ( hXorMask ) ;
}
memfree ( buffer ) ;
DeleteObject ( bitmap ) ;
} else {
// Reset to default system cursor
if ( cursors [ p_shape ] ) {
DestroyIcon ( cursors [ p_shape ] ) ;
cursors [ p_shape ] = NULL ;
}
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
set_cursor_shape ( c ) ;
cursors_cache . erase ( p_shape ) ;
}
}
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 ) ;
}
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 ( ) ;
}
}
2022-03-15 13:29:32 +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 ) {
String path = p_path . replace ( " / " , " \\ " ) ;
String cmdline = _quote_command_line_argument ( path ) ;
const List < String > : : Element * I = p_arguments . front ( ) ;
while ( I ) {
cmdline + = " " + _quote_command_line_argument ( I - > get ( ) ) ;
I = I - > next ( ) ;
}
ProcessInfo pi ;
ZeroMemory ( & pi . si , sizeof ( pi . si ) ) ;
pi . si . cb = sizeof ( pi . si ) ;
ZeroMemory ( & pi . pi , sizeof ( pi . pi ) ) ;
LPSTARTUPINFOW si_w = ( LPSTARTUPINFOW ) & pi . si ;
2022-08-15 22:54:03 +02:00
Char16String modstr = cmdline . utf16 ( ) ; // Windows wants to change this no idea why.
2022-03-15 13:29:32 +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 ;
}
2023-08-05 20:24:32 +02:00
int ret = CreateProcessW ( nullptr , ( LPWSTR ) ( modstr . ptrw ( ) ) , nullptr , nullptr , inherit_handles , creaton_flags , nullptr , nullptr , si_w , & pi . pi ) ;
2022-03-15 13:29:32 +01:00
if ( ! ret & & r_pipe ) {
CloseHandle ( pipe [ 0 ] ) ; // Cleanup pipe handles.
CloseHandle ( pipe [ 1 ] ) ;
}
ERR_FAIL_COND_V ( ret = = 0 , ERR_CANT_FORK ) ;
if ( p_blocking ) {
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 ;
2022-03-15 13:29:32 +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 ) ;
2022-03-15 13:29:32 +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 ;
}
2022-03-15 13:29:32 +01:00
}
2022-05-10 12:27:45 +02:00
if ( newline_index = = - 1 ) {
bytes_in_buffer + = read ;
continue ;
2022-03-15 13:29:32 +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 ) ;
}
2022-03-15 13:29:32 +01:00
CloseHandle ( pipe [ 0 ] ) ; // Close pipe read handle.
}
2023-08-05 20:24:32 +02:00
2023-08-05 20:18:32 +02:00
WaitForSingleObject ( pi . pi . hProcess , INFINITE ) ;
2022-03-15 13:29:32 +01:00
if ( r_exitcode ) {
DWORD ret2 ;
GetExitCodeProcess ( pi . pi . hProcess , & ret2 ) ;
* r_exitcode = ret2 ;
}
CloseHandle ( pi . pi . hProcess ) ;
CloseHandle ( pi . pi . hThread ) ;
} else {
ProcessID pid = pi . pi . dwProcessId ;
if ( r_child_id ) {
* r_child_id = pid ;
}
process_map - > insert ( pid , pi ) ;
}
return OK ;
2022-05-10 12:27:45 +02:00
}
2022-03-15 13:29:32 +01:00
Error OS_Windows : : kill ( const ProcessID & p_pid ) {
ERR_FAIL_COND_V ( ! process_map - > has ( p_pid ) , FAILED ) ;
const PROCESS_INFORMATION pi = ( * process_map ) [ p_pid ] . pi ;
process_map - > erase ( p_pid ) ;
const int ret = TerminateProcess ( pi . hProcess , 0 ) ;
CloseHandle ( pi . hProcess ) ;
CloseHandle ( pi . hThread ) ;
return ret ! = 0 ? OK : FAILED ;
} ;
int OS_Windows : : get_process_id ( ) const {
return _getpid ( ) ;
}
2022-07-27 14:46:53 +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 ;
}
2022-03-15 13:29:32 +01:00
Error OS_Windows : : set_cwd ( const String & p_cwd ) {
2022-08-15 22:54:03 +02:00
if ( _wchdir ( ( LPCWSTR ) ( p_cwd . utf16 ( ) . get_data ( ) ) ) ! = 0 ) {
2022-03-15 13:29:32 +01:00
return ERR_CANT_OPEN ;
2022-08-15 22:54:03 +02:00
}
2022-03-15 13:29:32 +01:00
return OK ;
}
String OS_Windows : : get_executable_path ( ) const {
2022-08-15 22:54:03 +02:00
WCHAR bufname [ 4096 ] ;
2022-03-15 13:29:32 +01:00
GetModuleFileNameW ( NULL , bufname , 4096 ) ;
2022-08-15 22:54:03 +02:00
String s = String : : utf16 ( ( const char16_t * ) bufname ) ;
2022-03-15 13:29:32 +01:00
return s . replace ( " \\ " , " / " ) ;
}
void OS_Windows : : set_native_icon ( const String & p_filename ) {
FileAccess * f = FileAccess : : open ( p_filename , FileAccess : : READ ) ;
ERR_FAIL_COND_MSG ( ! f , " Cannot open file with icon ' " + p_filename + " '. " ) ;
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 ) ;
ERR_FAIL_COND_MSG ( icon_dir - > idType ! = 1 , " Invalid icon file format! " ) ;
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 ) ) ) ;
2022-03-15 13:29:32 +01: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 ;
}
}
}
ERR_FAIL_COND_MSG ( big_icon_index = = - 1 , " No valid icons found! " ) ;
if ( small_icon_index = = - 1 ) {
WARN_PRINT ( " No small icon found, reusing " + itos ( big_icon_width ) + " x " + itos ( big_icon_width ) + " @ " + itos ( big_icon_cc ) + " icon! " ) ;
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 ) ;
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 ( ) ) + " . " ) ;
// 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 ) ;
ERR_FAIL_COND_MSG ( ! icon_small , " Could not create 16x16 @ " + itos ( small_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
// 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 ( ) ;
ERR_FAIL_COND_MSG ( err , " Error setting ICON_SMALL: " + format_error_message ( err ) + " . " ) ;
SendMessage ( hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) icon_big ) ;
err = GetLastError ( ) ;
ERR_FAIL_COND_MSG ( err , " Error setting ICON_BIG: " + format_error_message ( err ) + " . " ) ;
memdelete ( f ) ;
memdelete ( icon_dir ) ;
}
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 ) ;
}
}
2022-03-15 13:29:32 +01:00
int w = icon - > get_width ( ) ;
int h = icon - > get_height ( ) ;
/* Create temporary bitmap buffer */
int icon_len = 40 + h * w * 4 ;
Vector < BYTE > v ;
v . resize ( icon_len ) ;
BYTE * icon_bmp = v . ptrw ( ) ;
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 ] ;
PoolVector < uint8_t > : : Read r = icon - > get_data ( ) . read ( ) ;
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 ] ;
}
}
HICON hicon = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
/* Set the icon for the window */
SendMessage ( hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) hicon ) ;
/* Set the icon in the task manager (should we do this?) */
SendMessage ( hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) hicon ) ;
}
bool OS_Windows : : has_environment ( const String & p_var ) const {
# ifdef MINGW_ENABLED
2022-08-15 22:54:03 +02:00
return _wgetenv ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ! = nullptr ;
2022-03-15 13:29:32 +01:00
# else
2022-08-15 22:54:03 +02:00
WCHAR * env ;
2022-03-15 13:29:32 +01:00
size_t len ;
2022-08-15 22:54:03 +02:00
_wdupenv_s ( & env , & len , ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) ) ;
2022-03-15 13:29:32 +01:00
const bool has_env = env ! = NULL ;
free ( env ) ;
return has_env ;
# endif
} ;
String OS_Windows : : get_environment ( const String & p_var ) const {
2022-08-15 22:54:03 +02:00
WCHAR wval [ 0x7fff ] ; // MSDN says 32767 char is the maximum
int wlen = GetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , wval , 0x7fff ) ;
2022-03-15 13:29:32 +01:00
if ( wlen > 0 ) {
2022-08-15 22:54:03 +02:00
return String : : utf16 ( ( const char16_t * ) wval ) ;
2022-03-15 13:29:32 +01:00
}
return " " ;
}
bool OS_Windows : : set_environment ( const String & p_var , const String & p_value ) const {
2022-08-15 22:54:03 +02:00
return ( bool ) SetEnvironmentVariableW ( ( LPCWSTR ) ( p_var . utf16 ( ) . get_data ( ) ) , ( LPCWSTR ) ( p_value . utf16 ( ) . get_data ( ) ) ) ;
2022-03-15 13:29:32 +01:00
}
2023-02-07 03:09:55 +01:00
String OS_Windows : : get_stdin_string ( ) {
2023-08-05 20:33:48 +02:00
WCHAR buff [ 1024 ] ;
DWORD count = 0 ;
if ( ReadConsoleW ( GetStdHandle ( STD_INPUT_HANDLE ) , buff , 1024 , & count , nullptr ) ) {
return String : : utf16 ( ( const char16_t * ) buff , count ) ;
}
2022-03-15 13:29:32 +01:00
}
void OS_Windows : : enable_for_stealing_focus ( ProcessID pid ) {
AllowSetForegroundWindow ( pid ) ;
}
void OS_Windows : : move_window_to_foreground ( ) {
SetForegroundWindow ( hWnd ) ;
}
Error OS_Windows : : shell_open ( String p_uri ) {
2022-08-15 22:54:03 +02:00
INT_PTR ret = ( INT_PTR ) ShellExecuteW ( nullptr , nullptr , ( LPCWSTR ) ( p_uri . utf16 ( ) . get_data ( ) ) , nullptr , nullptr , SW_SHOWNORMAL ) ;
2022-03-15 13:29:32 +01:00
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 ;
}
}
}
String OS_Windows : : get_locale ( ) const {
const _WinLocale * wl = & _win_locales [ 0 ] ;
LANGID langid = GetUserDefaultUILanguage ( ) ;
String neutral ;
int lang = PRIMARYLANGID ( langid ) ;
int sublang = SUBLANGID ( langid ) ;
while ( wl - > locale ) {
if ( wl - > main_lang = = lang & & wl - > sublang = = SUBLANG_NEUTRAL )
neutral = wl - > locale ;
if ( lang = = wl - > main_lang & & sublang = = wl - > sublang )
return String ( wl - > locale ) . replace ( " - " , " _ " ) ;
wl + + ;
}
if ( neutral ! = " " )
return String ( neutral ) . replace ( " - " , " _ " ) ;
return " en " ;
}
// We need this because GetSystemInfo() is unreliable on WOW64
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724381(v=vs.85).aspx
// Taken from MSDN
typedef BOOL ( WINAPI * LPFN_ISWOW64PROCESS ) ( HANDLE , PBOOL ) ;
LPFN_ISWOW64PROCESS fnIsWow64Process ;
BOOL is_wow64 ( ) {
BOOL wow64 = FALSE ;
fnIsWow64Process = ( LPFN_ISWOW64PROCESS ) GetProcAddress ( GetModuleHandle ( TEXT ( " kernel32 " ) ) , " IsWow64Process " ) ;
if ( fnIsWow64Process ) {
if ( ! fnIsWow64Process ( GetCurrentProcess ( ) , & wow64 ) ) {
wow64 = FALSE ;
}
}
return wow64 ;
}
int OS_Windows : : get_processor_count ( ) const {
SYSTEM_INFO sysinfo ;
if ( is_wow64 ( ) )
GetNativeSystemInfo ( & sysinfo ) ;
else
GetSystemInfo ( & sysinfo ) ;
return sysinfo . dwNumberOfProcessors ;
}
String OS_Windows : : get_processor_name ( ) const {
const String id = " Hardware \\ Description \\ System \\ CentralProcessor \\ 0 " ;
HKEY hkey ;
2022-08-15 22:54:03 +02:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) ( id . utf16 ( ) . get_data ( ) ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
2022-03-15 13:29:32 +01:00
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. " ) ) ;
}
}
OS : : LatinKeyboardVariant OS_Windows : : get_latin_keyboard_variant ( ) const {
unsigned long azerty [ ] = {
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
} ;
char name [ KL_NAMELENGTH + 1 ] ;
name [ 0 ] = 0 ;
GetKeyboardLayoutNameA ( name ) ;
unsigned long hex = strtoul ( name , NULL , 16 ) ;
int i = 0 ;
while ( azerty [ i ] ! = 0 ) {
if ( azerty [ i ] = = hex )
return LATIN_KEYBOARD_AZERTY ;
i + + ;
}
i = 0 ;
while ( qwertz [ i ] ! = 0 ) {
if ( qwertz [ i ] = = hex )
return LATIN_KEYBOARD_QWERTZ ;
i + + ;
}
i = 0 ;
while ( dvorak [ i ] ! = 0 ) {
if ( dvorak [ i ] = = hex )
return LATIN_KEYBOARD_DVORAK ;
i + + ;
}
return LATIN_KEYBOARD_QWERTY ;
}
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 ) ;
2022-08-15 22:54:03 +02:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
memfree ( layouts ) ;
2022-08-15 22:54:03 +02:00
return String : : utf16 ( ( const char16_t * ) buf ) . substr ( 0 , 2 ) ;
2022-03-15 13:29:32 +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 ) {
2022-03-23 20:46:05 +01:00
// Pandemonium uses 'braces' instead of 'brackets'
2022-03-15 13:29:32 +01:00
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 ) ;
}
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 ;
2022-08-15 22:54:03 +02:00
WCHAR layout_text [ 1024 ] ;
memset ( layout_text , 0 , 1024 * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
2022-08-15 22:54:03 +02:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) ( id . utf16 ( ) . get_data ( ) ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
2022-03-15 13:29:32 +01:00
return ret ;
}
DWORD buffer = 1024 ;
DWORD vtype = REG_SZ ;
if ( RegQueryValueExW ( hkey , L " Layout Text " , NULL , & vtype , ( LPBYTE ) layout_text , & buffer ) = = ERROR_SUCCESS ) {
2022-08-15 22:54:03 +02:00
ret = String : : utf16 ( ( const char16_t * ) layout_text ) ;
2022-03-15 13:29:32 +01:00
}
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 ( ) ) {
2022-08-15 22:54:03 +02:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
2022-08-15 22:54:03 +02:00
WCHAR name [ 1024 ] ;
memset ( name , 0 , 1024 * sizeof ( WCHAR ) ) ;
2022-03-15 13:29:32 +01:00
GetLocaleInfoEx ( buf , LOCALE_SLOCALIZEDDISPLAYNAME , ( LPWSTR ) & name , 1024 ) ;
2022-08-15 22:54:03 +02:00
ret = String : : utf16 ( ( const char16_t * ) name ) ;
2022-03-15 13:29:32 +01:00
}
memfree ( layouts ) ;
return ret ;
}
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 ( ) ;
}
void OS_Windows : : force_process_input ( ) {
process_events ( ) ; // get rid of pending events
}
2023-09-10 09:59:50 +02:00
void OS_Windows : : main_run ( ) {
2022-03-15 13:29:32 +01:00
if ( ! main_loop )
return ;
main_loop - > init ( ) ;
while ( ! force_quit ) {
process_events ( ) ; // get rid of pending events
if ( Main : : iteration ( ) )
break ;
} ;
main_loop - > finish ( ) ;
}
MainLoop * OS_Windows : : get_main_loop ( ) const {
return main_loop ;
}
2022-07-27 09:33:39 +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 ;
}
2022-03-15 13:29:32 +01:00
String OS_Windows : : get_config_path ( ) const {
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if ( has_environment ( " XDG_CONFIG_HOME " ) ) {
if ( get_environment ( " XDG_CONFIG_HOME " ) . is_abs_path ( ) ) {
return get_environment ( " XDG_CONFIG_HOME " ) . replace ( " \\ " , " / " ) ;
} else {
WARN_PRINT_ONCE ( " `XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification. " ) ;
}
}
if ( has_environment ( " APPDATA " ) ) {
return get_environment ( " APPDATA " ) . replace ( " \\ " , " / " ) ;
}
return " . " ;
}
String OS_Windows : : get_data_path ( ) const {
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if ( has_environment ( " XDG_DATA_HOME " ) ) {
if ( get_environment ( " XDG_DATA_HOME " ) . is_abs_path ( ) ) {
return get_environment ( " XDG_DATA_HOME " ) . replace ( " \\ " , " / " ) ;
} else {
WARN_PRINT_ONCE ( " `XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification. " ) ;
}
}
return get_config_path ( ) ;
}
String OS_Windows : : get_cache_path ( ) const {
static String cache_path_cache ;
if ( cache_path_cache = = String ( ) ) {
// The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well.
if ( has_environment ( " XDG_CACHE_HOME " ) ) {
if ( get_environment ( " XDG_CACHE_HOME " ) . is_abs_path ( ) ) {
cache_path_cache = get_environment ( " XDG_CACHE_HOME " ) . replace ( " \\ " , " / " ) ;
} else {
WARN_PRINT_ONCE ( " `XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%LOCALAPPDATA% \\ cache`, `%TEMP%` or `get_config_path()` per the XDG Base Directory specification. " ) ;
}
}
if ( cache_path_cache = = String ( ) & & has_environment ( " LOCALAPPDATA " ) ) {
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 ( ) ;
}
}
return cache_path_cache ;
}
// Get properly capitalized engine name for system paths
2022-03-23 20:46:05 +01:00
String OS_Windows : : get_pandemonium_dir_name ( ) const {
2022-03-15 13:29:32 +01:00
return String ( VERSION_SHORT_NAME ) . capitalize ( ) ;
}
String OS_Windows : : get_system_dir ( SystemDir p_dir , bool p_shared_storage ) const {
KNOWNFOLDERID id ;
switch ( p_dir ) {
case SYSTEM_DIR_DESKTOP : {
id = FOLDERID_Desktop ;
} break ;
case SYSTEM_DIR_DCIM : {
id = FOLDERID_Pictures ;
} break ;
case SYSTEM_DIR_DOCUMENTS : {
id = FOLDERID_Documents ;
} break ;
case SYSTEM_DIR_DOWNLOADS : {
id = FOLDERID_Downloads ;
} break ;
case SYSTEM_DIR_MOVIES : {
id = FOLDERID_Videos ;
} break ;
case SYSTEM_DIR_MUSIC : {
id = FOLDERID_Music ;
} break ;
case SYSTEM_DIR_PICTURES : {
id = FOLDERID_Pictures ;
} break ;
case SYSTEM_DIR_RINGTONES : {
id = FOLDERID_Music ;
} break ;
}
PWSTR szPath ;
HRESULT res = SHGetKnownFolderPath ( id , 0 , NULL , & szPath ) ;
ERR_FAIL_COND_V ( res ! = S_OK , String ( ) ) ;
2022-08-15 22:54:03 +02:00
String path = String : : utf16 ( ( const char16_t * ) szPath ) . replace ( " \\ " , " / " ) ;
2022-03-15 13:29:32 +01:00
CoTaskMemFree ( szPath ) ;
return path ;
}
String OS_Windows : : get_user_data_dir ( ) const {
String appname = get_safe_dir_name ( ProjectSettings : : get_singleton ( ) - > get ( " application/config/name " ) ) ;
if ( appname ! = " " ) {
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 ( " \\ " , " / " ) ;
} else {
2022-03-23 20:46:05 +01:00
return get_data_path ( ) . plus_file ( get_pandemonium_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( appname ) . replace ( " \\ " , " / " ) ;
2022-03-15 13:29:32 +01:00
}
}
2022-03-23 20:46:05 +01:00
return get_data_path ( ) . plus_file ( get_pandemonium_dir_name ( ) ) . plus_file ( " app_userdata " ) . plus_file ( " [unnamed project] " ) ;
2022-03-15 13:29:32 +01:00
}
String OS_Windows : : get_unique_id ( ) const {
HW_PROFILE_INFO HwProfInfo ;
ERR_FAIL_COND_V ( ! GetCurrentHwProfile ( & HwProfInfo ) , " " ) ;
2022-08-15 22:54:03 +02:00
return String : : utf16 ( ( const char16_t * ) ( HwProfInfo . szHwProfileGuid ) , HW_PROFILE_GUIDLEN ) ;
2022-03-15 13:29:32 +01: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 ) ;
}
}
void OS_Windows : : set_ime_position ( const Point2 & p_pos ) {
im_position = p_pos ;
HIMC himc = ImmGetContext ( hWnd ) ;
if ( himc = = ( HIMC ) 0 )
return ;
COMPOSITIONFORM cps ;
cps . dwStyle = CFS_FORCE_POSITION ;
cps . ptCurrentPos . x = im_position . x ;
cps . ptCurrentPos . y = im_position . y ;
ImmSetCompositionWindow ( himc , & cps ) ;
ImmReleaseContext ( hWnd , himc ) ;
}
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 ) ;
}
void OS_Windows : : _set_use_vsync ( bool p_enable ) {
if ( gl_context )
gl_context - > set_use_vsync ( p_enable ) ;
}
/*
bool OS_Windows : : is_vsync_enabled ( ) const {
if ( gl_context )
return gl_context - > is_using_vsync ( ) ;
return true ;
} */
OS : : PowerState OS_Windows : : get_power_state ( ) {
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 ( ) ;
}
bool OS_Windows : : _check_internal_feature_support ( const String & p_feature ) {
return p_feature = = " pc " ;
}
void OS_Windows : : disable_crash_handler ( ) {
crash_handler . disable ( ) ;
}
bool OS_Windows : : is_disable_crash_handler ( ) const {
return crash_handler . is_disabled ( ) ;
}
void OS_Windows : : process_and_drop_events ( ) {
drop_events = true ;
process_events ( ) ;
drop_events = false ;
}
Error OS_Windows : : move_to_trash ( const String & p_path ) {
SHFILEOPSTRUCTW sf ;
2022-08-15 22:54:03 +02:00
Char16String utf16 = p_path . utf16 ( ) ;
WCHAR * from = new WCHAR [ utf16 . length ( ) + 2 ] ;
wcscpy_s ( from , utf16 . length ( ) + 1 , ( LPCWSTR ) ( utf16 . get_data ( ) ) ) ;
from [ utf16 . length ( ) + 1 ] = 0 ;
2022-03-15 13:29:32 +01:00
sf . hwnd = hWnd ;
sf . wFunc = FO_DELETE ;
sf . pFrom = from ;
sf . pTo = NULL ;
sf . fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION ;
sf . fAnyOperationsAborted = FALSE ;
sf . hNameMappings = NULL ;
sf . lpszProgressTitle = NULL ;
int ret = SHFileOperationW ( & sf ) ;
delete [ ] from ;
if ( ret ) {
ERR_PRINT ( " SHFileOperation error: " + itos ( ret ) ) ;
return FAILED ;
}
return OK ;
}
int OS_Windows : : get_tablet_driver_count ( ) const {
return tablet_drivers . size ( ) ;
}
String OS_Windows : : get_tablet_driver_name ( int p_driver ) const {
if ( p_driver < 0 | | p_driver > = tablet_drivers . size ( ) ) {
return " " ;
} else {
return tablet_drivers [ p_driver ] ;
}
}
String OS_Windows : : get_current_tablet_driver ( ) const {
return tablet_driver ;
}
void OS_Windows : : set_current_tablet_driver ( const String & p_driver ) {
if ( get_tablet_driver_count ( ) = = 0 ) {
return ;
}
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 ) {
block_mm = false ;
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-07-29 20:31:16 +02:00
wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2022-03-15 13:29:32 +01: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 + " . " ) ;
}
} ;
OS_Windows : : OS_Windows ( HINSTANCE _hInstance ) {
drop_events = false ;
key_event_pos = 0 ;
layered_window = false ;
force_quit = false ;
alt_mem = false ;
gr_mem = false ;
shift_mem = false ;
control_mem = false ;
meta_mem = false ;
minimized = false ;
was_maximized = false ;
window_focused = true ;
2022-07-29 20:31:16 +02:00
pen_inverted = false ;
block_mm = false ;
last_pen_inverted = false ;
2022-03-15 13:29:32 +01: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 ;
}
if ( wintab_available ) {
tablet_drivers . push_back ( " wintab " ) ;
}
//Note: Windows Ink API for pen input, available on Windows 8+ only.
HMODULE user32_lib = LoadLibraryW ( L " user32.dll " ) ;
if ( user32_lib ) {
win8p_GetPointerType = ( GetPointerTypePtr ) GetProcAddress ( user32_lib , " GetPointerType " ) ;
win8p_GetPointerPenInfo = ( GetPointerPenInfoPtr ) GetProcAddress ( user32_lib , " GetPointerPenInfo " ) ;
winink_available = win8p_GetPointerType & & win8p_GetPointerPenInfo ;
}
if ( winink_available ) {
tablet_drivers . push_back ( " winink " ) ;
}
hInstance = _hInstance ;
pressrc = 0 ;
old_invalid = true ;
mouse_mode = MOUSE_MODE_VISIBLE ;
# ifdef STDOUT_FILE
stdo = fopen ( " stdout.txt " , " wb " ) ;
# endif
user_proc = NULL ;
# ifdef WASAPI_ENABLED
AudioDriverManager : : add_driver ( & driver_wasapi ) ;
# endif
# ifdef XAUDIO2_ENABLED
AudioDriverManager : : add_driver ( & driver_xaudio2 ) ;
# endif
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. " ) ;
}
2022-03-15 13:29:32 +01:00
Vector < Logger * > loggers ;
loggers . push_back ( memnew ( WindowsTerminalLogger ) ) ;
_set_logger ( memnew ( CompositeLogger ( loggers ) ) ) ;
}
OS_Windows : : ~ OS_Windows ( ) {
if ( wintab_available & & wtctx ) {
wintab_WTClose ( wtctx ) ;
wtctx = 0 ;
}
# ifdef STDOUT_FILE
fclose ( stdo ) ;
# endif
}