2020-03-28 12:29:29 +00:00
/**************************************************************************/
/* display_server_windows.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2020-03-09 15:56:48 +00:00
# include "display_server_windows.h"
2020-06-29 09:31:36 +00:00
2023-06-08 12:51:32 +00:00
# include "os_windows.h"
2021-11-12 12:49:49 +00:00
# include "wgl_detect_version.h"
2023-06-08 12:51:32 +00:00
2023-07-17 09:12:24 +00:00
# include "core/config/project_settings.h"
2020-03-09 15:56:48 +00:00
# include "core/io/marshalls.h"
2023-12-07 20:51:53 +00:00
# include "core/version.h"
2022-08-02 12:05:37 +00:00
# include "drivers/png/png_driver_common.h"
2020-03-09 15:56:48 +00:00
# include "main/main.h"
2023-12-19 17:57:56 +00:00
# if defined(VULKAN_ENABLED)
# include "rendering_context_driver_vulkan_windows.h"
# endif
# if defined(D3D12_ENABLED)
# include "drivers/d3d12/rendering_context_driver_d3d12.h"
# endif
2023-06-08 12:51:32 +00:00
# if defined(GLES3_ENABLED)
# include "drivers/gles3/rasterizer_gles3.h"
# endif
2020-03-09 15:56:48 +00:00
# include <avrt.h>
2022-08-29 08:24:48 +00:00
# include <dwmapi.h>
2023-12-07 20:51:53 +00:00
# include <propkey.h>
# include <propvarutil.h>
# include <shellapi.h>
2023-07-17 09:12:24 +00:00
# include <shlwapi.h>
# include <shobjidl.h>
2024-01-09 17:26:23 +00:00
# include <wbemcli.h>
2022-08-29 08:24:48 +00:00
# ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
# define DWMWA_USE_IMMERSIVE_DARK_MODE 20
# endif
2020-03-09 15:56:48 +00:00
2024-01-24 04:44:40 +00:00
# ifndef DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
# define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
# endif
2023-08-03 09:45:56 +00:00
# define WM_INDICATOR_CALLBACK_MESSAGE (WM_USER + 1)
2021-11-10 14:09:56 +00:00
# if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
# define GetProcAddress (void *)GetProcAddress
# endif
2020-03-09 15:56:48 +00:00
static String format_error_message ( DWORD id ) {
2020-04-01 23:20:12 +00:00
LPWSTR messageBuffer = nullptr ;
2020-03-09 15:56:48 +00:00
size_t size = FormatMessageW ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2020-04-01 23:20:12 +00:00
nullptr , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , nullptr ) ;
2020-03-09 15:56:48 +00:00
2020-07-27 10:43:20 +00:00
String msg = " Error " + itos ( id ) + " : " + String : : utf16 ( ( const char16_t * ) messageBuffer , size ) ;
2020-03-09 15:56:48 +00:00
LocalFree ( messageBuffer ) ;
return msg ;
}
2021-12-28 10:27:46 +00:00
static void track_mouse_leave_event ( HWND hWnd ) {
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( TRACKMOUSEEVENT ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hWnd ;
tme . dwHoverTime = HOVER_DEFAULT ;
TrackMouseEvent ( & tme ) ;
}
2020-03-09 15:56:48 +00:00
bool DisplayServerWindows : : has_feature ( Feature p_feature ) const {
switch ( p_feature ) {
2024-01-19 17:41:01 +00:00
# ifndef DISABLE_DEPRECATED
case FEATURE_GLOBAL_MENU : {
return ( native_menu & & native_menu - > has_feature ( NativeMenu : : FEATURE_GLOBAL_MENU ) ) ;
} break ;
# endif
2020-03-09 15:56:48 +00:00
case FEATURE_SUBWINDOWS :
case FEATURE_TOUCHSCREEN :
case FEATURE_MOUSE :
case FEATURE_MOUSE_WARP :
case FEATURE_CLIPBOARD :
case FEATURE_CURSOR_SHAPE :
case FEATURE_CUSTOM_CURSOR_SHAPE :
case FEATURE_IME :
case FEATURE_WINDOW_TRANSPARENCY :
case FEATURE_HIDPI :
case FEATURE_ICON :
case FEATURE_NATIVE_ICON :
2023-07-17 09:12:24 +00:00
case FEATURE_NATIVE_DIALOG :
2024-03-26 13:18:06 +00:00
case FEATURE_NATIVE_DIALOG_INPUT :
case FEATURE_NATIVE_DIALOG_FILE :
2020-03-09 15:56:48 +00:00
case FEATURE_SWAP_BUFFERS :
case FEATURE_KEEP_SCREEN_ON :
2021-11-04 12:33:37 +00:00
case FEATURE_TEXT_TO_SPEECH :
2023-02-27 18:36:13 +00:00
case FEATURE_SCREEN_CAPTURE :
2023-08-03 09:45:56 +00:00
case FEATURE_STATUS_INDICATOR :
2020-03-09 15:56:48 +00:00
return true ;
default :
return false ;
}
}
String DisplayServerWindows : : get_name ( ) const {
return " Windows " ;
}
void DisplayServerWindows : : _set_mouse_mode_impl ( MouseMode p_mode ) {
2021-09-10 13:44:44 +00:00
if ( windows . has ( MAIN_WINDOW_ID ) & & ( p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_CONFINED | | p_mode = = MOUSE_MODE_CONFINED_HIDDEN ) ) {
2021-03-30 22:35:08 +00:00
// Mouse is grabbed (captured or confined).
2022-02-01 02:49:51 +00:00
2022-09-07 17:31:57 +00:00
WindowID window_id = _get_focused_window_or_popup ( ) ;
if ( ! windows . has ( window_id ) ) {
window_id = MAIN_WINDOW_ID ;
}
2022-02-01 02:49:51 +00:00
WindowData & wd = windows [ window_id ] ;
2020-03-09 15:56:48 +00:00
RECT clipRect ;
GetClientRect ( wd . hWnd , & clipRect ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & clipRect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & clipRect . right ) ;
ClipCursor ( & clipRect ) ;
if ( p_mode = = MOUSE_MODE_CAPTURED ) {
center = window_get_size ( ) / 2 ;
POINT pos = { ( int ) center . x , ( int ) center . y } ;
ClientToScreen ( wd . hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
SetCapture ( wd . hWnd ) ;
2022-09-07 17:31:57 +00:00
_register_raw_input_devices ( window_id ) ;
2020-03-09 15:56:48 +00:00
}
} else {
2021-03-30 22:35:08 +00:00
// Mouse is free to move around (not captured or confined).
2020-03-09 15:56:48 +00:00
ReleaseCapture ( ) ;
2020-04-01 23:20:12 +00:00
ClipCursor ( nullptr ) ;
2022-09-07 17:31:57 +00:00
_register_raw_input_devices ( INVALID_WINDOW_ID ) ;
2020-03-09 15:56:48 +00:00
}
2021-03-30 22:35:08 +00:00
if ( p_mode = = MOUSE_MODE_HIDDEN | | p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2020-06-22 10:05:18 +00:00
if ( hCursor = = nullptr ) {
hCursor = SetCursor ( nullptr ) ;
} else {
SetCursor ( nullptr ) ;
}
2020-03-09 15:56:48 +00:00
} else {
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
}
}
2020-05-14 12:29:06 +00:00
2022-09-07 17:31:57 +00:00
DisplayServer : : WindowID DisplayServerWindows : : _get_focused_window_or_popup ( ) const {
const List < WindowID > : : Element * E = popup_list . back ( ) ;
if ( E ) {
return E - > get ( ) ;
}
return last_focused_window ;
}
void DisplayServerWindows : : _register_raw_input_devices ( WindowID p_target_window ) {
use_raw_input = true ;
2023-08-03 13:18:26 +00:00
RAWINPUTDEVICE rid [ 2 ] = { } ;
rid [ 0 ] . usUsagePage = 0x01 ; // HID_USAGE_PAGE_GENERIC
rid [ 0 ] . usUsage = 0x02 ; // HID_USAGE_GENERIC_MOUSE
2022-09-07 17:31:57 +00:00
rid [ 0 ] . dwFlags = 0 ;
2023-08-03 13:18:26 +00:00
rid [ 1 ] . usUsagePage = 0x01 ; // HID_USAGE_PAGE_GENERIC
rid [ 1 ] . usUsage = 0x06 ; // HID_USAGE_GENERIC_KEYBOARD
rid [ 1 ] . dwFlags = 0 ;
2022-09-07 17:31:57 +00:00
if ( p_target_window ! = INVALID_WINDOW_ID & & windows . has ( p_target_window ) ) {
// Follow the defined window
rid [ 0 ] . hwndTarget = windows [ p_target_window ] . hWnd ;
2023-08-03 13:18:26 +00:00
rid [ 1 ] . hwndTarget = windows [ p_target_window ] . hWnd ;
2022-09-07 17:31:57 +00:00
} else {
// Follow the keyboard focus
2024-03-12 14:40:40 +00:00
rid [ 0 ] . hwndTarget = nullptr ;
rid [ 1 ] . hwndTarget = nullptr ;
2022-09-07 17:31:57 +00:00
}
2023-08-03 13:18:26 +00:00
if ( RegisterRawInputDevices ( rid , 2 , sizeof ( rid [ 0 ] ) ) = = FALSE ) {
2022-09-07 17:31:57 +00:00
// Registration failed.
use_raw_input = false ;
}
}
2021-11-04 12:33:37 +00:00
bool DisplayServerWindows : : tts_is_speaking ( ) const {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_V_MSG ( tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
return tts - > is_speaking ( ) ;
}
bool DisplayServerWindows : : tts_is_paused ( ) const {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_V_MSG ( tts , false , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
return tts - > is_paused ( ) ;
}
2022-08-05 01:41:48 +00:00
TypedArray < Dictionary > DisplayServerWindows : : tts_get_voices ( ) const {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_V_MSG ( tts , TypedArray < Dictionary > ( ) , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
return tts - > get_voices ( ) ;
}
void DisplayServerWindows : : tts_speak ( const String & p_text , const String & p_voice , int p_volume , float p_pitch , float p_rate , int p_utterance_id , bool p_interrupt ) {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
tts - > speak ( p_text , p_voice , p_volume , p_pitch , p_rate , p_utterance_id , p_interrupt ) ;
}
void DisplayServerWindows : : tts_pause ( ) {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
tts - > pause ( ) ;
}
void DisplayServerWindows : : tts_resume ( ) {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
tts - > resume ( ) ;
}
void DisplayServerWindows : : tts_stop ( ) {
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( tts , " Enable the \" audio/general/text_to_speech \" project setting to use text-to-speech. " ) ;
2021-11-04 12:33:37 +00:00
tts - > stop ( ) ;
}
2023-10-13 09:37:46 +00:00
// Silence warning due to a COM API weirdness.
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
# endif
class FileDialogEventHandler : public IFileDialogEvents , public IFileDialogControlEvents {
LONG ref_count = 1 ;
int ctl_id = 1 ;
HashMap < int , String > ctls ;
Dictionary selected ;
String root ;
public :
// IUnknown methods
HRESULT STDMETHODCALLTYPE QueryInterface ( REFIID riid , void * * ppv ) {
static const QITAB qit [ ] = {
2024-01-31 04:18:15 +00:00
# ifdef __MINGW32__
{ & __uuidof ( IFileDialogEvents ) , static_cast < decltype ( qit [ 0 ] . dwOffset ) > ( OFFSETOFCLASS ( IFileDialogEvents , FileDialogEventHandler ) ) } ,
{ & __uuidof ( IFileDialogControlEvents ) , static_cast < decltype ( qit [ 0 ] . dwOffset ) > ( OFFSETOFCLASS ( IFileDialogControlEvents , FileDialogEventHandler ) ) } ,
# else
2023-10-13 09:37:46 +00:00
QITABENT ( FileDialogEventHandler , IFileDialogEvents ) ,
QITABENT ( FileDialogEventHandler , IFileDialogControlEvents ) ,
2024-01-31 04:18:15 +00:00
# endif
2024-03-12 14:40:40 +00:00
{ nullptr , 0 } ,
2023-10-13 09:37:46 +00:00
} ;
return QISearch ( this , qit , riid , ppv ) ;
}
ULONG STDMETHODCALLTYPE AddRef ( ) {
return InterlockedIncrement ( & ref_count ) ;
}
ULONG STDMETHODCALLTYPE Release ( ) {
long ref = InterlockedDecrement ( & ref_count ) ;
if ( ! ref ) {
delete this ;
}
return ref ;
}
// IFileDialogEvents methods
HRESULT STDMETHODCALLTYPE OnFileOk ( IFileDialog * ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnFolderChange ( IFileDialog * ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnFolderChanging ( IFileDialog * p_pfd , IShellItem * p_item ) {
if ( root . is_empty ( ) ) {
return S_OK ;
}
LPWSTR lpw_path = nullptr ;
p_item - > GetDisplayName ( SIGDN_FILESYSPATH , & lpw_path ) ;
if ( ! lpw_path ) {
return S_FALSE ;
}
String path = String : : utf16 ( ( const char16_t * ) lpw_path ) . simplify_path ( ) ;
if ( ! path . begins_with ( root . simplify_path ( ) ) ) {
return S_FALSE ;
}
return S_OK ;
}
HRESULT STDMETHODCALLTYPE OnHelp ( IFileDialog * ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnSelectionChange ( IFileDialog * ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnShareViolation ( IFileDialog * , IShellItem * , FDE_SHAREVIOLATION_RESPONSE * ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnTypeChange ( IFileDialog * pfd ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnOverwrite ( IFileDialog * , IShellItem * , FDE_OVERWRITE_RESPONSE * ) { return S_OK ; } ;
// IFileDialogControlEvents methods
HRESULT STDMETHODCALLTYPE OnItemSelected ( IFileDialogCustomize * p_pfdc , DWORD p_ctl_id , DWORD p_item_idx ) {
if ( ctls . has ( p_ctl_id ) ) {
selected [ ctls [ p_ctl_id ] ] = ( int ) p_item_idx ;
}
return S_OK ;
}
HRESULT STDMETHODCALLTYPE OnButtonClicked ( IFileDialogCustomize * , DWORD ) { return S_OK ; } ;
HRESULT STDMETHODCALLTYPE OnCheckButtonToggled ( IFileDialogCustomize * p_pfdc , DWORD p_ctl_id , BOOL p_checked ) {
if ( ctls . has ( p_ctl_id ) ) {
selected [ ctls [ p_ctl_id ] ] = ( bool ) p_checked ;
}
return S_OK ;
}
HRESULT STDMETHODCALLTYPE OnControlActivating ( IFileDialogCustomize * , DWORD ) { return S_OK ; } ;
Dictionary get_selected ( ) {
return selected ;
}
void set_root ( const String & p_root ) {
root = p_root ;
}
void add_option ( IFileDialogCustomize * p_pfdc , const String & p_name , const Vector < String > & p_options , int p_default ) {
int gid = ctl_id + + ;
int cid = ctl_id + + ;
if ( p_options . size ( ) = = 0 ) {
// Add check box.
p_pfdc - > StartVisualGroup ( gid , L " " ) ;
p_pfdc - > AddCheckButton ( cid , ( LPCWSTR ) p_name . utf16 ( ) . get_data ( ) , p_default ) ;
p_pfdc - > SetControlState ( cid , CDCS_VISIBLE | CDCS_ENABLED ) ;
p_pfdc - > EndVisualGroup ( ) ;
selected [ p_name ] = ( bool ) p_default ;
} else {
// Add combo box.
p_pfdc - > StartVisualGroup ( gid , ( LPCWSTR ) p_name . utf16 ( ) . get_data ( ) ) ;
p_pfdc - > AddComboBox ( cid ) ;
p_pfdc - > SetControlState ( cid , CDCS_VISIBLE | CDCS_ENABLED ) ;
for ( int i = 0 ; i < p_options . size ( ) ; i + + ) {
p_pfdc - > AddControlItem ( cid , i , ( LPCWSTR ) p_options [ i ] . utf16 ( ) . get_data ( ) ) ;
}
p_pfdc - > SetSelectedControlItem ( cid , p_default ) ;
p_pfdc - > EndVisualGroup ( ) ;
selected [ p_name ] = p_default ;
}
ctls [ cid ] = p_name ;
}
virtual ~ FileDialogEventHandler ( ) { } ;
} ;
# if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
# endif
2023-07-17 09:12:24 +00:00
Error DisplayServerWindows : : file_dialog_show ( const String & p_title , const String & p_current_directory , const String & p_filename , bool p_show_hidden , FileDialogMode p_mode , const Vector < String > & p_filters , const Callable & p_callback ) {
2023-10-13 09:37:46 +00:00
return _file_dialog_with_options_show ( p_title , p_current_directory , String ( ) , p_filename , p_show_hidden , p_mode , p_filters , TypedArray < Dictionary > ( ) , p_callback , false ) ;
}
Error DisplayServerWindows : : file_dialog_with_options_show ( const String & p_title , const String & p_current_directory , const String & p_root , const String & p_filename , bool p_show_hidden , FileDialogMode p_mode , const Vector < String > & p_filters , const TypedArray < Dictionary > & p_options , const Callable & p_callback ) {
return _file_dialog_with_options_show ( p_title , p_current_directory , p_root , p_filename , p_show_hidden , p_mode , p_filters , p_options , p_callback , true ) ;
}
Error DisplayServerWindows : : _file_dialog_with_options_show ( const String & p_title , const String & p_current_directory , const String & p_root , const String & p_filename , bool p_show_hidden , FileDialogMode p_mode , const Vector < String > & p_filters , const TypedArray < Dictionary > & p_options , const Callable & p_callback , bool p_options_in_cb ) {
2023-07-17 09:12:24 +00:00
_THREAD_SAFE_METHOD_
2023-08-21 07:54:24 +00:00
ERR_FAIL_INDEX_V ( int ( p_mode ) , FILE_DIALOG_MODE_SAVE_MAX , FAILED ) ;
2023-07-17 09:12:24 +00:00
Vector < Char16String > filter_names ;
Vector < Char16String > filter_exts ;
for ( const String & E : p_filters ) {
Vector < String > tokens = E . split ( " ; " ) ;
2023-08-21 07:54:24 +00:00
if ( tokens . size ( ) > = 1 ) {
String flt = tokens [ 0 ] . strip_edges ( ) ;
int filter_slice_count = flt . get_slice_count ( " , " ) ;
Vector < String > exts ;
for ( int j = 0 ; j < filter_slice_count ; j + + ) {
String str = ( flt . get_slice ( " , " , j ) . strip_edges ( ) ) ;
if ( ! str . is_empty ( ) ) {
exts . push_back ( str ) ;
}
}
if ( ! exts . is_empty ( ) ) {
String str = String ( " ; " ) . join ( exts ) ;
filter_exts . push_back ( str . utf16 ( ) ) ;
if ( tokens . size ( ) = = 2 ) {
filter_names . push_back ( tokens [ 1 ] . strip_edges ( ) . utf16 ( ) ) ;
} else {
filter_names . push_back ( str . utf16 ( ) ) ;
}
}
2023-07-17 09:12:24 +00:00
}
}
2023-08-21 07:54:24 +00:00
if ( filter_names . is_empty ( ) ) {
filter_exts . push_back ( String ( " *.* " ) . utf16 ( ) ) ;
filter_names . push_back ( RTR ( " All Files " ) . utf16 ( ) ) ;
}
2023-07-17 09:12:24 +00:00
Vector < COMDLG_FILTERSPEC > filters ;
for ( int i = 0 ; i < filter_names . size ( ) ; i + + ) {
filters . push_back ( { ( LPCWSTR ) filter_names [ i ] . ptr ( ) , ( LPCWSTR ) filter_exts [ i ] . ptr ( ) } ) ;
}
2023-08-24 05:49:18 +00:00
WindowID prev_focus = last_focused_window ;
2023-07-17 09:12:24 +00:00
HRESULT hr = S_OK ;
IFileDialog * pfd = nullptr ;
if ( p_mode = = FILE_DIALOG_MODE_SAVE_FILE ) {
hr = CoCreateInstance ( CLSID_FileSaveDialog , nullptr , CLSCTX_INPROC_SERVER , IID_IFileSaveDialog , ( void * * ) & pfd ) ;
} else {
hr = CoCreateInstance ( CLSID_FileOpenDialog , nullptr , CLSCTX_INPROC_SERVER , IID_IFileOpenDialog , ( void * * ) & pfd ) ;
}
if ( SUCCEEDED ( hr ) ) {
2023-10-13 09:37:46 +00:00
IFileDialogEvents * pfde = nullptr ;
FileDialogEventHandler * event_handler = new FileDialogEventHandler ( ) ;
hr = event_handler - > QueryInterface ( IID_PPV_ARGS ( & pfde ) ) ;
DWORD cookie = 0 ;
hr = pfd - > Advise ( pfde , & cookie ) ;
IFileDialogCustomize * pfdc = nullptr ;
hr = pfd - > QueryInterface ( IID_PPV_ARGS ( & pfdc ) ) ;
for ( int i = 0 ; i < p_options . size ( ) ; i + + ) {
const Dictionary & item = p_options [ i ] ;
if ( ! item . has ( " name " ) | | ! item . has ( " values " ) | | ! item . has ( " default " ) ) {
continue ;
}
const String & name = item [ " name " ] ;
const Vector < String > & options = item [ " values " ] ;
int default_idx = item [ " default " ] ;
event_handler - > add_option ( pfdc , name , options , default_idx ) ;
}
event_handler - > set_root ( p_root ) ;
pfdc - > Release ( ) ;
2023-07-17 09:12:24 +00:00
DWORD flags ;
pfd - > GetOptions ( & flags ) ;
if ( p_mode = = FILE_DIALOG_MODE_OPEN_FILES ) {
flags | = FOS_ALLOWMULTISELECT ;
}
if ( p_mode = = FILE_DIALOG_MODE_OPEN_DIR ) {
flags | = FOS_PICKFOLDERS ;
}
if ( p_show_hidden ) {
flags | = FOS_FORCESHOWHIDDEN ;
}
pfd - > SetOptions ( flags | FOS_FORCEFILESYSTEM ) ;
pfd - > SetTitle ( ( LPCWSTR ) p_title . utf16 ( ) . ptr ( ) ) ;
String dir = ProjectSettings : : get_singleton ( ) - > globalize_path ( p_current_directory ) ;
if ( dir = = " . " ) {
dir = OS : : get_singleton ( ) - > get_executable_path ( ) . get_base_dir ( ) ;
}
dir = dir . replace ( " / " , " \\ " ) ;
IShellItem * shellitem = nullptr ;
hr = SHCreateItemFromParsingName ( ( LPCWSTR ) dir . utf16 ( ) . ptr ( ) , nullptr , IID_IShellItem , ( void * * ) & shellitem ) ;
if ( SUCCEEDED ( hr ) ) {
pfd - > SetDefaultFolder ( shellitem ) ;
pfd - > SetFolder ( shellitem ) ;
}
pfd - > SetFileName ( ( LPCWSTR ) p_filename . utf16 ( ) . ptr ( ) ) ;
pfd - > SetFileTypes ( filters . size ( ) , filters . ptr ( ) ) ;
pfd - > SetFileTypeIndex ( 0 ) ;
2023-07-20 09:14:16 +00:00
WindowID window_id = _get_focused_window_or_popup ( ) ;
if ( ! windows . has ( window_id ) ) {
window_id = MAIN_WINDOW_ID ;
}
hr = pfd - > Show ( windows [ window_id ] . hWnd ) ;
2023-10-13 09:37:46 +00:00
pfd - > Unadvise ( cookie ) ;
Dictionary options = event_handler - > get_selected ( ) ;
pfde - > Release ( ) ;
event_handler - > Release ( ) ;
2023-08-21 07:54:24 +00:00
UINT index = 0 ;
pfd - > GetFileTypeIndex ( & index ) ;
2023-10-13 09:37:46 +00:00
if ( index > 0 ) {
index = index - 1 ;
}
2023-08-21 07:54:24 +00:00
2023-07-17 09:12:24 +00:00
if ( SUCCEEDED ( hr ) ) {
Vector < String > file_names ;
if ( p_mode = = FILE_DIALOG_MODE_OPEN_FILES ) {
IShellItemArray * results ;
hr = static_cast < IFileOpenDialog * > ( pfd ) - > GetResults ( & results ) ;
if ( SUCCEEDED ( hr ) ) {
DWORD count = 0 ;
results - > GetCount ( & count ) ;
for ( DWORD i = 0 ; i < count ; i + + ) {
IShellItem * result ;
results - > GetItemAt ( i , & result ) ;
PWSTR file_path = nullptr ;
hr = result - > GetDisplayName ( SIGDN_FILESYSPATH , & file_path ) ;
if ( SUCCEEDED ( hr ) ) {
file_names . push_back ( String : : utf16 ( ( const char16_t * ) file_path ) ) ;
CoTaskMemFree ( file_path ) ;
}
result - > Release ( ) ;
}
results - > Release ( ) ;
}
} else {
IShellItem * result ;
hr = pfd - > GetResult ( & result ) ;
if ( SUCCEEDED ( hr ) ) {
PWSTR file_path = nullptr ;
hr = result - > GetDisplayName ( SIGDN_FILESYSPATH , & file_path ) ;
if ( SUCCEEDED ( hr ) ) {
file_names . push_back ( String : : utf16 ( ( const char16_t * ) file_path ) ) ;
CoTaskMemFree ( file_path ) ;
}
result - > Release ( ) ;
}
}
2024-04-27 09:56:39 +00:00
if ( p_callback . is_valid ( ) ) {
2023-10-13 09:37:46 +00:00
if ( p_options_in_cb ) {
Variant v_result = true ;
Variant v_files = file_names ;
Variant v_index = index ;
Variant v_opt = options ;
Variant ret ;
Callable : : CallError ce ;
const Variant * args [ 4 ] = { & v_result , & v_files , & v_index , & v_opt } ;
p_callback . callp ( args , 4 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute file dialogs callback: %s. " , Variant : : get_callable_error_text ( p_callback , args , 4 , ce ) ) ) ;
}
} else {
Variant v_result = true ;
Variant v_files = file_names ;
Variant v_index = index ;
Variant ret ;
Callable : : CallError ce ;
const Variant * args [ 3 ] = { & v_result , & v_files , & v_index } ;
p_callback . callp ( args , 3 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute file dialogs callback: %s. " , Variant : : get_callable_error_text ( p_callback , args , 3 , ce ) ) ) ;
}
2023-10-12 05:27:40 +00:00
}
2023-07-17 09:12:24 +00:00
}
} else {
2024-04-27 09:56:39 +00:00
if ( p_callback . is_valid ( ) ) {
2023-10-13 09:37:46 +00:00
if ( p_options_in_cb ) {
Variant v_result = false ;
Variant v_files = Vector < String > ( ) ;
Variant v_index = index ;
Variant v_opt = options ;
Variant ret ;
Callable : : CallError ce ;
const Variant * args [ 4 ] = { & v_result , & v_files , & v_index , & v_opt } ;
p_callback . callp ( args , 4 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute file dialogs callback: %s. " , Variant : : get_callable_error_text ( p_callback , args , 4 , ce ) ) ) ;
}
} else {
Variant v_result = false ;
Variant v_files = Vector < String > ( ) ;
Variant v_index = index ;
Variant ret ;
Callable : : CallError ce ;
const Variant * args [ 3 ] = { & v_result , & v_files , & v_index } ;
p_callback . callp ( args , 3 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute file dialogs callback: %s. " , Variant : : get_callable_error_text ( p_callback , args , 3 , ce ) ) ) ;
}
2023-10-12 05:27:40 +00:00
}
2023-07-17 09:12:24 +00:00
}
}
pfd - > Release ( ) ;
2023-08-24 05:49:18 +00:00
if ( prev_focus ! = INVALID_WINDOW_ID ) {
callable_mp ( DisplayServer : : get_singleton ( ) , & DisplayServer : : window_move_to_foreground ) . call_deferred ( prev_focus ) ;
}
2023-07-17 09:12:24 +00:00
return OK ;
} else {
return ERR_CANT_OPEN ;
}
}
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : mouse_set_mode ( MouseMode p_mode ) {
_THREAD_SAFE_METHOD_
2021-09-10 13:44:44 +00:00
if ( mouse_mode = = p_mode ) {
// Already in the same mode; do nothing.
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
mouse_mode = p_mode ;
2020-06-22 10:05:18 +00:00
_set_mouse_mode_impl ( p_mode ) ;
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
DisplayServer : : MouseMode DisplayServerWindows : : mouse_get_mode ( ) const {
return mouse_mode ;
}
2022-03-27 09:17:36 +00:00
void DisplayServerWindows : : warp_mouse ( const Point2i & p_position ) {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
2022-09-07 17:31:57 +00:00
WindowID window_id = _get_focused_window_or_popup ( ) ;
if ( ! windows . has ( window_id ) ) {
2021-09-10 13:44:44 +00:00
return ; // No focused window?
2020-03-09 15:56:48 +00:00
}
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
2022-03-27 09:17:36 +00:00
old_x = p_position . x ;
old_y = p_position . y ;
2020-03-09 15:56:48 +00:00
} else {
POINT p ;
2022-03-27 09:17:36 +00:00
p . x = p_position . x ;
p . y = p_position . y ;
2022-09-07 17:31:57 +00:00
ClientToScreen ( windows [ window_id ] . hWnd , & p ) ;
2020-03-09 15:56:48 +00:00
SetCursorPos ( p . x , p . y ) ;
}
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
Point2i DisplayServerWindows : : mouse_get_position ( ) const {
2020-03-20 02:32:09 +00:00
POINT p ;
GetCursorPos ( & p ) ;
2023-01-04 22:00:02 +00:00
return Point2i ( p . x , p . y ) - _get_screens_origin ( ) ;
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2023-01-07 23:55:54 +00:00
BitField < MouseButtonMask > DisplayServerWindows : : mouse_get_button_state ( ) const {
2020-03-09 15:56:48 +00:00
return last_button_state ;
}
void DisplayServerWindows : : clipboard_set ( const String & p_text ) {
_THREAD_SAFE_METHOD_
2023-02-24 15:31:57 +00:00
if ( ! windows . has ( MAIN_WINDOW_ID ) ) {
return ;
2020-03-09 15:56:48 +00:00
}
2021-09-10 13:44:44 +00:00
// 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.
2020-03-09 15:56:48 +00:00
2023-02-24 15:31:57 +00:00
if ( ! OpenClipboard ( windows [ MAIN_WINDOW_ID ] . hWnd ) ) {
2020-03-09 15:56:48 +00:00
ERR_FAIL_MSG ( " Unable to open clipboard. " ) ;
}
EmptyClipboard ( ) ;
2020-07-27 10:43:20 +00:00
Char16String utf16 = text . utf16 ( ) ;
HGLOBAL mem = GlobalAlloc ( GMEM_MOVEABLE , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( mem , " Unable to allocate memory for clipboard contents. " ) ;
2020-03-09 15:56:48 +00:00
LPWSTR lptstrCopy = ( LPWSTR ) GlobalLock ( mem ) ;
2020-07-27 10:43:20 +00:00
memcpy ( lptstrCopy , utf16 . get_data ( ) , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2020-03-09 15:56:48 +00:00
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_UNICODETEXT , mem ) ;
2021-09-10 13:44:44 +00:00
// Set the CF_TEXT version (not needed?).
2020-03-09 15:56:48 +00:00
CharString utf8 = text . utf8 ( ) ;
mem = GlobalAlloc ( GMEM_MOVEABLE , utf8 . length ( ) + 1 ) ;
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL_MSG ( mem , " Unable to allocate memory for clipboard contents. " ) ;
2020-03-09 15:56:48 +00:00
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
memcpy ( ptr , utf8 . get_data ( ) , utf8 . length ( ) ) ;
ptr [ utf8 . length ( ) ] = 0 ;
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_TEXT , mem ) ;
CloseClipboard ( ) ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
String DisplayServerWindows : : clipboard_get ( ) const {
_THREAD_SAFE_METHOD_
2023-02-24 15:31:57 +00:00
if ( ! windows . has ( MAIN_WINDOW_ID ) ) {
return String ( ) ;
2020-03-09 15:56:48 +00:00
}
String ret ;
2023-02-24 15:31:57 +00:00
if ( ! OpenClipboard ( windows [ MAIN_WINDOW_ID ] . hWnd ) ) {
2020-03-09 15:56:48 +00:00
ERR_FAIL_V_MSG ( " " , " Unable to open clipboard. " ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
if ( IsClipboardFormatAvailable ( CF_UNICODETEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
2020-04-01 23:20:12 +00:00
if ( mem ! = nullptr ) {
2020-03-09 15:56:48 +00:00
LPWSTR ptr = ( LPWSTR ) GlobalLock ( mem ) ;
2020-04-01 23:20:12 +00:00
if ( ptr ! = nullptr ) {
2020-07-27 10:43:20 +00:00
ret = String : : utf16 ( ( const char16_t * ) ptr ) ;
2020-03-09 15:56:48 +00:00
GlobalUnlock ( mem ) ;
2022-02-16 12:56:32 +00:00
}
}
2020-03-09 15:56:48 +00:00
} else if ( IsClipboardFormatAvailable ( CF_TEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
2020-04-01 23:20:12 +00:00
if ( mem ! = nullptr ) {
2020-03-09 15:56:48 +00:00
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
2020-04-01 23:20:12 +00:00
if ( ptr ! = nullptr ) {
2020-03-09 15:56:48 +00:00
ret . parse_utf8 ( ( const char * ) ptr ) ;
GlobalUnlock ( mem ) ;
2022-02-16 12:56:32 +00:00
}
}
}
2020-03-09 15:56:48 +00:00
CloseClipboard ( ) ;
return ret ;
}
2022-08-02 12:05:37 +00:00
Ref < Image > DisplayServerWindows : : clipboard_get_image ( ) const {
Ref < Image > image ;
if ( ! windows . has ( last_focused_window ) ) {
return image ; // No focused window?
}
if ( ! OpenClipboard ( windows [ last_focused_window ] . hWnd ) ) {
ERR_FAIL_V_MSG ( image , " Unable to open clipboard. " ) ;
}
UINT png_format = RegisterClipboardFormatA ( " PNG " ) ;
if ( png_format & & IsClipboardFormatAvailable ( png_format ) ) {
HANDLE png_handle = GetClipboardData ( png_format ) ;
if ( png_handle ) {
size_t png_size = GlobalSize ( png_handle ) ;
uint8_t * png_data = ( uint8_t * ) GlobalLock ( png_handle ) ;
image . instantiate ( ) ;
PNGDriverCommon : : png_to_image ( png_data , png_size , false , image ) ;
GlobalUnlock ( png_handle ) ;
}
} else if ( IsClipboardFormatAvailable ( CF_DIB ) ) {
HGLOBAL mem = GetClipboardData ( CF_DIB ) ;
2024-03-12 14:40:40 +00:00
if ( mem ! = nullptr ) {
2022-08-02 12:05:37 +00:00
BITMAPINFO * ptr = static_cast < BITMAPINFO * > ( GlobalLock ( mem ) ) ;
2024-03-12 14:40:40 +00:00
if ( ptr ! = nullptr ) {
2022-08-02 12:05:37 +00:00
BITMAPINFOHEADER * info = & ptr - > bmiHeader ;
2024-02-16 15:10:16 +00:00
void * dib_bits = ( void * ) ( ptr - > bmiColors ) ;
// Draw DIB image to temporary DC surface and read it back as BGRA8.
2024-03-12 14:40:40 +00:00
HDC dc = GetDC ( nullptr ) ;
2024-02-16 15:10:16 +00:00
if ( dc ) {
HDC hdc = CreateCompatibleDC ( dc ) ;
if ( hdc ) {
HBITMAP hbm = CreateCompatibleBitmap ( dc , info - > biWidth , abs ( info - > biHeight ) ) ;
if ( hbm ) {
SelectObject ( hdc , hbm ) ;
SetDIBitsToDevice ( hdc , 0 , 0 , info - > biWidth , abs ( info - > biHeight ) , 0 , 0 , 0 , abs ( info - > biHeight ) , dib_bits , ptr , DIB_RGB_COLORS ) ;
BITMAPINFO bmp_info = { } ;
bmp_info . bmiHeader . biSize = sizeof ( bmp_info . bmiHeader ) ;
bmp_info . bmiHeader . biWidth = info - > biWidth ;
bmp_info . bmiHeader . biHeight = - abs ( info - > biHeight ) ;
bmp_info . bmiHeader . biPlanes = 1 ;
bmp_info . bmiHeader . biBitCount = 32 ;
bmp_info . bmiHeader . biCompression = BI_RGB ;
Vector < uint8_t > img_data ;
img_data . resize ( info - > biWidth * abs ( info - > biHeight ) * 4 ) ;
GetDIBits ( hdc , hbm , 0 , abs ( info - > biHeight ) , img_data . ptrw ( ) , & bmp_info , DIB_RGB_COLORS ) ;
uint8_t * wr = ( uint8_t * ) img_data . ptrw ( ) ;
for ( int i = 0 ; i < info - > biWidth * abs ( info - > biHeight ) ; i + + ) {
SWAP ( wr [ i * 4 + 0 ] , wr [ i * 4 + 2 ] ) ; // Swap B and R.
if ( info - > biBitCount ! = 32 ) {
wr [ i * 4 + 3 ] = 255 ; // Set A to solid if it's not in the source image.
}
}
image = Image : : create_from_data ( info - > biWidth , abs ( info - > biHeight ) , false , Image : : Format : : FORMAT_RGBA8 , img_data ) ;
DeleteObject ( hbm ) ;
}
DeleteDC ( hdc ) ;
2022-08-02 12:05:37 +00:00
}
2024-03-12 14:40:40 +00:00
ReleaseDC ( nullptr , dc ) ;
2022-08-02 12:05:37 +00:00
}
GlobalUnlock ( mem ) ;
}
}
}
CloseClipboard ( ) ;
return image ;
}
bool DisplayServerWindows : : clipboard_has ( ) const {
return ( IsClipboardFormatAvailable ( CF_TEXT ) | |
IsClipboardFormatAvailable ( CF_UNICODETEXT ) | |
IsClipboardFormatAvailable ( CF_OEMTEXT ) ) ;
}
bool DisplayServerWindows : : clipboard_has_image ( ) const {
UINT png_format = RegisterClipboardFormatA ( " PNG " ) ;
return ( ( png_format & & IsClipboardFormatAvailable ( png_format ) ) | | IsClipboardFormatAvailable ( CF_DIB ) ) ;
}
2020-03-09 15:56:48 +00:00
typedef struct {
int count ;
int screen ;
HMONITOR monitor ;
} EnumScreenData ;
2023-01-04 22:00:02 +00:00
static BOOL CALLBACK _MonitorEnumProcPrim ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumScreenData * data = ( EnumScreenData * ) dwData ;
2023-01-07 13:41:06 +00:00
if ( ( lprcMonitor - > left = = 0 ) & & ( lprcMonitor - > top = = 0 ) ) {
data - > screen = data - > count ;
return FALSE ;
2023-01-04 22:00:02 +00:00
}
data - > count + + ;
return TRUE ;
}
2020-03-09 15:56:48 +00: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 ;
}
static BOOL CALLBACK _MonitorEnumProcCount ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
int * data = ( int * ) dwData ;
( * data ) + + ;
return TRUE ;
}
int DisplayServerWindows : : get_screen_count ( ) const {
_THREAD_SAFE_METHOD_
int data = 0 ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcCount , ( LPARAM ) & data ) ;
2020-03-09 15:56:48 +00:00
return data ;
}
2023-01-04 22:00:02 +00:00
int DisplayServerWindows : : get_primary_screen ( ) const {
2024-03-12 14:40:40 +00:00
EnumScreenData data = { 0 , 0 , nullptr } ;
2023-01-04 22:00:02 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcPrim , ( LPARAM ) & data ) ;
return data . screen ;
}
2023-03-21 11:08:46 +00:00
int DisplayServerWindows : : get_keyboard_focus_screen ( ) const {
HWND hwnd = GetForegroundWindow ( ) ;
if ( hwnd ) {
EnumScreenData data = { 0 , 0 , MonitorFromWindow ( hwnd , MONITOR_DEFAULTTONEAREST ) } ;
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcScreen , ( LPARAM ) & data ) ;
return data . screen ;
} else {
return get_primary_screen ( ) ;
}
}
2020-03-09 15:56:48 +00:00
typedef struct {
int count ;
int screen ;
Point2 pos ;
} EnumPosData ;
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 ;
}
2020-05-14 12:29:06 +00:00
2023-01-04 22:00:02 +00:00
static BOOL CALLBACK _MonitorEnumProcOrigin ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumPosData * data = ( EnumPosData * ) dwData ;
2024-03-03 11:49:08 +00:00
data - > pos = data - > pos . min ( Point2 ( lprcMonitor - > left , lprcMonitor - > top ) ) ;
2023-01-04 22:00:02 +00:00
return TRUE ;
}
Point2i DisplayServerWindows : : _get_screens_origin ( ) const {
_THREAD_SAFE_METHOD_
EnumPosData data = { 0 , 0 , Point2 ( ) } ;
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcOrigin , ( LPARAM ) & data ) ;
return data . pos ;
}
2020-03-09 15:56:48 +00:00
Point2i DisplayServerWindows : : screen_get_position ( int p_screen ) const {
_THREAD_SAFE_METHOD_
2023-03-21 11:08:46 +00:00
p_screen = _get_screen_index ( p_screen ) ;
2023-01-04 22:00:02 +00:00
EnumPosData data = { 0 , p_screen , Point2 ( ) } ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcPos , ( LPARAM ) & data ) ;
2023-01-04 22:00:02 +00:00
return data . pos - _get_screens_origin ( ) ;
2020-03-09 15:56:48 +00:00
}
typedef struct {
int count ;
int screen ;
Size2 size ;
} EnumSizeData ;
2020-03-20 02:32:09 +00:00
typedef struct {
int count ;
int screen ;
Rect2i rect ;
} EnumRectData ;
2022-01-27 19:46:57 +00:00
typedef struct {
2023-10-31 12:22:34 +00:00
Vector < DISPLAYCONFIG_PATH_INFO > paths ;
Vector < DISPLAYCONFIG_MODE_INFO > modes ;
2022-01-27 19:46:57 +00:00
int count ;
int screen ;
float rate ;
} EnumRefreshRateData ;
2020-03-09 15:56:48 +00: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 ;
}
Size2i DisplayServerWindows : : screen_get_size ( int p_screen ) const {
_THREAD_SAFE_METHOD_
2023-03-21 11:08:46 +00:00
p_screen = _get_screen_index ( p_screen ) ;
2023-01-04 22:00:02 +00:00
EnumSizeData data = { 0 , p_screen , Size2 ( ) } ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
2020-03-09 15:56:48 +00:00
return data . size ;
}
2020-03-20 02:32:09 +00:00
static BOOL CALLBACK _MonitorEnumProcUsableSize ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumRectData * data = ( EnumRectData * ) dwData ;
if ( data - > count = = data - > screen ) {
MONITORINFO minfo ;
2021-04-27 14:19:21 +00:00
memset ( & minfo , 0 , sizeof ( MONITORINFO ) ) ;
2020-03-20 02:32:09 +00:00
minfo . cbSize = sizeof ( MONITORINFO ) ;
GetMonitorInfoA ( hMonitor , & minfo ) ;
data - > rect . position . x = minfo . rcWork . left ;
data - > rect . position . y = minfo . rcWork . top ;
data - > rect . size . x = minfo . rcWork . right - minfo . rcWork . left ;
data - > rect . size . y = minfo . rcWork . bottom - minfo . rcWork . top ;
}
data - > count + + ;
return TRUE ;
}
2022-01-27 19:46:57 +00:00
static BOOL CALLBACK _MonitorEnumProcRefreshRate ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumRefreshRateData * data = ( EnumRefreshRateData * ) dwData ;
if ( data - > count = = data - > screen ) {
MONITORINFOEXW minfo ;
memset ( & minfo , 0 , sizeof ( minfo ) ) ;
minfo . cbSize = sizeof ( minfo ) ;
GetMonitorInfoW ( hMonitor , & minfo ) ;
2023-10-31 12:22:34 +00:00
bool found = false ;
for ( const DISPLAYCONFIG_PATH_INFO & path : data - > paths ) {
DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name ;
memset ( & source_name , 0 , sizeof ( source_name ) ) ;
source_name . header . type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME ;
source_name . header . size = sizeof ( source_name ) ;
source_name . header . adapterId = path . sourceInfo . adapterId ;
source_name . header . id = path . sourceInfo . id ;
if ( DisplayConfigGetDeviceInfo ( & source_name . header ) = = ERROR_SUCCESS ) {
if ( wcscmp ( minfo . szDevice , source_name . viewGdiDeviceName ) = = 0 & & path . targetInfo . refreshRate . Numerator ! = 0 & & path . targetInfo . refreshRate . Denominator ! = 0 ) {
data - > rate = ( double ) path . targetInfo . refreshRate . Numerator / ( double ) path . targetInfo . refreshRate . Denominator ;
found = true ;
break ;
}
}
}
if ( ! found ) {
DEVMODEW dm ;
memset ( & dm , 0 , sizeof ( dm ) ) ;
dm . dmSize = sizeof ( dm ) ;
EnumDisplaySettingsW ( minfo . szDevice , ENUM_CURRENT_SETTINGS , & dm ) ;
2022-01-27 19:46:57 +00:00
2023-10-31 12:22:34 +00:00
data - > rate = dm . dmDisplayFrequency ;
}
2022-01-27 19:46:57 +00:00
}
data - > count + + ;
return TRUE ;
}
2020-03-20 02:32:09 +00:00
Rect2i DisplayServerWindows : : screen_get_usable_rect ( int p_screen ) const {
_THREAD_SAFE_METHOD_
2023-03-21 11:08:46 +00:00
p_screen = _get_screen_index ( p_screen ) ;
2023-01-04 22:00:02 +00:00
EnumRectData data = { 0 , p_screen , Rect2i ( ) } ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcUsableSize , ( LPARAM ) & data ) ;
2023-01-04 22:00:02 +00:00
data . rect . position - = _get_screens_origin ( ) ;
2020-03-20 02:32:09 +00:00
return data . rect ;
}
2020-03-09 15:56:48 +00:00
typedef struct {
int count ;
int screen ;
int dpi ;
} EnumDpiData ;
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 ;
2020-04-01 23:20:12 +00:00
static HMODULE Shcore = nullptr ;
2020-03-09 15:56:48 +00:00
typedef HRESULT ( WINAPI * GetDPIForMonitor_t ) ( HMONITOR hmonitor , _MonitorDpiType dpiType , UINT * dpiX , UINT * dpiY ) ;
2020-04-01 23:20:12 +00:00
static GetDPIForMonitor_t getDPIForMonitor = nullptr ;
2020-03-09 15:56:48 +00:00
2020-04-01 23:20:12 +00:00
if ( Shcore = = nullptr ) {
2020-03-09 15:56:48 +00:00
Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
2020-04-01 23:20:12 +00:00
getDPIForMonitor = Shcore ? ( GetDPIForMonitor_t ) GetProcAddress ( Shcore , " GetDpiForMonitor " ) : nullptr ;
2020-03-09 15:56:48 +00:00
2020-04-01 23:20:12 +00:00
if ( ( Shcore = = nullptr ) | | ( getDPIForMonitor = = nullptr ) ) {
2022-02-16 12:56:32 +00:00
if ( Shcore ) {
2020-03-09 15:56:48 +00:00
FreeLibrary ( Shcore ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
Shcore = ( HMODULE ) INVALID_HANDLE_VALUE ;
}
}
UINT x = 0 , y = 0 ;
if ( hmon & & ( Shcore ! = ( HMODULE ) INVALID_HANDLE_VALUE ) ) {
2022-04-05 10:40:26 +00:00
HRESULT hr = getDPIForMonitor ( hmon , dpiType /*MDT_Effective_DPI*/ , & x , & y ) ;
2020-03-09 15:56:48 +00:00
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 ) {
2020-04-01 23:20:12 +00:00
HDC hdc = GetDC ( nullptr ) ;
2020-03-09 15:56:48 +00:00
if ( hdc ) {
overallX = GetDeviceCaps ( hdc , LOGPIXELSX ) ;
overallY = GetDeviceCaps ( hdc , LOGPIXELSY ) ;
2020-04-01 23:20:12 +00:00
ReleaseDC ( nullptr , hdc ) ;
2020-03-09 15:56:48 +00:00
}
}
if ( overallX > 0 & & overallY > 0 ) {
dpiX = overallX ;
dpiY = overallY ;
}
}
return ( dpiX + dpiY ) / 2 ;
}
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 ;
}
int DisplayServerWindows : : screen_get_dpi ( int p_screen ) const {
_THREAD_SAFE_METHOD_
2023-03-21 11:08:46 +00:00
p_screen = _get_screen_index ( p_screen ) ;
2023-01-04 22:00:02 +00:00
EnumDpiData data = { 0 , p_screen , 72 } ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcDpi , ( LPARAM ) & data ) ;
2020-03-09 15:56:48 +00:00
return data . dpi ;
}
2023-02-27 18:36:13 +00:00
Color DisplayServerWindows : : screen_get_pixel ( const Point2i & p_position ) const {
Point2i pos = p_position + _get_screens_origin ( ) ;
POINT p ;
p . x = pos . x ;
p . y = pos . y ;
if ( win81p_LogicalToPhysicalPointForPerMonitorDPI ) {
2024-03-12 14:40:40 +00:00
win81p_LogicalToPhysicalPointForPerMonitorDPI ( nullptr , & p ) ;
2023-02-27 18:36:13 +00:00
}
2024-03-12 14:40:40 +00:00
HDC dc = GetDC ( nullptr ) ;
2023-03-16 21:26:09 +00:00
if ( dc ) {
COLORREF col = GetPixel ( dc , p . x , p . y ) ;
if ( col ! = CLR_INVALID ) {
2024-03-12 14:40:40 +00:00
ReleaseDC ( nullptr , dc ) ;
2023-07-11 19:30:50 +00:00
return Color ( float ( col & 0x000000FF ) / 255.0f , float ( ( col & 0x0000FF00 ) > > 8 ) / 255.0f , float ( ( col & 0x00FF0000 ) > > 16 ) / 255.0f , 1.0f ) ;
2023-03-16 21:26:09 +00:00
}
2024-03-12 14:40:40 +00:00
ReleaseDC ( nullptr , dc ) ;
2023-02-27 18:36:13 +00:00
}
return Color ( ) ;
}
2023-03-16 21:26:09 +00:00
Ref < Image > DisplayServerWindows : : screen_get_image ( int p_screen ) const {
ERR_FAIL_INDEX_V ( p_screen , get_screen_count ( ) , Ref < Image > ( ) ) ;
switch ( p_screen ) {
case SCREEN_PRIMARY : {
p_screen = get_primary_screen ( ) ;
} break ;
case SCREEN_OF_MAIN_WINDOW : {
p_screen = window_get_current_screen ( MAIN_WINDOW_ID ) ;
} break ;
default :
break ;
}
Point2i pos = screen_get_position ( p_screen ) + _get_screens_origin ( ) ;
Size2i size = screen_get_size ( p_screen ) ;
POINT p1 ;
p1 . x = pos . x ;
p1 . y = pos . y ;
POINT p2 ;
p2 . x = pos . x + size . x ;
p2 . y = pos . y + size . y ;
if ( win81p_LogicalToPhysicalPointForPerMonitorDPI ) {
2024-03-12 14:40:40 +00:00
win81p_LogicalToPhysicalPointForPerMonitorDPI ( nullptr , & p1 ) ;
win81p_LogicalToPhysicalPointForPerMonitorDPI ( nullptr , & p2 ) ;
2023-03-16 21:26:09 +00:00
}
Ref < Image > img ;
2024-03-12 14:40:40 +00:00
HDC dc = GetDC ( nullptr ) ;
2023-03-16 21:26:09 +00:00
if ( dc ) {
HDC hdc = CreateCompatibleDC ( dc ) ;
int width = p2 . x - p1 . x ;
int height = p2 . y - p1 . y ;
if ( hdc ) {
HBITMAP hbm = CreateCompatibleBitmap ( dc , width , height ) ;
if ( hbm ) {
SelectObject ( hdc , hbm ) ;
BitBlt ( hdc , 0 , 0 , width , height , dc , p1 . x , p1 . y , SRCCOPY ) ;
2023-05-17 16:35:18 +00:00
BITMAPINFO bmp_info = { } ;
2023-03-16 21:26:09 +00:00
bmp_info . bmiHeader . biSize = sizeof ( bmp_info . bmiHeader ) ;
bmp_info . bmiHeader . biWidth = width ;
bmp_info . bmiHeader . biHeight = - height ;
bmp_info . bmiHeader . biPlanes = 1 ;
bmp_info . bmiHeader . biBitCount = 32 ;
bmp_info . bmiHeader . biCompression = BI_RGB ;
Vector < uint8_t > img_data ;
img_data . resize ( width * height * 4 ) ;
GetDIBits ( hdc , hbm , 0 , height , img_data . ptrw ( ) , & bmp_info , DIB_RGB_COLORS ) ;
uint8_t * wr = ( uint8_t * ) img_data . ptrw ( ) ;
for ( int i = 0 ; i < width * height ; i + + ) {
2024-02-16 15:10:16 +00:00
SWAP ( wr [ i * 4 + 0 ] , wr [ i * 4 + 2 ] ) ; // Swap B and R.
2023-03-16 21:26:09 +00:00
}
img = Image : : create_from_data ( width , height , false , Image : : FORMAT_RGBA8 , img_data ) ;
DeleteObject ( hbm ) ;
}
DeleteDC ( hdc ) ;
}
2024-03-12 14:40:40 +00:00
ReleaseDC ( nullptr , dc ) ;
2023-03-16 21:26:09 +00:00
}
return img ;
}
2022-01-27 19:46:57 +00:00
float DisplayServerWindows : : screen_get_refresh_rate ( int p_screen ) const {
_THREAD_SAFE_METHOD_
2023-03-21 11:08:46 +00:00
p_screen = _get_screen_index ( p_screen ) ;
2023-10-31 12:22:34 +00:00
EnumRefreshRateData data = { Vector < DISPLAYCONFIG_PATH_INFO > ( ) , Vector < DISPLAYCONFIG_MODE_INFO > ( ) , 0 , p_screen , SCREEN_REFRESH_RATE_FALLBACK } ;
uint32_t path_count = 0 ;
uint32_t mode_count = 0 ;
if ( GetDisplayConfigBufferSizes ( QDC_ONLY_ACTIVE_PATHS , & path_count , & mode_count ) = = ERROR_SUCCESS ) {
data . paths . resize ( path_count ) ;
data . modes . resize ( mode_count ) ;
if ( QueryDisplayConfig ( QDC_ONLY_ACTIVE_PATHS , & path_count , data . paths . ptrw ( ) , & mode_count , data . modes . ptrw ( ) , nullptr ) ! = ERROR_SUCCESS ) {
data . paths . clear ( ) ;
data . modes . clear ( ) ;
}
}
2022-01-27 19:46:57 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcRefreshRate , ( LPARAM ) & data ) ;
return data . rate ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : screen_set_keep_on ( bool p_enable ) {
2022-08-03 16:29:07 +00:00
if ( keep_screen_on = = p_enable ) {
return ;
}
if ( p_enable ) {
const String reason = " Godot Engine running with display/window/energy_saving/keep_screen_on = true " ;
Char16String reason_utf16 = reason . utf16 ( ) ;
REASON_CONTEXT context ;
context . Version = POWER_REQUEST_CONTEXT_VERSION ;
context . Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING ;
context . Reason . SimpleReasonString = ( LPWSTR ) ( reason_utf16 . ptrw ( ) ) ;
power_request = PowerCreateRequest ( & context ) ;
if ( power_request = = INVALID_HANDLE_VALUE ) {
print_error ( " Failed to enable screen_keep_on. " ) ;
return ;
}
if ( PowerSetRequest ( power_request , POWER_REQUEST_TYPE : : PowerRequestSystemRequired ) = = 0 ) {
print_error ( " Failed to request system sleep override. " ) ;
return ;
}
if ( PowerSetRequest ( power_request , POWER_REQUEST_TYPE : : PowerRequestDisplayRequired ) = = 0 ) {
print_error ( " Failed to request display timeout override. " ) ;
return ;
}
} else {
PowerClearRequest ( power_request , POWER_REQUEST_TYPE : : PowerRequestSystemRequired ) ;
PowerClearRequest ( power_request , POWER_REQUEST_TYPE : : PowerRequestDisplayRequired ) ;
CloseHandle ( power_request ) ;
power_request = nullptr ;
}
keep_screen_on = p_enable ;
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
bool DisplayServerWindows : : screen_is_kept_on ( ) const {
2022-08-03 16:29:07 +00:00
return keep_screen_on ;
2020-03-09 15:56:48 +00:00
}
2020-03-24 23:15:35 +00:00
Vector < DisplayServer : : WindowID > DisplayServerWindows : : get_window_list ( ) const {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
2020-03-24 23:15:35 +00:00
Vector < DisplayServer : : WindowID > ret ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
ret . push_back ( E . key ) ;
2020-03-09 15:56:48 +00:00
}
return ret ;
}
2020-03-24 23:15:35 +00:00
DisplayServer : : WindowID DisplayServerWindows : : get_window_at_screen_position ( const Point2i & p_position ) const {
2023-01-04 22:00:02 +00:00
Point2i offset = _get_screens_origin ( ) ;
2020-03-24 23:15:35 +00:00
POINT p ;
2023-01-04 22:00:02 +00:00
p . x = p_position . x + offset . x ;
p . y = p_position . y + offset . y ;
2020-03-24 23:15:35 +00:00
HWND hwnd = WindowFromPoint ( p ) ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( E . value . hWnd = = hwnd ) {
return E . key ;
2020-03-24 23:15:35 +00:00
}
}
return INVALID_WINDOW_ID ;
}
2023-01-04 22:00:02 +00:00
DisplayServer : : WindowID DisplayServerWindows : : create_sub_window ( WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Rect2i & p_rect ) {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
2023-01-04 22:00:02 +00:00
WindowID window_id = _create_window ( p_mode , p_vsync_mode , p_flags , p_rect ) ;
2020-06-27 16:26:34 +00:00
ERR_FAIL_COND_V_MSG ( window_id = = INVALID_WINDOW_ID , INVALID_WINDOW_ID , " Failed to create sub window. " ) ;
2020-03-20 20:51:53 +00:00
WindowData & wd = windows [ window_id ] ;
if ( p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT ) {
wd . resizable = false ;
}
if ( p_flags & WINDOW_FLAG_BORDERLESS_BIT ) {
wd . borderless = true ;
}
2022-01-28 09:19:53 +00:00
if ( p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT & & p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-03-20 20:51:53 +00:00
wd . always_on_top = true ;
}
if ( p_flags & WINDOW_FLAG_NO_FOCUS_BIT ) {
wd . no_focus = true ;
2020-03-09 15:56:48 +00:00
}
2023-01-15 10:05:25 +00:00
if ( p_flags & WINDOW_FLAG_MOUSE_PASSTHROUGH_BIT ) {
wd . mpass = true ;
}
2022-02-24 09:21:23 +00:00
if ( p_flags & WINDOW_FLAG_POPUP_BIT ) {
wd . is_popup = true ;
}
2023-01-19 07:21:36 +00:00
if ( p_flags & WINDOW_FLAG_TRANSPARENT_BIT ) {
DWM_BLURBEHIND bb ;
ZeroMemory ( & bb , sizeof ( bb ) ) ;
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = TRUE ;
DwmEnableBlurBehindWindow ( wd . hWnd , & bb ) ;
wd . layered_window = true ;
}
2020-03-09 15:56:48 +00:00
2022-02-07 01:28:42 +00:00
// Inherit icons from MAIN_WINDOW for all sub windows.
HICON mainwindow_icon = ( HICON ) SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_GETICON , ICON_SMALL , 0 ) ;
if ( mainwindow_icon ) {
SendMessage ( windows [ window_id ] . hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) mainwindow_icon ) ;
}
mainwindow_icon = ( HICON ) SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_GETICON , ICON_BIG , 0 ) ;
if ( mainwindow_icon ) {
SendMessage ( windows [ window_id ] . hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) mainwindow_icon ) ;
}
2023-12-19 17:57:56 +00:00
# ifdef RD_ENABLED
if ( rendering_device ) {
rendering_device - > screen_create ( window_id ) ;
}
# endif
2020-08-21 07:39:30 +00:00
return window_id ;
}
void DisplayServerWindows : : show_window ( WindowID p_id ) {
2021-09-10 13:44:44 +00:00
ERR_FAIL_COND ( ! windows . has ( p_id ) ) ;
2020-08-21 07:39:30 +00:00
WindowData & wd = windows [ p_id ] ;
2022-02-24 09:21:23 +00:00
popup_open ( p_id ) ;
2020-08-21 07:39:30 +00:00
2020-08-22 15:50:06 +00:00
if ( p_id ! = MAIN_WINDOW_ID ) {
_update_window_style ( p_id ) ;
}
2022-09-15 07:05:41 +00:00
if ( wd . maximized ) {
ShowWindow ( wd . hWnd , SW_SHOWMAXIMIZED ) ;
SetForegroundWindow ( wd . hWnd ) ; // Slightly higher priority.
SetFocus ( wd . hWnd ) ; // Set keyboard focus.
} else if ( wd . minimized ) {
ShowWindow ( wd . hWnd , SW_SHOWMINIMIZED ) ;
2023-06-07 18:40:36 +00:00
} else if ( wd . no_focus ) {
2022-05-08 23:25:49 +00:00
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
ShowWindow ( wd . hWnd , SW_SHOWNA ) ;
2023-06-07 18:40:36 +00:00
} else if ( wd . is_popup ) {
ShowWindow ( wd . hWnd , SW_SHOWNA ) ;
SetFocus ( wd . hWnd ) ; // Set keyboard focus.
2022-05-08 23:25:49 +00:00
} else {
ShowWindow ( wd . hWnd , SW_SHOW ) ;
2021-09-10 13:44:44 +00:00
SetForegroundWindow ( wd . hWnd ) ; // Slightly higher priority.
SetFocus ( wd . hWnd ) ; // Set keyboard focus.
2020-03-20 20:51:53 +00:00
}
2023-01-19 07:21:36 +00:00
if ( wd . always_on_top ) {
SetWindowPos ( wd . hWnd , HWND_TOPMOST , 0 , 0 , 0 , 0 , SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ( ( wd . no_focus | | wd . is_popup ) ? SWP_NOACTIVATE : 0 ) ) ;
}
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : delete_sub_window ( WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
ERR_FAIL_COND_MSG ( p_window = = MAIN_WINDOW_ID , " Main window cannot be deleted. " ) ;
2022-02-24 09:21:23 +00:00
popup_close ( p_window ) ;
2020-03-09 15:56:48 +00:00
WindowData & wd = windows [ p_window ] ;
2023-12-07 20:51:53 +00:00
IPropertyStore * prop_store ;
HRESULT hr = SHGetPropertyStoreForWindow ( wd . hWnd , IID_IPropertyStore , ( void * * ) & prop_store ) ;
if ( hr = = S_OK ) {
PROPVARIANT val ;
PropVariantInit ( & val ) ;
prop_store - > SetValue ( PKEY_AppUserModel_ID , val ) ;
prop_store - > Release ( ) ;
}
2020-03-09 15:56:48 +00:00
while ( wd . transient_children . size ( ) ) {
2022-05-19 15:00:06 +00:00
window_set_transient ( * wd . transient_children . begin ( ) , INVALID_WINDOW_ID ) ;
2020-03-09 15:56:48 +00:00
}
if ( wd . transient_parent ! = INVALID_WINDOW_ID ) {
window_set_transient ( p_window , INVALID_WINDOW_ID ) ;
}
2023-12-19 11:48:02 +00:00
# ifdef RD_ENABLED
2023-12-19 17:57:56 +00:00
if ( rendering_device ) {
rendering_device - > screen_free ( p_window ) ;
}
if ( rendering_context ) {
rendering_context - > window_destroy ( p_window ) ;
2023-01-09 15:56:16 +00:00
}
# endif
2021-10-26 15:18:39 +00:00
# ifdef GLES3_ENABLED
2021-11-12 12:49:49 +00:00
if ( gl_manager_angle ) {
gl_manager_angle - > window_destroy ( p_window ) ;
}
if ( gl_manager_native ) {
gl_manager_native - > window_destroy ( p_window ) ;
2020-11-18 18:11:30 +00:00
}
# endif
2020-03-09 15:56:48 +00:00
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ p_window ] . wtctx ) {
2020-05-05 11:16:02 +00:00
wintab_WTClose ( windows [ p_window ] . wtctx ) ;
2024-03-12 14:40:40 +00:00
windows [ p_window ] . wtctx = nullptr ;
2020-05-05 11:16:02 +00:00
}
2020-03-09 15:56:48 +00:00
DestroyWindow ( windows [ p_window ] . hWnd ) ;
windows . erase ( p_window ) ;
2023-02-02 20:41:36 +00:00
if ( last_focused_window = = p_window ) {
last_focused_window = INVALID_WINDOW_ID ;
}
2020-03-09 15:56:48 +00:00
}
2020-11-18 18:11:30 +00:00
void DisplayServerWindows : : gl_window_make_current ( DisplayServer : : WindowID p_window_id ) {
2021-10-26 15:18:39 +00:00
# if defined(GLES3_ENABLED)
2021-11-12 12:49:49 +00:00
if ( gl_manager_angle ) {
gl_manager_angle - > window_make_current ( p_window_id ) ;
}
if ( gl_manager_native ) {
gl_manager_native - > window_make_current ( p_window_id ) ;
2022-07-06 06:56:47 +00:00
}
2020-11-18 18:11:30 +00:00
# endif
}
2022-01-14 10:41:04 +00:00
int64_t DisplayServerWindows : : window_get_native_handle ( HandleType p_handle_type , WindowID p_window ) const {
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , 0 ) ;
switch ( p_handle_type ) {
case DISPLAY_HANDLE : {
return 0 ; // Not supported.
}
case WINDOW_HANDLE : {
return ( int64_t ) windows [ p_window ] . hWnd ;
}
2022-10-23 01:29:15 +00:00
# if defined(GLES3_ENABLED)
2022-01-14 10:41:04 +00:00
case WINDOW_VIEW : {
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
return ( int64_t ) gl_manager_native - > get_hdc ( p_window ) ;
} else {
return ( int64_t ) GetDC ( windows [ p_window ] . hWnd ) ;
2022-11-13 13:06:26 +00:00
}
2022-01-14 10:41:04 +00:00
}
2022-10-23 01:29:15 +00:00
case OPENGL_CONTEXT : {
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
return ( int64_t ) gl_manager_native - > get_hglrc ( p_window ) ;
}
if ( gl_manager_angle ) {
return ( int64_t ) gl_manager_angle - > get_context ( p_window ) ;
2022-11-13 13:06:26 +00:00
}
return 0 ;
2022-10-23 01:29:15 +00:00
}
# endif
2022-01-14 10:41:04 +00:00
default : {
return 0 ;
}
}
}
2020-03-24 23:15:35 +00:00
void DisplayServerWindows : : window_attach_instance_id ( ObjectID p_instance , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . instance_id = p_instance ;
}
ObjectID DisplayServerWindows : : window_get_attached_instance_id ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , ObjectID ( ) ) ;
return windows [ p_window ] . instance_id ;
}
2020-03-14 16:06:39 +00:00
void DisplayServerWindows : : window_set_rect_changed_callback ( const Callable & p_callable , WindowID p_window ) {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-03-14 16:06:39 +00:00
windows [ p_window ] . rect_changed_callback = p_callable ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : window_set_window_event_callback ( const Callable & p_callable , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . event_callback = p_callable ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_input_event_callback ( const Callable & p_callable , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . input_event_callback = p_callable ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_input_text_callback ( const Callable & p_callable , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . input_text_callback = p_callable ;
}
void DisplayServerWindows : : window_set_drop_files_callback ( const Callable & p_callable , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . drop_files_callback = p_callable ;
}
void DisplayServerWindows : : window_set_title ( const String & p_title , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-07-27 10:43:20 +00:00
SetWindowTextW ( windows [ p_window ] . hWnd , ( LPCWSTR ) ( p_title . utf16 ( ) . get_data ( ) ) ) ;
2020-03-09 15:56:48 +00:00
}
2023-08-08 08:31:56 +00:00
Size2i DisplayServerWindows : : window_get_title_size ( const String & p_title , WindowID p_window ) const {
_THREAD_SAFE_METHOD_
Size2i size ;
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , size ) ;
const WindowData & wd = windows [ p_window ] ;
if ( wd . fullscreen | | wd . minimized | | wd . borderless ) {
return size ;
}
2024-03-12 14:40:40 +00:00
HDC hdc = GetDCEx ( wd . hWnd , nullptr , DCX_WINDOW ) ;
2023-08-08 08:31:56 +00:00
if ( hdc ) {
Char16String s = p_title . utf16 ( ) ;
SIZE text_size ;
if ( GetTextExtentPoint32W ( hdc , ( LPCWSTR ) ( s . get_data ( ) ) , s . length ( ) , & text_size ) ) {
size . x = text_size . cx ;
size . y = text_size . cy ;
}
ReleaseDC ( wd . hWnd , hdc ) ;
}
RECT rect ;
if ( DwmGetWindowAttribute ( wd . hWnd , DWMWA_CAPTION_BUTTON_BOUNDS , & rect , sizeof ( RECT ) ) = = S_OK ) {
if ( rect . right - rect . left > 0 ) {
ClientToScreen ( wd . hWnd , ( POINT * ) & rect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & rect . right ) ;
if ( win81p_PhysicalToLogicalPointForPerMonitorDPI ) {
2024-03-12 14:40:40 +00:00
win81p_PhysicalToLogicalPointForPerMonitorDPI ( nullptr , ( POINT * ) & rect . left ) ;
win81p_PhysicalToLogicalPointForPerMonitorDPI ( nullptr , ( POINT * ) & rect . right ) ;
2023-08-08 08:31:56 +00:00
}
size . x + = ( rect . right - rect . left ) ;
size . y = MAX ( size . y , rect . bottom - rect . top ) ;
}
}
if ( icon . is_valid ( ) ) {
size . x + = 32 ;
} else {
size . x + = 16 ;
}
return size ;
}
2020-06-29 09:31:36 +00:00
void DisplayServerWindows : : window_set_mouse_passthrough ( const Vector < Vector2 > & p_region , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
windows [ p_window ] . mpath = p_region ;
_update_window_mouse_passthrough ( p_window ) ;
}
void DisplayServerWindows : : _update_window_mouse_passthrough ( WindowID p_window ) {
2021-09-10 13:44:44 +00:00
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2023-01-15 10:05:25 +00:00
if ( windows [ p_window ] . mpass | | windows [ p_window ] . mpath . size ( ) = = 0 ) {
2023-08-02 07:40:40 +00:00
SetWindowRgn ( windows [ p_window ] . hWnd , nullptr , FALSE ) ;
2020-06-29 09:31:36 +00:00
} else {
POINT * points = ( POINT * ) memalloc ( sizeof ( POINT ) * windows [ p_window ] . mpath . size ( ) ) ;
for ( int i = 0 ; i < windows [ p_window ] . mpath . size ( ) ; i + + ) {
if ( windows [ p_window ] . borderless ) {
points [ i ] . x = windows [ p_window ] . mpath [ i ] . x ;
points [ i ] . y = windows [ p_window ] . mpath [ i ] . y ;
} else {
points [ i ] . x = windows [ p_window ] . mpath [ i ] . x + GetSystemMetrics ( SM_CXSIZEFRAME ) ;
points [ i ] . y = windows [ p_window ] . mpath [ i ] . y + GetSystemMetrics ( SM_CYSIZEFRAME ) + GetSystemMetrics ( SM_CYCAPTION ) ;
}
}
HRGN region = CreatePolygonRgn ( points , windows [ p_window ] . mpath . size ( ) , ALTERNATE ) ;
2023-08-02 07:40:40 +00:00
SetWindowRgn ( windows [ p_window ] . hWnd , region , FALSE ) ;
2020-06-29 09:31:36 +00:00
memfree ( points ) ;
}
}
2020-03-09 15:56:48 +00:00
int DisplayServerWindows : : window_get_current_screen ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , - 1 ) ;
EnumScreenData data = { 0 , 0 , MonitorFromWindow ( windows [ p_window ] . hWnd , MONITOR_DEFAULTTONEAREST ) } ;
2020-04-01 23:20:12 +00:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcScreen , ( LPARAM ) & data ) ;
2020-03-09 15:56:48 +00:00
return data . screen ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_current_screen ( int p_screen , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
ERR_FAIL_INDEX ( p_screen , get_screen_count ( ) ) ;
2022-12-08 18:34:15 +00:00
if ( window_get_current_screen ( p_window ) = = p_screen ) {
return ;
}
2022-01-15 18:47:34 +00:00
const WindowData & wd = windows [ p_window ] ;
if ( wd . fullscreen ) {
2023-01-04 22:00:02 +00:00
Point2 pos = screen_get_position ( p_screen ) + _get_screens_origin ( ) ;
2022-01-15 18:47:34 +00:00
Size2 size = screen_get_size ( p_screen ) ;
MoveWindow ( wd . hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
} else {
2022-12-08 18:34:15 +00:00
Rect2i srect = screen_get_usable_rect ( p_screen ) ;
Point2i wpos = window_get_position ( p_window ) - screen_get_position ( window_get_current_screen ( p_window ) ) ;
Size2i wsize = window_get_size ( p_window ) ;
wpos + = srect . position ;
2024-03-03 11:49:08 +00:00
wpos = wpos . clamp ( srect . position , srect . position + srect . size - wsize / 3 ) ;
2022-12-08 18:34:15 +00:00
window_set_position ( wpos , p_window ) ;
2022-01-15 18:47:34 +00:00
}
2020-03-09 15:56:48 +00:00
}
Point2i DisplayServerWindows : : window_get_position ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Point2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
if ( wd . minimized ) {
return wd . last_pos ;
}
2020-03-12 12:37:40 +00:00
POINT point ;
point . x = 0 ;
point . y = 0 ;
ClientToScreen ( wd . hWnd , & point ) ;
2023-01-04 22:00:02 +00:00
return Point2i ( point . x , point . y ) - _get_screens_origin ( ) ;
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2022-11-30 08:28:16 +00:00
Point2i DisplayServerWindows : : window_get_position_with_decorations ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Point2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
if ( wd . minimized ) {
return wd . last_pos ;
}
RECT r ;
if ( GetWindowRect ( wd . hWnd , & r ) ) {
2023-01-04 22:00:02 +00:00
return Point2i ( r . left , r . top ) - _get_screens_origin ( ) ;
2022-11-30 08:28:16 +00:00
}
return Point2i ( ) ;
}
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : _update_real_mouse_position ( WindowID p_window ) {
2021-09-10 13:44:44 +00:00
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-03-09 15:56:48 +00:00
POINT mouse_pos ;
if ( GetCursorPos ( & mouse_pos ) & & ScreenToClient ( windows [ p_window ] . hWnd , & mouse_pos ) ) {
if ( mouse_pos . x > 0 & & mouse_pos . y > 0 & & mouse_pos . x < = windows [ p_window ] . width & & mouse_pos . y < = windows [ p_window ] . height ) {
old_x = mouse_pos . x ;
old_y = mouse_pos . y ;
old_invalid = false ;
2020-04-28 13:19:37 +00:00
Input : : get_singleton ( ) - > set_mouse_position ( Point2i ( mouse_pos . x , mouse_pos . y ) ) ;
2020-03-09 15:56:48 +00:00
}
}
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_position ( const Point2i & p_position , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
2022-09-15 07:05:41 +00:00
if ( wd . fullscreen | | wd . maximized ) {
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-12 12:37:40 +00:00
2023-01-04 22:00:02 +00:00
Point2i offset = _get_screens_origin ( ) ;
2020-03-12 12:37:40 +00:00
RECT rc ;
2023-01-04 22:00:02 +00:00
rc . left = p_position . x + offset . x ;
rc . right = p_position . x + wd . width + offset . x ;
rc . bottom = p_position . y + wd . height + offset . y ;
rc . top = p_position . y + offset . y ;
2020-03-09 15:56:48 +00:00
2020-03-12 12:37:40 +00:00
const DWORD style = GetWindowLongPtr ( wd . hWnd , GWL_STYLE ) ;
const DWORD exStyle = GetWindowLongPtr ( wd . hWnd , GWL_EXSTYLE ) ;
AdjustWindowRectEx ( & rc , style , false , exStyle ) ;
MoveWindow ( wd . hWnd , rc . left , rc . top , rc . right - rc . left , rc . bottom - rc . top , TRUE ) ;
2021-09-10 13:44:44 +00:00
2020-03-09 15:56:48 +00:00
wd . last_pos = p_position ;
_update_real_mouse_position ( p_window ) ;
}
2022-01-19 12:04:05 +00:00
void DisplayServerWindows : : window_set_exclusive ( WindowID p_window , bool p_exclusive ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
if ( wd . exclusive ! = p_exclusive ) {
wd . exclusive = p_exclusive ;
if ( wd . transient_parent ! = INVALID_WINDOW_ID ) {
if ( wd . exclusive ) {
2022-04-05 10:40:26 +00:00
WindowData & wd_parent = windows [ wd . transient_parent ] ;
2022-01-19 12:04:05 +00:00
SetWindowLongPtr ( wd . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) wd_parent . hWnd ) ;
} else {
SetWindowLongPtr ( wd . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) nullptr ) ;
}
}
}
}
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_transient ( WindowID p_window , WindowID p_parent ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( p_window = = p_parent ) ;
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2021-09-10 13:44:44 +00:00
2020-03-09 15:56:48 +00:00
WindowData & wd_window = windows [ p_window ] ;
ERR_FAIL_COND ( wd_window . transient_parent = = p_parent ) ;
ERR_FAIL_COND_MSG ( wd_window . always_on_top , " Windows with the 'on top' can't become transient. " ) ;
if ( p_parent = = INVALID_WINDOW_ID ) {
2021-09-10 13:44:44 +00:00
// Remove transient.
2020-03-09 15:56:48 +00:00
ERR_FAIL_COND ( wd_window . transient_parent = = INVALID_WINDOW_ID ) ;
ERR_FAIL_COND ( ! windows . has ( wd_window . transient_parent ) ) ;
WindowData & wd_parent = windows [ wd_window . transient_parent ] ;
wd_window . transient_parent = INVALID_WINDOW_ID ;
wd_parent . transient_children . erase ( p_window ) ;
2022-01-19 12:04:05 +00:00
if ( wd_window . exclusive ) {
SetWindowLongPtr ( wd_window . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) nullptr ) ;
}
2020-03-09 15:56:48 +00:00
} else {
ERR_FAIL_COND ( ! windows . has ( p_parent ) ) ;
ERR_FAIL_COND_MSG ( wd_window . transient_parent ! = INVALID_WINDOW_ID , " Window already has a transient parent " ) ;
WindowData & wd_parent = windows [ p_parent ] ;
wd_window . transient_parent = p_parent ;
wd_parent . transient_children . insert ( p_window ) ;
2022-01-19 12:04:05 +00:00
if ( wd_window . exclusive ) {
SetWindowLongPtr ( wd_window . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) wd_parent . hWnd ) ;
}
2020-03-09 15:56:48 +00:00
}
}
void DisplayServerWindows : : window_set_max_size ( const Size2i p_size , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
if ( ( p_size ! = Size2 ( ) ) & & ( ( p_size . x < wd . min_size . x ) | | ( p_size . y < wd . min_size . y ) ) ) {
ERR_PRINT ( " Maximum window size can't be smaller than minimum window size! " ) ;
return ;
}
wd . max_size = p_size ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
Size2i DisplayServerWindows : : window_get_max_size ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Size2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
return wd . max_size ;
}
void DisplayServerWindows : : window_set_min_size ( const Size2i p_size , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
if ( ( p_size ! = Size2 ( ) ) & & ( wd . max_size ! = Size2 ( ) ) & & ( ( p_size . x > wd . max_size . x ) | | ( p_size . y > wd . max_size . y ) ) ) {
ERR_PRINT ( " Minimum window size can't be larger than maximum window size! " ) ;
return ;
}
wd . min_size = p_size ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
Size2i DisplayServerWindows : : window_get_min_size ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Size2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
return wd . min_size ;
}
void DisplayServerWindows : : window_set_size ( const Size2i p_size , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
2022-09-15 07:05:41 +00:00
if ( wd . fullscreen | | wd . maximized ) {
return ;
}
2020-03-09 15:56:48 +00:00
int w = p_size . width ;
int h = p_size . height ;
wd . width = w ;
wd . height = h ;
2020-03-20 02:32:09 +00:00
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
rendering_context - > window_set_size ( p_window , w , h ) ;
2023-01-09 15:56:16 +00:00
}
# endif
2021-10-26 15:18:39 +00:00
# if defined(GLES3_ENABLED)
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
gl_manager_native - > window_resize ( p_window , w , h ) ;
}
if ( gl_manager_angle ) {
gl_manager_angle - > window_resize ( p_window , w , h ) ;
2020-11-18 18:11:30 +00:00
}
# endif
2020-03-09 15:56:48 +00:00
RECT rect ;
GetWindowRect ( wd . hWnd , & rect ) ;
if ( ! wd . borderless ) {
RECT crect ;
GetClientRect ( wd . hWnd , & crect ) ;
w + = ( rect . right - rect . left ) - ( crect . right - crect . left ) ;
h + = ( rect . bottom - rect . top ) - ( crect . bottom - crect . top ) ;
}
MoveWindow ( wd . hWnd , rect . left , rect . top , w , h , TRUE ) ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
Size2i DisplayServerWindows : : window_get_size ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Size2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
2021-08-23 02:30:28 +00:00
// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.
2020-03-09 15:56:48 +00:00
if ( wd . minimized ) {
return Size2 ( wd . width , wd . height ) ;
}
RECT r ;
2021-09-10 13:44:44 +00:00
if ( GetClientRect ( wd . hWnd , & r ) ) { // Retrieves area inside of window border, including decoration.
2020-03-09 15:56:48 +00:00
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
2020-05-14 12:29:06 +00:00
2022-11-30 08:28:16 +00:00
Size2i DisplayServerWindows : : window_get_size_with_decorations ( WindowID p_window ) const {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Size2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
RECT r ;
2021-09-10 13:44:44 +00:00
if ( GetWindowRect ( wd . hWnd , & r ) ) { // Retrieves area inside of window border, including decoration.
2020-03-09 15:56:48 +00:00
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
2024-02-14 14:42:26 +00:00
void DisplayServerWindows : : _get_window_style ( bool p_main_window , bool p_fullscreen , bool p_multiwindow_fs , bool p_borderless , bool p_resizable , bool p_maximized , bool p_maximized_fs , bool p_no_activate_focus , DWORD & r_style , DWORD & r_style_ex ) {
2021-09-10 13:44:44 +00:00
// Windows docs for window styles:
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
2020-03-14 19:56:37 +00:00
r_style = 0 ;
r_style_ex = WS_EX_WINDOWEDGE ;
if ( p_main_window ) {
r_style_ex | = WS_EX_APPWINDOW ;
2022-02-24 09:21:23 +00:00
r_style | = WS_VISIBLE ;
2020-03-14 16:06:39 +00:00
}
2020-03-14 19:56:37 +00:00
if ( p_fullscreen | | p_borderless ) {
2021-09-10 13:44:44 +00:00
r_style | = WS_POPUP ; // p_borderless was WS_EX_TOOLWINDOW in the past.
2024-02-14 14:42:26 +00:00
if ( p_maximized ) {
r_style | = WS_MAXIMIZE ;
}
if ( ! p_fullscreen ) {
r_style | = WS_SYSMENU | WS_MINIMIZEBOX ;
if ( p_resizable ) {
r_style | = WS_MAXIMIZEBOX ;
}
}
if ( ( p_fullscreen & & p_multiwindow_fs ) | | p_maximized_fs ) {
2022-01-28 09:19:53 +00:00
r_style | = WS_BORDER ; // Allows child windows to be displayed on top of full screen.
}
2020-03-09 15:56:48 +00:00
} else {
2020-03-14 19:56:37 +00:00
if ( p_resizable ) {
2020-03-09 15:56:48 +00:00
if ( p_maximized ) {
2020-03-14 19:56:37 +00:00
r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE ;
2020-03-09 15:56:48 +00:00
} else {
2020-03-14 19:56:37 +00:00
r_style = WS_OVERLAPPEDWINDOW ;
2020-03-09 15:56:48 +00:00
}
} else {
2023-06-02 13:31:50 +00:00
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX ;
2020-03-09 15:56:48 +00:00
}
}
2020-03-20 20:51:53 +00:00
if ( p_no_activate_focus ) {
r_style_ex | = WS_EX_TOPMOST | WS_EX_NOACTIVATE ;
}
2022-02-24 09:21:23 +00:00
if ( ! p_borderless & & ! p_no_activate_focus ) {
r_style | = WS_VISIBLE ;
}
2020-03-14 19:56:37 +00:00
r_style | = WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
2022-02-11 07:21:58 +00:00
r_style_ex | = WS_EX_ACCEPTFILES ;
2020-03-14 19:56:37 +00:00
}
2020-12-31 11:49:03 +00:00
void DisplayServerWindows : : _update_window_style ( WindowID p_window , bool p_repaint ) {
2020-03-14 19:56:37 +00:00
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
DWORD style = 0 ;
DWORD style_ex = 0 ;
2024-02-14 14:42:26 +00:00
_get_window_style ( p_window = = MAIN_WINDOW_ID , wd . fullscreen , wd . multiwindow_fs , wd . borderless , wd . resizable , wd . maximized , wd . maximized_fs , wd . no_focus | | wd . is_popup , style , style_ex ) ;
2020-03-14 19:56:37 +00:00
2020-03-14 16:06:39 +00:00
SetWindowLongPtr ( wd . hWnd , GWL_STYLE , style ) ;
SetWindowLongPtr ( wd . hWnd , GWL_EXSTYLE , style_ex ) ;
2022-05-10 08:12:04 +00:00
if ( icon . is_valid ( ) ) {
set_icon ( icon ) ;
}
2022-02-24 09:21:23 +00:00
SetWindowPos ( wd . hWnd , wd . always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST , 0 , 0 , 0 , 0 , SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | ( ( wd . no_focus | | wd . is_popup ) ? SWP_NOACTIVATE : 0 ) ) ;
2020-03-09 15:56:48 +00:00
if ( p_repaint ) {
RECT rect ;
GetWindowRect ( wd . hWnd , & rect ) ;
MoveWindow ( wd . hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
}
}
void DisplayServerWindows : : window_set_mode ( WindowMode p_mode , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
2022-01-28 09:19:53 +00:00
if ( wd . fullscreen & & p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-03-09 15:56:48 +00:00
RECT rect ;
wd . fullscreen = false ;
2022-01-28 09:19:53 +00:00
wd . multiwindow_fs = false ;
2020-12-31 11:49:03 +00:00
wd . maximized = wd . was_maximized ;
2020-03-09 15:56:48 +00:00
2023-06-12 18:22:45 +00:00
_update_window_style ( p_window , false ) ;
2020-03-09 15:56:48 +00:00
if ( wd . pre_fs_valid ) {
rect = wd . pre_fs_rect ;
} else {
rect . left = 0 ;
rect . right = wd . width ;
rect . top = 0 ;
rect . bottom = wd . height ;
2020-12-31 11:49:03 +00:00
wd . pre_fs_valid = true ;
2020-03-09 15:56:48 +00:00
}
2024-02-14 14:42:26 +00:00
ShowWindow ( wd . hWnd , SW_RESTORE ) ;
2020-03-09 15:56:48 +00:00
MoveWindow ( wd . hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
2021-09-20 04:42:56 +00:00
if ( restore_mouse_trails > 1 ) {
2024-03-12 14:40:40 +00:00
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , nullptr , 0 ) ;
2021-09-20 04:42:56 +00:00
restore_mouse_trails = 0 ;
}
2023-11-06 07:36:12 +00:00
}
if ( p_mode = = WINDOW_MODE_WINDOWED ) {
2020-12-31 11:49:03 +00:00
ShowWindow ( wd . hWnd , SW_RESTORE ) ;
wd . maximized = false ;
wd . minimized = false ;
2020-03-09 15:56:48 +00:00
}
if ( p_mode = = WINDOW_MODE_MAXIMIZED ) {
ShowWindow ( wd . hWnd , SW_MAXIMIZE ) ;
wd . maximized = true ;
wd . minimized = false ;
}
if ( p_mode = = WINDOW_MODE_MINIMIZED ) {
ShowWindow ( wd . hWnd , SW_MINIMIZE ) ;
wd . maximized = false ;
wd . minimized = true ;
}
2022-01-28 09:19:53 +00:00
if ( p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
wd . multiwindow_fs = false ;
2022-02-11 07:21:58 +00:00
_update_window_style ( p_window , false ) ;
2022-01-28 09:19:53 +00:00
} else {
wd . multiwindow_fs = true ;
2022-02-11 07:21:58 +00:00
_update_window_style ( p_window , false ) ;
2022-01-28 09:19:53 +00:00
}
if ( ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) & & ! wd . fullscreen ) {
2024-02-14 14:42:26 +00:00
if ( wd . minimized | | wd . maximized ) {
2020-03-09 15:56:48 +00:00
ShowWindow ( wd . hWnd , SW_RESTORE ) ;
}
wd . was_maximized = wd . maximized ;
if ( wd . pre_fs_valid ) {
GetWindowRect ( wd . hWnd , & wd . pre_fs_rect ) ;
}
int cs = window_get_current_screen ( p_window ) ;
2023-01-04 22:00:02 +00:00
Point2 pos = screen_get_position ( cs ) + _get_screens_origin ( ) ;
2020-03-09 15:56:48 +00:00
Size2 size = screen_get_size ( cs ) ;
wd . fullscreen = true ;
wd . maximized = false ;
wd . minimized = false ;
2022-02-11 07:21:58 +00:00
_update_window_style ( p_window , false ) ;
2020-03-09 15:56:48 +00:00
MoveWindow ( wd . hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
2021-09-20 04:42:56 +00:00
// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.
// Save number of trails so we can restore when exiting, then turn off mouse trails
SystemParametersInfoA ( SPI_GETMOUSETRAILS , 0 , & restore_mouse_trails , 0 ) ;
if ( restore_mouse_trails > 1 ) {
2024-03-12 14:40:40 +00:00
SystemParametersInfoA ( SPI_SETMOUSETRAILS , 0 , nullptr , 0 ) ;
2021-09-20 04:42:56 +00:00
}
2020-03-09 15:56:48 +00:00
}
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
DisplayServer : : WindowMode DisplayServerWindows : : window_get_mode ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , WINDOW_MODE_WINDOWED ) ;
const WindowData & wd = windows [ p_window ] ;
if ( wd . fullscreen ) {
2022-01-28 09:19:53 +00:00
if ( wd . multiwindow_fs ) {
return WINDOW_MODE_FULLSCREEN ;
} else {
return WINDOW_MODE_EXCLUSIVE_FULLSCREEN ;
}
2020-03-09 15:56:48 +00:00
} else if ( wd . minimized ) {
return WINDOW_MODE_MINIMIZED ;
} else if ( wd . maximized ) {
return WINDOW_MODE_MAXIMIZED ;
} else {
return WINDOW_MODE_WINDOWED ;
}
}
bool DisplayServerWindows : : window_is_maximize_allowed ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , false ) ;
2020-04-01 13:35:00 +00:00
// FIXME: Implement this, or confirm that it should always be true.
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
return true ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : window_set_flag ( WindowFlags p_flag , bool p_enabled , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
switch ( p_flag ) {
case WINDOW_FLAG_RESIZE_DISABLED : {
wd . resizable = ! p_enabled ;
_update_window_style ( p_window ) ;
} break ;
case WINDOW_FLAG_BORDERLESS : {
wd . borderless = p_enabled ;
_update_window_style ( p_window ) ;
2020-06-29 09:31:36 +00:00
_update_window_mouse_passthrough ( p_window ) ;
2022-02-24 09:21:23 +00:00
ShowWindow ( wd . hWnd , ( wd . no_focus | | wd . is_popup ) ? SW_SHOWNOACTIVATE : SW_SHOW ) ; // Show the window.
2020-03-09 15:56:48 +00:00
} break ;
case WINDOW_FLAG_ALWAYS_ON_TOP : {
ERR_FAIL_COND_MSG ( wd . transient_parent ! = INVALID_WINDOW_ID & & p_enabled , " Transient windows can't become on top " ) ;
wd . always_on_top = p_enabled ;
_update_window_style ( p_window ) ;
} break ;
case WINDOW_FLAG_TRANSPARENT : {
2022-09-03 10:40:53 +00:00
if ( p_enabled ) {
//enable per-pixel alpha
2022-10-06 06:30:25 +00:00
DWM_BLURBEHIND bb ;
ZeroMemory ( & bb , sizeof ( bb ) ) ;
2022-09-03 10:40:53 +00:00
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = TRUE ;
DwmEnableBlurBehindWindow ( wd . hWnd , & bb ) ;
wd . layered_window = true ;
} else {
//disable per-pixel alpha
wd . layered_window = false ;
2022-10-06 06:30:25 +00:00
DWM_BLURBEHIND bb ;
ZeroMemory ( & bb , sizeof ( bb ) ) ;
2022-09-03 10:40:53 +00:00
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = FALSE ;
DwmEnableBlurBehindWindow ( wd . hWnd , & bb ) ;
}
2020-03-09 15:56:48 +00:00
} break ;
2020-03-20 20:51:53 +00:00
case WINDOW_FLAG_NO_FOCUS : {
wd . no_focus = p_enabled ;
_update_window_style ( p_window ) ;
} break ;
2023-01-15 10:05:25 +00:00
case WINDOW_FLAG_MOUSE_PASSTHROUGH : {
wd . mpass = p_enabled ;
_update_window_mouse_passthrough ( p_window ) ;
} break ;
2022-02-24 09:21:23 +00:00
case WINDOW_FLAG_POPUP : {
ERR_FAIL_COND_MSG ( p_window = = MAIN_WINDOW_ID , " Main window can't be popup. " ) ;
2022-03-08 09:46:44 +00:00
ERR_FAIL_COND_MSG ( IsWindowVisible ( wd . hWnd ) & & ( wd . is_popup ! = p_enabled ) , " Popup flag can't changed while window is opened. " ) ;
2022-02-24 09:21:23 +00:00
wd . is_popup = p_enabled ;
} break ;
2022-10-06 06:30:25 +00:00
default :
2020-05-10 11:00:47 +00:00
break ;
2020-03-09 15:56:48 +00:00
}
}
2020-04-01 13:35:00 +00:00
2020-03-09 15:56:48 +00:00
bool DisplayServerWindows : : window_get_flag ( WindowFlags p_flag , WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , false ) ;
const WindowData & wd = windows [ p_window ] ;
switch ( p_flag ) {
case WINDOW_FLAG_RESIZE_DISABLED : {
return ! wd . resizable ;
} break ;
case WINDOW_FLAG_BORDERLESS : {
return wd . borderless ;
} break ;
case WINDOW_FLAG_ALWAYS_ON_TOP : {
return wd . always_on_top ;
} break ;
case WINDOW_FLAG_TRANSPARENT : {
2022-09-03 10:40:53 +00:00
return wd . layered_window ;
2020-04-01 13:35:00 +00:00
} break ;
case WINDOW_FLAG_NO_FOCUS : {
return wd . no_focus ;
2020-03-09 15:56:48 +00:00
} break ;
2023-01-15 10:05:25 +00:00
case WINDOW_FLAG_MOUSE_PASSTHROUGH : {
return wd . mpass ;
} break ;
2022-02-24 09:21:23 +00:00
case WINDOW_FLAG_POPUP : {
return wd . is_popup ;
} break ;
2022-10-06 06:30:25 +00:00
default :
2020-05-10 11:00:47 +00:00
break ;
2020-03-09 15:56:48 +00:00
}
return false ;
}
void DisplayServerWindows : : window_request_attention ( WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2022-04-05 10:40:26 +00:00
const WindowData & wd = windows [ p_window ] ;
2020-03-09 15:56:48 +00:00
FLASHWINFO info ;
info . cbSize = sizeof ( FLASHWINFO ) ;
info . hwnd = wd . hWnd ;
2023-06-15 08:04:54 +00:00
info . dwFlags = FLASHW_ALL ;
2020-03-09 15:56:48 +00:00
info . dwTimeout = 0 ;
info . uCount = 2 ;
FlashWindowEx ( & info ) ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_move_to_foreground ( WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
2022-02-24 09:21:23 +00:00
if ( ! wd . no_focus & & ! wd . is_popup ) {
SetForegroundWindow ( wd . hWnd ) ;
}
2020-03-09 15:56:48 +00:00
}
2023-06-15 07:53:31 +00:00
bool DisplayServerWindows : : window_is_focused ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , false ) ;
const WindowData & wd = windows [ p_window ] ;
return wd . window_focused ;
}
2023-12-22 16:50:21 +00:00
DisplayServerWindows : : WindowID DisplayServerWindows : : get_focused_window ( ) const {
return last_focused_window ;
}
2020-03-09 15:56:48 +00:00
bool DisplayServerWindows : : window_can_draw ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , false ) ;
const WindowData & wd = windows [ p_window ] ;
2021-06-02 10:51:36 +00:00
return ! wd . minimized ;
2020-03-09 15:56:48 +00:00
}
bool DisplayServerWindows : : can_any_window_draw ( ) const {
_THREAD_SAFE_METHOD_
2021-08-09 20:13:42 +00:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( ! E . value . minimized ) {
2020-03-09 15:56:48 +00:00
return true ;
}
}
return false ;
}
2022-12-10 23:21:22 +00:00
Vector2i DisplayServerWindows : : ime_get_selection ( ) const {
_THREAD_SAFE_METHOD_
DisplayServer : : WindowID window_id = _get_focused_window_or_popup ( ) ;
const WindowData & wd = windows [ window_id ] ;
if ( ! wd . ime_active ) {
return Vector2i ( ) ;
}
int cursor = ImmGetCompositionStringW ( wd . im_himc , GCS_CURSORPOS , nullptr , 0 ) ;
int32_t length = ImmGetCompositionStringW ( wd . im_himc , GCS_COMPSTR , nullptr , 0 ) ;
wchar_t * string = reinterpret_cast < wchar_t * > ( memalloc ( length ) ) ;
ImmGetCompositionStringW ( wd . im_himc , GCS_COMPSTR , string , length ) ;
int32_t utf32_cursor = 0 ;
2023-01-25 11:17:11 +00:00
for ( int32_t i = 0 ; i < length / int32_t ( sizeof ( wchar_t ) ) ; i + + ) {
2022-12-10 23:21:22 +00:00
if ( ( string [ i ] & 0xfffffc00 ) = = 0xd800 ) {
i + + ;
}
if ( i < cursor ) {
utf32_cursor + + ;
} else {
break ;
}
}
memdelete ( string ) ;
return Vector2i ( utf32_cursor , 0 ) ;
}
String DisplayServerWindows : : ime_get_text ( ) const {
_THREAD_SAFE_METHOD_
DisplayServer : : WindowID window_id = _get_focused_window_or_popup ( ) ;
const WindowData & wd = windows [ window_id ] ;
if ( ! wd . ime_active ) {
return String ( ) ;
}
String ret ;
int32_t length = ImmGetCompositionStringW ( wd . im_himc , GCS_COMPSTR , nullptr , 0 ) ;
wchar_t * string = reinterpret_cast < wchar_t * > ( memalloc ( length ) ) ;
ImmGetCompositionStringW ( wd . im_himc , GCS_COMPSTR , string , length ) ;
ret . parse_utf16 ( ( char16_t * ) string , length / sizeof ( wchar_t ) ) ;
memdelete ( string ) ;
return ret ;
}
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_ime_active ( const bool p_active , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
if ( p_active ) {
2022-12-10 23:21:22 +00:00
wd . ime_active = true ;
2020-03-09 15:56:48 +00:00
ImmAssociateContext ( wd . hWnd , wd . im_himc ) ;
2024-03-12 14:40:40 +00:00
CreateCaret ( wd . hWnd , nullptr , 1 , 1 ) ;
2020-03-09 15:56:48 +00:00
window_set_ime_position ( wd . im_position , p_window ) ;
} else {
2024-03-12 14:40:40 +00:00
ImmAssociateContext ( wd . hWnd , ( HIMC ) nullptr ) ;
2023-01-25 13:29:08 +00:00
DestroyCaret ( ) ;
2022-12-10 23:21:22 +00:00
wd . ime_active = false ;
2020-03-09 15:56:48 +00:00
}
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : window_set_ime_position ( const Point2i & p_pos , WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
wd . im_position = p_pos ;
HIMC himc = ImmGetContext ( wd . hWnd ) ;
2024-03-12 14:40:40 +00:00
if ( himc = = ( HIMC ) nullptr ) {
2020-03-09 15:56:48 +00:00
return ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
COMPOSITIONFORM cps ;
2022-12-10 23:21:22 +00:00
cps . dwStyle = CFS_POINT ;
2020-03-09 15:56:48 +00:00
cps . ptCurrentPos . x = wd . im_position . x ;
cps . ptCurrentPos . y = wd . im_position . y ;
ImmSetCompositionWindow ( himc , & cps ) ;
ImmReleaseContext ( wd . hWnd , himc ) ;
}
void DisplayServerWindows : : cursor_set_shape ( CursorShape p_shape ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2021-09-10 13:44:44 +00:00
if ( cursor_shape = = p_shape ) {
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
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 ,
2021-09-10 13:44:44 +00:00
IDC_HAND , // Finger.
2020-03-09 15:56:48 +00:00
IDC_CROSS ,
IDC_WAIT ,
IDC_APPSTARTING ,
2021-10-06 11:13:42 +00:00
IDC_SIZEALL ,
2020-03-09 15:56:48 +00:00
IDC_ARROW ,
IDC_NO ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_SIZENESW ,
IDC_SIZENWSE ,
IDC_SIZEALL ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_HELP
} ;
2022-08-30 15:50:56 +00:00
if ( cursors_cache . has ( p_shape ) ) {
2020-03-09 15:56:48 +00:00
SetCursor ( cursors [ p_shape ] ) ;
} else {
SetCursor ( LoadCursor ( hInstance , win_cursors [ p_shape ] ) ) ;
}
cursor_shape = p_shape ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
DisplayServer : : CursorShape DisplayServerWindows : : cursor_get_shape ( ) const {
return cursor_shape ;
}
2022-05-02 23:43:50 +00:00
void DisplayServerWindows : : cursor_set_custom_image ( const Ref < Resource > & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2020-03-09 15:56:48 +00:00
_THREAD_SAFE_METHOD_
2023-02-17 13:17:37 +00:00
ERR_FAIL_INDEX ( p_shape , CURSOR_MAX ) ;
2020-03-09 15:56:48 +00:00
if ( p_cursor . is_valid ( ) ) {
2022-05-13 13:04:37 +00:00
RBMap < CursorShape , Vector < Variant > > : : Element * cursor_c = cursors_cache . find ( p_shape ) ;
2020-03-09 15:56:48 +00:00
if ( cursor_c ) {
if ( cursor_c - > get ( ) [ 0 ] = = p_cursor & & cursor_c - > get ( ) [ 1 ] = = p_hotspot ) {
cursor_set_shape ( p_shape ) ;
return ;
}
cursors_cache . erase ( p_shape ) ;
}
Rect2 atlas_rect ;
2024-02-28 17:38:15 +00:00
Ref < Image > image = _get_cursor_image_from_resource ( p_cursor , p_hotspot , atlas_rect ) ;
ERR_FAIL_COND ( image . is_null ( ) ) ;
Vector2i texture_size = image - > get_size ( ) ;
2020-03-09 15:56:48 +00:00
UINT image_size = texture_size . width * texture_size . height ;
2021-09-10 13:44:44 +00:00
// Create the BITMAP with alpha channel.
2022-08-30 15:50:56 +00:00
COLORREF * buffer = nullptr ;
BITMAPV5HEADER bi ;
ZeroMemory ( & bi , sizeof ( bi ) ) ;
bi . bV5Size = sizeof ( bi ) ;
bi . bV5Width = texture_size . width ;
bi . bV5Height = - texture_size . height ;
bi . bV5Planes = 1 ;
bi . bV5BitCount = 32 ;
bi . bV5Compression = BI_BITFIELDS ;
bi . bV5RedMask = 0x00ff0000 ;
bi . bV5GreenMask = 0x0000ff00 ;
bi . bV5BlueMask = 0x000000ff ;
bi . bV5AlphaMask = 0xff000000 ;
HDC dc = GetDC ( nullptr ) ;
HBITMAP bitmap = CreateDIBSection ( dc , reinterpret_cast < BITMAPINFO * > ( & bi ) , DIB_RGB_COLORS , reinterpret_cast < void * * > ( & buffer ) , nullptr , 0 ) ;
HBITMAP mask = CreateBitmap ( texture_size . width , texture_size . height , 1 , 1 , nullptr ) ;
bool fully_transparent = true ;
2020-03-09 15:56:48 +00:00
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 ;
2024-02-28 17:38:15 +00:00
if ( atlas_rect . has_area ( ) ) {
2020-03-09 15:56:48 +00:00
column_index = MIN ( column_index , atlas_rect . size . width - 1 ) ;
row_index = MIN ( row_index , atlas_rect . size . height - 1 ) ;
}
2022-08-30 15:50:56 +00:00
const Color & c = image - > get_pixel ( column_index , row_index ) ;
fully_transparent = fully_transparent & & ( c . a = = 0.f ) ;
2020-03-09 15:56:48 +00:00
2022-08-30 15:50:56 +00:00
* ( buffer + index ) = c . to_argb32 ( ) ;
2020-03-09 15:56:48 +00:00
}
2021-09-10 13:44:44 +00:00
// Finally, create the icon.
if ( cursors [ p_shape ] ) {
2020-03-09 15:56:48 +00:00
DestroyIcon ( cursors [ p_shape ] ) ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
2022-08-30 15:50:56 +00:00
if ( fully_transparent ) {
cursors [ p_shape ] = nullptr ;
} else {
ICONINFO iconinfo ;
iconinfo . fIcon = FALSE ;
iconinfo . xHotspot = p_hotspot . x ;
iconinfo . yHotspot = p_hotspot . y ;
iconinfo . hbmMask = mask ;
iconinfo . hbmColor = bitmap ;
cursors [ p_shape ] = CreateIconIndirect ( & iconinfo ) ;
}
2020-03-09 15:56:48 +00:00
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 ] ) ;
}
}
2022-08-30 15:50:56 +00:00
DeleteObject ( mask ) ;
2020-03-09 15:56:48 +00:00
DeleteObject ( bitmap ) ;
2022-08-30 15:50:56 +00:00
ReleaseDC ( nullptr , dc ) ;
2020-03-09 15:56:48 +00:00
} else {
2021-09-10 13:44:44 +00:00
// Reset to default system cursor.
2020-03-09 15:56:48 +00:00
if ( cursors [ p_shape ] ) {
DestroyIcon ( cursors [ p_shape ] ) ;
}
2022-08-30 15:50:56 +00:00
cursors [ p_shape ] = nullptr ;
2020-03-09 15:56:48 +00:00
2023-03-06 21:00:34 +00:00
cursors_cache . erase ( p_shape ) ;
2020-03-09 15:56:48 +00:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
}
}
2020-07-08 23:02:38 +00:00
bool DisplayServerWindows : : get_swap_cancel_ok ( ) {
2020-03-09 15:56:48 +00:00
return true ;
}
void DisplayServerWindows : : enable_for_stealing_focus ( OS : : ProcessID pid ) {
_THREAD_SAFE_METHOD_
AllowSetForegroundWindow ( pid ) ;
}
2024-02-19 00:56:32 +00:00
static HRESULT CALLBACK win32_task_dialog_callback ( HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam , LONG_PTR lpRefData ) {
if ( msg = = TDN_CREATED ) {
// To match the input text dialog.
SendMessageW ( hwnd , WM_SETICON , ICON_BIG , 0 ) ;
SendMessageW ( hwnd , WM_SETICON , ICON_SMALL , 0 ) ;
}
return 0 ;
}
Error DisplayServerWindows : : dialog_show ( String p_title , String p_description , Vector < String > p_buttons , const Callable & p_callback ) {
_THREAD_SAFE_METHOD_
TASKDIALOGCONFIG config ;
ZeroMemory ( & config , sizeof ( TASKDIALOGCONFIG ) ) ;
config . cbSize = sizeof ( TASKDIALOGCONFIG ) ;
Char16String title = p_title . utf16 ( ) ;
Char16String message = p_description . utf16 ( ) ;
2024-04-15 13:18:34 +00:00
LocalVector < Char16String > buttons ;
2024-02-19 00:56:32 +00:00
for ( String s : p_buttons ) {
buttons . push_back ( s . utf16 ( ) ) ;
}
config . pszWindowTitle = ( LPCWSTR ) ( title . get_data ( ) ) ;
config . pszContent = ( LPCWSTR ) ( message . get_data ( ) ) ;
2024-04-15 13:18:34 +00:00
const int button_count = MIN ( ( int ) buttons . size ( ) , 8 ) ;
2024-02-19 00:56:32 +00:00
config . cButtons = button_count ;
// No dynamic stack array size :(
TASKDIALOG_BUTTON * tbuttons = button_count ! = 0 ? ( TASKDIALOG_BUTTON * ) alloca ( sizeof ( TASKDIALOG_BUTTON ) * button_count ) : nullptr ;
if ( tbuttons ) {
for ( int i = 0 ; i < button_count ; i + + ) {
tbuttons [ i ] . nButtonID = i ;
tbuttons [ i ] . pszButtonText = ( LPCWSTR ) ( buttons [ i ] . get_data ( ) ) ;
}
}
config . pButtons = tbuttons ;
config . pfCallback = win32_task_dialog_callback ;
2024-03-05 18:16:15 +00:00
Error result = FAILED ;
2024-02-19 00:56:32 +00:00
HMODULE comctl = LoadLibraryW ( L " comctl32.dll " ) ;
if ( comctl ) {
typedef HRESULT ( WINAPI * TaskDialogIndirectPtr ) ( const TASKDIALOGCONFIG * pTaskConfig , int * pnButton , int * pnRadioButton , BOOL * pfVerificationFlagChecked ) ;
TaskDialogIndirectPtr task_dialog_indirect = ( TaskDialogIndirectPtr ) GetProcAddress ( comctl , " TaskDialogIndirect " ) ;
2024-03-05 18:16:15 +00:00
int button_pressed ;
2024-02-19 00:56:32 +00:00
2024-03-05 18:16:15 +00:00
if ( task_dialog_indirect & & SUCCEEDED ( task_dialog_indirect ( & config , & button_pressed , nullptr , nullptr ) ) ) {
2024-04-27 09:56:39 +00:00
if ( p_callback . is_valid ( ) ) {
2024-02-19 00:56:32 +00:00
Variant button = button_pressed ;
const Variant * args [ 1 ] = { & button } ;
Variant ret ;
Callable : : CallError ce ;
p_callback . callp ( args , 1 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute dialog callback: %s. " , Variant : : get_callable_error_text ( p_callback , args , 1 , ce ) ) ) ;
}
}
2024-03-05 18:16:15 +00:00
result = OK ;
2024-02-19 00:56:32 +00:00
}
FreeLibrary ( comctl ) ;
2024-03-05 18:16:15 +00:00
} else {
ERR_PRINT ( " Unable to create native dialog. " ) ;
2024-02-19 00:56:32 +00:00
}
2024-03-05 18:16:15 +00:00
return result ;
2024-02-19 00:56:32 +00:00
}
struct Win32InputTextDialogInit {
const char16_t * title ;
const char16_t * description ;
const char16_t * partial ;
const Callable & callback ;
} ;
2024-03-06 23:15:01 +00:00
static int scale_with_dpi ( int p_pos , int p_dpi ) {
2024-02-19 00:56:32 +00:00
return IsProcessDPIAware ( ) ? ( p_pos * p_dpi / 96 ) : p_pos ;
}
static INT_PTR input_text_dialog_init ( HWND hWnd , UINT code , WPARAM wParam , LPARAM lParam ) {
Win32InputTextDialogInit init = * ( Win32InputTextDialogInit * ) lParam ;
SetWindowLongPtrW ( hWnd , GWLP_USERDATA , ( LONG_PTR ) & init . callback ) ; // Set dialog callback.
SetWindowTextW ( hWnd , ( LPCWSTR ) init . title ) ;
const int dpi = DisplayServerWindows : : get_singleton ( ) - > screen_get_dpi ( ) ;
const int margin = scale_with_dpi ( 7 , dpi ) ;
const SIZE dlg_size = { scale_with_dpi ( 300 , dpi ) , scale_with_dpi ( 50 , dpi ) } ;
int str_len = lstrlenW ( ( LPCWSTR ) init . description ) ;
SIZE str_size = { dlg_size . cx , 0 } ;
if ( str_len > 0 ) {
HDC hdc = GetDC ( nullptr ) ;
RECT trect = { margin , margin , margin + dlg_size . cx , margin + dlg_size . cy } ;
SelectObject ( hdc , ( HFONT ) SendMessageW ( hWnd , WM_GETFONT , 0 , 0 ) ) ;
// `+ margin` adds some space between the static text and the edit field.
// Don't scale this with DPI because DPI is already handled by DrawText.
str_size . cy = DrawTextW ( hdc , ( LPCWSTR ) init . description , str_len , & trect , DT_LEFT | DT_WORDBREAK | DT_CALCRECT ) + margin ;
ReleaseDC ( nullptr , hdc ) ;
}
RECT crect , wrect ;
GetClientRect ( hWnd , & crect ) ;
GetWindowRect ( hWnd , & wrect ) ;
int sw = GetSystemMetrics ( SM_CXSCREEN ) ;
int sh = GetSystemMetrics ( SM_CYSCREEN ) ;
int new_width = dlg_size . cx + margin * 2 + wrect . right - wrect . left - crect . right ;
int new_height = dlg_size . cy + margin * 2 + wrect . bottom - wrect . top - crect . bottom + str_size . cy ;
MoveWindow ( hWnd , ( sw - new_width ) / 2 , ( sh - new_height ) / 2 , new_width , new_height , true ) ;
HWND ok_button = GetDlgItem ( hWnd , 1 ) ;
MoveWindow ( ok_button ,
dlg_size . cx + margin - scale_with_dpi ( 65 , dpi ) ,
dlg_size . cy + str_size . cy + margin - scale_with_dpi ( 20 , dpi ) ,
scale_with_dpi ( 65 , dpi ) , scale_with_dpi ( 20 , dpi ) , true ) ;
HWND description = GetDlgItem ( hWnd , 3 ) ;
MoveWindow ( description , margin , margin , dlg_size . cx , str_size . cy , true ) ;
SetWindowTextW ( description , ( LPCWSTR ) init . description ) ;
HWND text_edit = GetDlgItem ( hWnd , 2 ) ;
MoveWindow ( text_edit , margin , str_size . cy + margin , dlg_size . cx , scale_with_dpi ( 20 , dpi ) , true ) ;
SetWindowTextW ( text_edit , ( LPCWSTR ) init . partial ) ;
return TRUE ;
}
static INT_PTR input_text_dialog_cmd_proc ( HWND hWnd , UINT code , WPARAM wParam , LPARAM lParam ) {
if ( LOWORD ( wParam ) = = 1 ) {
HWND text_edit = GetDlgItem ( hWnd , 2 ) ;
ERR_FAIL_NULL_V ( text_edit , false ) ;
Char16String text ;
text . resize ( GetWindowTextLengthW ( text_edit ) + 1 ) ;
GetWindowTextW ( text_edit , ( LPWSTR ) text . get_data ( ) , text . size ( ) ) ;
const Callable * callback = ( const Callable * ) GetWindowLongPtrW ( hWnd , GWLP_USERDATA ) ;
if ( callback & & callback - > is_valid ( ) ) {
Variant v_result = String ( ( const wchar_t * ) text . get_data ( ) ) ;
Variant ret ;
Callable : : CallError ce ;
const Variant * args [ 1 ] = { & v_result } ;
callback - > callp ( args , 1 , ret , ce ) ;
if ( ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( vformat ( " Failed to execute input dialog callback: %s. " , Variant : : get_callable_error_text ( * callback , args , 1 , ce ) ) ) ;
}
}
return EndDialog ( hWnd , 0 ) ;
}
return false ;
}
static INT_PTR CALLBACK input_text_dialog_proc ( HWND hWnd , UINT code , WPARAM wParam , LPARAM lParam ) {
switch ( code ) {
case WM_INITDIALOG :
return input_text_dialog_init ( hWnd , code , wParam , lParam ) ;
case WM_COMMAND :
return input_text_dialog_cmd_proc ( hWnd , code , wParam , lParam ) ;
default :
return FALSE ;
}
}
Error DisplayServerWindows : : dialog_input_text ( String p_title , String p_description , String p_partial , const Callable & p_callback ) {
# pragma pack(push, 1)
// NOTE: Use default/placeholder coordinates here. Windows uses its own coordinate system
// specifically for dialogs which relies on font sizes instead of pixels.
const struct {
WORD dlgVer ; // must be 1
WORD signature ; // must be 0xFFFF
DWORD helpID ;
DWORD exStyle ;
DWORD style ;
WORD cDlgItems ;
short x ;
short y ;
short cx ;
short cy ;
WCHAR menu [ 1 ] ; // must be 0
WCHAR windowClass [ 7 ] ; // must be "#32770" -- the default window class for dialogs
WCHAR title [ 1 ] ; // must be 0
WORD pointsize ;
WORD weight ;
BYTE italic ;
BYTE charset ;
WCHAR font [ 13 ] ; // must be "MS Shell Dlg"
} template_base = {
1 , 0xFFFF , 0 , 0 ,
DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU ,
3 , 0 , 0 , 20 , 20 , L " " , L " #32770 " , L " " , 8 , FW_NORMAL , 0 , DEFAULT_CHARSET , L " MS Shell Dlg "
} ;
const struct {
DWORD helpID ;
DWORD exStyle ;
DWORD style ;
short x ;
short y ;
short cx ;
short cy ;
DWORD id ;
WCHAR windowClass [ 7 ] ; // must be "Button"
WCHAR title [ 3 ] ; // must be "OK"
WORD extraCount ;
} ok_button = {
0 , 0 , WS_VISIBLE | BS_DEFPUSHBUTTON , 0 , 0 , 50 , 14 , 1 , WC_BUTTONW , L " OK " , 0
} ;
const struct {
DWORD helpID ;
DWORD exStyle ;
DWORD style ;
short x ;
short y ;
short cx ;
short cy ;
DWORD id ;
WCHAR windowClass [ 5 ] ; // must be "Edit"
WCHAR title [ 1 ] ; // must be 0
WORD extraCount ;
} text_field = {
0 , 0 , WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL , 0 , 0 , 250 , 14 , 2 , WC_EDITW , L " " , 0
} ;
const struct {
DWORD helpID ;
DWORD exStyle ;
DWORD style ;
short x ;
short y ;
short cx ;
short cy ;
DWORD id ;
WCHAR windowClass [ 7 ] ; // must be "Static"
WCHAR title [ 1 ] ; // must be 0
WORD extraCount ;
} static_text = {
0 , 0 , WS_VISIBLE , 0 , 0 , 250 , 14 , 3 , WC_STATICW , L " " , 0
} ;
# pragma pack(pop)
// Dialog template
const size_t data_size = sizeof ( template_base ) + ( sizeof ( template_base ) % 4 ) +
sizeof ( ok_button ) + ( sizeof ( ok_button ) % 4 ) +
sizeof ( text_field ) + ( sizeof ( text_field ) % 4 ) +
sizeof ( static_text ) + ( sizeof ( static_text ) % 4 ) ;
void * data_template = memalloc ( data_size ) ;
ERR_FAIL_NULL_V_MSG ( data_template , FAILED , " Unable to allocate memory for the dialog template. " ) ;
ZeroMemory ( data_template , data_size ) ;
char * current_block = ( char * ) data_template ;
CopyMemory ( current_block , & template_base , sizeof ( template_base ) ) ;
current_block + = sizeof ( template_base ) + ( sizeof ( template_base ) % 4 ) ;
CopyMemory ( current_block , & ok_button , sizeof ( ok_button ) ) ;
current_block + = sizeof ( ok_button ) + ( sizeof ( ok_button ) % 4 ) ;
CopyMemory ( current_block , & text_field , sizeof ( text_field ) ) ;
current_block + = sizeof ( text_field ) + ( sizeof ( text_field ) % 4 ) ;
CopyMemory ( current_block , & static_text , sizeof ( static_text ) ) ;
Char16String title16 = p_title . utf16 ( ) ;
Char16String description16 = p_description . utf16 ( ) ;
Char16String partial16 = p_partial . utf16 ( ) ;
Win32InputTextDialogInit init = {
title16 . get_data ( ) , description16 . get_data ( ) , partial16 . get_data ( ) , p_callback
} ;
// No modal dialogs for specific windows? Assume main window here.
INT_PTR ret = DialogBoxIndirectParamW ( hInstance , ( LPDLGTEMPLATEW ) data_template , nullptr , ( DLGPROC ) input_text_dialog_proc , ( LPARAM ) ( & init ) ) ;
Error result = ret ! = - 1 ? OK : FAILED ;
memfree ( data_template ) ;
if ( result = = FAILED ) {
ERR_PRINT ( " Unable to create native dialog. " ) ;
}
return result ;
}
2020-06-11 09:27:07 +00:00
int DisplayServerWindows : : keyboard_get_layout_count ( ) const {
2021-04-29 09:47:24 +00:00
return GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 09:27:07 +00:00
}
2020-03-09 15:56:48 +00:00
2020-06-11 09:27:07 +00:00
int DisplayServerWindows : : keyboard_get_current_layout ( ) const {
HKL cur_layout = GetKeyboardLayout ( 0 ) ;
2021-04-29 09:47:24 +00:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 09:27:07 +00:00
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 DisplayServerWindows : : keyboard_set_current_layout ( int p_index ) {
2021-04-29 09:47:24 +00:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 09:27:07 +00:00
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 DisplayServerWindows : : keyboard_get_layout_language ( int p_index ) const {
2021-04-29 09:47:24 +00:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-03-09 15:56:48 +00:00
2020-06-11 09:27:07 +00:00
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
2020-03-09 15:56:48 +00:00
2020-06-11 09:27:07 +00:00
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
2020-03-09 15:56:48 +00:00
2020-07-27 10:43:20 +00:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2020-06-11 09:27:07 +00:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
memfree ( layouts ) ;
2020-07-27 10:43:20 +00:00
return String : : utf16 ( ( const char16_t * ) buf ) . substr ( 0 , 2 ) ;
2020-06-11 09:27:07 +00:00
}
2021-09-17 01:07:59 +00:00
Key DisplayServerWindows : : keyboard_get_keycode_from_physical ( Key p_keycode ) const {
2021-08-13 21:31:57 +00:00
Key modifiers = p_keycode & KeyModifierMask : : MODIFIER_MASK ;
Key keycode_no_mod = ( Key ) ( p_keycode & KeyModifierMask : : CODE_MASK ) ;
2021-09-17 01:07:59 +00:00
2021-08-13 21:31:57 +00:00
if ( keycode_no_mod = = Key : : PRINT | |
keycode_no_mod = = Key : : KP_ADD | |
keycode_no_mod = = Key : : KP_5 | |
( keycode_no_mod > = Key : : KEY_0 & & keycode_no_mod < = Key : : KEY_9 ) ) {
2021-09-17 01:07:59 +00:00
return p_keycode ;
}
unsigned int scancode = KeyMappingWindows : : get_scancode ( keycode_no_mod ) ;
if ( scancode = = 0 ) {
return p_keycode ;
}
HKL current_layout = GetKeyboardLayout ( 0 ) ;
UINT vk = MapVirtualKeyEx ( scancode , MAPVK_VSC_TO_VK , current_layout ) ;
if ( vk = = 0 ) {
return p_keycode ;
}
UINT char_code = MapVirtualKeyEx ( vk , MAPVK_VK_TO_CHAR , current_layout ) & 0x7FFF ;
// Unlike a similar Linux/BSD check which matches full Latin-1 range,
// we limit these to ASCII to fix some layouts, including Arabic ones
if ( char_code > = 32 & & char_code < = 127 ) {
// Godot uses 'braces' instead of 'brackets'
2021-08-13 21:31:57 +00:00
if ( char_code = = ( unsigned int ) Key : : BRACKETLEFT | | char_code = = ( unsigned int ) Key : : BRACKETRIGHT ) {
2021-09-17 01:07:59 +00:00
char_code + = 32 ;
}
2021-08-13 21:31:57 +00:00
return ( Key ) ( char_code | ( unsigned int ) modifiers ) ;
2021-09-17 01:07:59 +00:00
}
return ( Key ) ( KeyMappingWindows : : get_keysym ( vk ) | modifiers ) ;
}
2023-06-08 06:26:48 +00:00
Key DisplayServerWindows : : keyboard_get_label_from_physical ( Key p_keycode ) const {
Key modifiers = p_keycode & KeyModifierMask : : MODIFIER_MASK ;
Key keycode_no_mod = ( Key ) ( p_keycode & KeyModifierMask : : CODE_MASK ) ;
if ( keycode_no_mod = = Key : : PRINT | |
keycode_no_mod = = Key : : KP_ADD | |
keycode_no_mod = = Key : : KP_5 | |
( keycode_no_mod > = Key : : KEY_0 & & keycode_no_mod < = Key : : KEY_9 ) ) {
return p_keycode ;
}
unsigned int scancode = KeyMappingWindows : : get_scancode ( keycode_no_mod ) ;
if ( scancode = = 0 ) {
return p_keycode ;
}
Key keycode = KeyMappingWindows : : get_keysym ( MapVirtualKey ( scancode , MAPVK_VSC_TO_VK ) ) ;
HKL current_layout = GetKeyboardLayout ( 0 ) ;
static BYTE keyboard_state [ 256 ] ;
memset ( keyboard_state , 0 , 256 ) ;
wchar_t chars [ 256 ] = { } ;
UINT extended_code = MapVirtualKey ( scancode , MAPVK_VSC_TO_VK_EX ) ;
if ( ToUnicodeEx ( extended_code , scancode , keyboard_state , chars , 255 , 4 , current_layout ) > 0 ) {
String keysym = String : : utf16 ( ( char16_t * ) chars , 255 ) ;
if ( ! keysym . is_empty ( ) ) {
return fix_key_label ( keysym [ 0 ] , keycode ) | modifiers ;
}
}
return p_keycode ;
}
2020-06-11 09:27:07 +00:00
String _get_full_layout_name_from_registry ( HKL p_layout ) {
String id = " SYSTEM \\ CurrentControlSet \\ Control \\ Keyboard Layouts \\ " + String : : num_int64 ( ( int64_t ) p_layout , 16 , false ) . lpad ( 8 , " 0 " ) ;
String ret ;
HKEY hkey ;
2020-07-27 10:43:20 +00:00
WCHAR layout_text [ 1024 ] ;
memset ( layout_text , 0 , 1024 * sizeof ( WCHAR ) ) ;
2020-06-11 09:27:07 +00:00
2020-07-27 10:43:20 +00:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) ( id . utf16 ( ) . get_data ( ) ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
2020-06-11 09:27:07 +00:00
return ret ;
2020-03-09 15:56:48 +00:00
}
2020-06-11 09:27:07 +00:00
DWORD buffer = 1024 ;
DWORD vtype = REG_SZ ;
2021-04-29 09:47:24 +00:00
if ( RegQueryValueExW ( hkey , L " Layout Text " , nullptr , & vtype , ( LPBYTE ) layout_text , & buffer ) = = ERROR_SUCCESS ) {
2020-07-27 10:43:20 +00:00
ret = String : : utf16 ( ( const char16_t * ) layout_text ) ;
2020-03-09 15:56:48 +00:00
}
2020-06-11 09:27:07 +00:00
RegCloseKey ( hkey ) ;
return ret ;
}
String DisplayServerWindows : : keyboard_get_layout_name ( int p_index ) const {
2021-04-29 09:47:24 +00:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 09:27:07 +00:00
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
2020-03-09 15:56:48 +00:00
2020-06-11 09:27:07 +00:00
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).
2021-12-09 09:42:46 +00:00
if ( ret . is_empty ( ) ) {
2020-07-27 10:43:20 +00:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2020-06-11 09:27:07 +00:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
2020-07-27 10:43:20 +00:00
WCHAR name [ 1024 ] ;
memset ( name , 0 , 1024 * sizeof ( WCHAR ) ) ;
2020-06-11 09:27:07 +00:00
GetLocaleInfoEx ( buf , LOCALE_SLOCALIZEDDISPLAYNAME , ( LPWSTR ) & name , 1024 ) ;
2020-07-27 10:43:20 +00:00
ret = String : : utf16 ( ( const char16_t * ) name ) ;
2020-03-09 15:56:48 +00:00
}
2020-06-11 09:27:07 +00:00
memfree ( layouts ) ;
2020-03-09 15:56:48 +00:00
2020-06-11 09:27:07 +00:00
return ret ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : process_events ( ) {
2024-04-09 09:47:06 +00:00
_THREAD_SAFE_LOCK_
2020-03-09 15:56:48 +00:00
MSG msg ;
if ( ! drop_events ) {
joypad - > process_joypads ( ) ;
}
2020-04-01 23:20:12 +00:00
while ( PeekMessageW ( & msg , nullptr , 0 , 0 , PM_REMOVE ) ) {
2020-03-09 15:56:48 +00:00
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
if ( ! drop_events ) {
_process_key_events ( ) ;
2024-04-09 09:47:06 +00:00
_THREAD_SAFE_UNLOCK_
2021-08-12 22:38:41 +00:00
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2024-04-09 09:47:06 +00:00
} else {
_THREAD_SAFE_UNLOCK_
2020-03-09 15:56:48 +00:00
}
}
void DisplayServerWindows : : force_process_and_drop_events ( ) {
_THREAD_SAFE_METHOD_
drop_events = true ;
process_events ( ) ;
drop_events = false ;
}
void DisplayServerWindows : : release_rendering_thread ( ) {
2024-04-09 09:47:06 +00:00
# if defined(GLES3_ENABLED)
if ( gl_manager_angle ) {
gl_manager_angle - > release_current ( ) ;
}
if ( gl_manager_native ) {
gl_manager_native - > release_current ( ) ;
}
# endif
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : swap_buffers ( ) {
2021-10-26 15:18:39 +00:00
# if defined(GLES3_ENABLED)
2021-11-12 12:49:49 +00:00
if ( gl_manager_angle ) {
gl_manager_angle - > swap_buffers ( ) ;
}
if ( gl_manager_native ) {
gl_manager_native - > swap_buffers ( ) ;
2022-05-08 23:25:49 +00:00
}
2020-11-18 18:11:30 +00:00
# endif
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : set_native_icon ( const String & p_filename ) {
_THREAD_SAFE_METHOD_
2022-03-23 09:08:58 +00:00
Ref < FileAccess > f = FileAccess : : open ( p_filename , FileAccess : : READ ) ;
ERR_FAIL_COND_MSG ( f . is_null ( ) , " Cannot open file with icon ' " + p_filename + " '. " ) ;
2020-03-09 15:56:48 +00:00
ICONDIR * icon_dir = ( ICONDIR * ) memalloc ( sizeof ( ICONDIR ) ) ;
int pos = 0 ;
icon_dir - > idReserved = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
icon_dir - > idType = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
ERR_FAIL_COND_MSG ( icon_dir - > idType ! = 1 , " Invalid icon file format! " ) ;
icon_dir - > idCount = f - > get_32 ( ) ;
pos + = sizeof ( WORD ) ;
f - > seek ( pos ) ;
2022-11-13 23:47:14 +00:00
icon_dir = ( ICONDIR * ) memrealloc ( icon_dir , sizeof ( ICONDIR ) - sizeof ( ICONDIRENTRY ) + icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ;
2020-03-09 15:56:48 +00:00
f - > get_buffer ( ( uint8_t * ) & icon_dir - > idEntries [ 0 ] , icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ;
2021-09-10 13:44:44 +00:00
int small_icon_index = - 1 ; // Select 16x16 with largest color count.
2020-03-09 15:56:48 +00:00
int small_icon_cc = 0 ;
2021-09-10 13:44:44 +00:00
int big_icon_index = - 1 ; // Select largest.
2020-03-09 15:56:48 +00:00
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 ;
}
2021-09-10 13:44:44 +00:00
// Read the big icon.
2020-03-09 15:56:48 +00:00
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 ) ;
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL_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 ( ) ) + " . " ) ;
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
// Read the small icon.
2020-03-09 15:56:48 +00:00
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 ) ;
2023-09-09 15:40:07 +00:00
ERR_FAIL_NULL_MSG ( icon_small , " Could not create 16x16 @ " + itos ( small_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
// Online tradition says to be sure last error is cleared and set the small icon first.
2020-03-09 15:56:48 +00:00
int err = 0 ;
SetLastError ( err ) ;
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) icon_small ) ;
err = GetLastError ( ) ;
ERR_FAIL_COND_MSG ( err , " Error setting ICON_SMALL: " + format_error_message ( err ) + " . " ) ;
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) icon_big ) ;
err = GetLastError ( ) ;
ERR_FAIL_COND_MSG ( err , " Error setting ICON_BIG: " + format_error_message ( err ) + " . " ) ;
memdelete ( icon_dir ) ;
}
2020-05-14 12:29:06 +00:00
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : set_icon ( const Ref < Image > & p_icon ) {
_THREAD_SAFE_METHOD_
2023-06-19 13:18:39 +00:00
if ( p_icon . is_valid ( ) ) {
ERR_FAIL_COND ( p_icon - > get_width ( ) < = 0 | | p_icon - > get_height ( ) < = 0 ) ;
Ref < Image > img = p_icon ;
if ( img ! = icon ) {
img = img - > duplicate ( ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2022-05-10 08:12:04 +00:00
}
2023-06-19 13:18:39 +00:00
int w = img - > get_width ( ) ;
int h = img - > 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 ] ;
const uint8_t * r = img - > get_data ( ) . ptr ( ) ;
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 ] ;
}
2020-03-09 15:56:48 +00:00
}
2023-06-19 13:18:39 +00:00
HICON hicon = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
2023-09-09 15:46:44 +00:00
ERR_FAIL_NULL ( hicon ) ;
2023-06-19 13:18:39 +00:00
icon = img ;
2020-03-09 15:56:48 +00:00
2023-06-19 13:18:39 +00:00
// Set the icon for the window.
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) hicon ) ;
2020-03-09 15:56:48 +00:00
2023-06-19 13:18:39 +00:00
// Set the icon in the task manager (should we do this?).
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) hicon ) ;
} else {
icon = Ref < Image > ( ) ;
2023-07-20 20:51:04 +00:00
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_SMALL , 0 ) ;
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_BIG , 0 ) ;
2023-06-19 13:18:39 +00:00
}
2020-03-09 15:56:48 +00:00
}
2024-03-16 21:14:30 +00:00
DisplayServer : : IndicatorID DisplayServerWindows : : create_status_indicator ( const Ref < Texture2D > & p_icon , const String & p_tooltip , const Callable & p_callback ) {
2023-08-03 09:45:56 +00:00
HICON hicon = nullptr ;
if ( p_icon . is_valid ( ) & & p_icon - > get_width ( ) > 0 & & p_icon - > get_height ( ) > 0 ) {
2024-03-16 21:14:30 +00:00
Ref < Image > img = p_icon - > get_image ( ) ;
img = img - > duplicate ( ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2023-08-03 09:45:56 +00:00
int w = img - > get_width ( ) ;
int h = img - > 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 ] ;
const uint8_t * r = img - > get_data ( ) . ptr ( ) ;
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 = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
}
IndicatorData idat ;
idat . callback = p_callback ;
NOTIFYICONDATAW ndat ;
ZeroMemory ( & ndat , sizeof ( NOTIFYICONDATAW ) ) ;
ndat . cbSize = sizeof ( NOTIFYICONDATAW ) ;
ndat . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
ndat . uID = indicator_id_counter ;
ndat . uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE ;
ndat . uCallbackMessage = WM_INDICATOR_CALLBACK_MESSAGE ;
ndat . hIcon = hicon ;
memcpy ( ndat . szTip , p_tooltip . utf16 ( ) . ptr ( ) , MIN ( p_tooltip . utf16 ( ) . length ( ) , 127 ) * sizeof ( WCHAR ) ) ;
ndat . uVersion = NOTIFYICON_VERSION ;
Shell_NotifyIconW ( NIM_ADD , & ndat ) ;
Shell_NotifyIconW ( NIM_SETVERSION , & ndat ) ;
IndicatorID iid = indicator_id_counter + + ;
indicators [ iid ] = idat ;
return iid ;
}
2024-03-16 21:14:30 +00:00
void DisplayServerWindows : : status_indicator_set_icon ( IndicatorID p_id , const Ref < Texture2D > & p_icon ) {
2023-08-03 09:45:56 +00:00
ERR_FAIL_COND ( ! indicators . has ( p_id ) ) ;
HICON hicon = nullptr ;
if ( p_icon . is_valid ( ) & & p_icon - > get_width ( ) > 0 & & p_icon - > get_height ( ) > 0 ) {
2024-03-16 21:14:30 +00:00
Ref < Image > img = p_icon - > get_image ( ) ;
img = img - > duplicate ( ) ;
img - > convert ( Image : : FORMAT_RGBA8 ) ;
2023-08-03 09:45:56 +00:00
int w = img - > get_width ( ) ;
int h = img - > 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 ] ;
const uint8_t * r = img - > get_data ( ) . ptr ( ) ;
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 = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
}
NOTIFYICONDATAW ndat ;
ZeroMemory ( & ndat , sizeof ( NOTIFYICONDATAW ) ) ;
ndat . cbSize = sizeof ( NOTIFYICONDATAW ) ;
ndat . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
ndat . uID = p_id ;
ndat . uFlags = NIF_ICON ;
ndat . hIcon = hicon ;
ndat . uVersion = NOTIFYICON_VERSION ;
Shell_NotifyIconW ( NIM_MODIFY , & ndat ) ;
}
void DisplayServerWindows : : status_indicator_set_tooltip ( IndicatorID p_id , const String & p_tooltip ) {
ERR_FAIL_COND ( ! indicators . has ( p_id ) ) ;
NOTIFYICONDATAW ndat ;
ZeroMemory ( & ndat , sizeof ( NOTIFYICONDATAW ) ) ;
ndat . cbSize = sizeof ( NOTIFYICONDATAW ) ;
ndat . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
ndat . uID = p_id ;
ndat . uFlags = NIF_TIP ;
memcpy ( ndat . szTip , p_tooltip . utf16 ( ) . ptr ( ) , MIN ( p_tooltip . utf16 ( ) . length ( ) , 127 ) * sizeof ( WCHAR ) ) ;
ndat . uVersion = NOTIFYICON_VERSION ;
Shell_NotifyIconW ( NIM_MODIFY , & ndat ) ;
}
2024-03-16 21:14:30 +00:00
void DisplayServerWindows : : status_indicator_set_menu ( IndicatorID p_id , const RID & p_menu_rid ) {
ERR_FAIL_COND ( ! indicators . has ( p_id ) ) ;
indicators [ p_id ] . menu_rid = p_menu_rid ;
}
2023-08-03 09:45:56 +00:00
void DisplayServerWindows : : status_indicator_set_callback ( IndicatorID p_id , const Callable & p_callback ) {
ERR_FAIL_COND ( ! indicators . has ( p_id ) ) ;
indicators [ p_id ] . callback = p_callback ;
}
2024-03-08 07:24:58 +00:00
Rect2 DisplayServerWindows : : status_indicator_get_rect ( IndicatorID p_id ) const {
ERR_FAIL_COND_V ( ! indicators . has ( p_id ) , Rect2 ( ) ) ;
NOTIFYICONIDENTIFIER nid ;
ZeroMemory ( & nid , sizeof ( NOTIFYICONIDENTIFIER ) ) ;
nid . cbSize = sizeof ( NOTIFYICONIDENTIFIER ) ;
nid . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
nid . uID = p_id ;
nid . guidItem = GUID_NULL ;
RECT rect ;
if ( Shell_NotifyIconGetRect ( & nid , & rect ) ! = S_OK ) {
return Rect2 ( ) ;
}
Rect2 ind_rect = Rect2 ( Point2 ( rect . left , rect . top ) - _get_screens_origin ( ) , Size2 ( rect . right - rect . left , rect . bottom - rect . top ) ) ;
for ( int i = 0 ; i < get_screen_count ( ) ; i + + ) {
Rect2 screen_rect = Rect2 ( screen_get_position ( i ) , screen_get_size ( i ) ) ;
if ( screen_rect . encloses ( ind_rect ) ) {
return ind_rect ;
}
}
return Rect2 ( ) ;
}
2023-08-03 09:45:56 +00:00
void DisplayServerWindows : : delete_status_indicator ( IndicatorID p_id ) {
ERR_FAIL_COND ( ! indicators . has ( p_id ) ) ;
NOTIFYICONDATAW ndat ;
ZeroMemory ( & ndat , sizeof ( NOTIFYICONDATAW ) ) ;
ndat . cbSize = sizeof ( NOTIFYICONDATAW ) ;
ndat . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
ndat . uID = p_id ;
ndat . uVersion = NOTIFYICON_VERSION ;
Shell_NotifyIconW ( NIM_DELETE , & ndat ) ;
indicators . erase ( p_id ) ;
}
2021-06-19 15:44:59 +00:00
void DisplayServerWindows : : window_set_vsync_mode ( DisplayServer : : VSyncMode p_vsync_mode , WindowID p_window ) {
_THREAD_SAFE_METHOD_
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
rendering_context - > window_set_vsync_mode ( p_window , p_vsync_mode ) ;
2023-01-09 15:56:16 +00:00
}
# endif
2022-11-15 22:14:21 +00:00
# if defined(GLES3_ENABLED)
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
gl_manager_native - > set_use_vsync ( p_window , p_vsync_mode ! = DisplayServer : : VSYNC_DISABLED ) ;
}
if ( gl_manager_angle ) {
gl_manager_angle - > set_use_vsync ( p_vsync_mode ! = DisplayServer : : VSYNC_DISABLED ) ;
2022-11-15 22:14:21 +00:00
}
# endif
2020-03-09 15:56:48 +00:00
}
2020-05-14 12:29:06 +00:00
2021-06-19 15:44:59 +00:00
DisplayServer : : VSyncMode DisplayServerWindows : : window_get_vsync_mode ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
return rendering_context - > window_get_vsync_mode ( p_window ) ;
2023-01-09 15:56:16 +00:00
}
# endif
2022-11-15 22:14:21 +00:00
# if defined(GLES3_ENABLED)
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
return gl_manager_native - > is_using_vsync ( p_window ) ? DisplayServer : : VSYNC_ENABLED : DisplayServer : : VSYNC_DISABLED ;
}
if ( gl_manager_angle ) {
return gl_manager_angle - > is_using_vsync ( ) ? DisplayServer : : VSYNC_ENABLED : DisplayServer : : VSYNC_DISABLED ;
2022-11-15 22:14:21 +00:00
}
# endif
2021-09-26 23:07:10 +00:00
return DisplayServer : : VSYNC_ENABLED ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : set_context ( Context p_context ) {
}
# 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?
2024-02-28 13:25:35 +00:00
# define IsPenEvent(dw) (((dw) & SIGNATURE_MASK) == MI_WP_SIGNATURE)
2021-09-10 13:44:44 +00:00
// This one tells whether the event comes from touchscreen (and not from pen).
2024-02-28 13:25:35 +00:00
# define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw) & 0x80))
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : _touch_event ( WindowID p_window , bool p_pressed , float p_x , float p_y , int idx ) {
2021-09-10 13:44:44 +00:00
if ( touch_state . has ( idx ) = = p_pressed ) {
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
if ( p_pressed ) {
touch_state . insert ( idx , Vector2 ( p_x , p_y ) ) ;
} else {
touch_state . erase ( idx ) ;
}
Ref < InputEventScreenTouch > event ;
2021-06-17 22:03:09 +00:00
event . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
event - > set_index ( idx ) ;
event - > set_window_id ( p_window ) ;
event - > set_pressed ( p_pressed ) ;
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( event ) ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : _drag_event ( WindowID p_window , float p_x , float p_y , int idx ) {
2022-05-13 13:04:37 +00:00
RBMap < int , Vector2 > : : Element * curr = touch_state . find ( idx ) ;
2021-09-10 13:44:44 +00:00
if ( ! curr ) {
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
if ( curr - > get ( ) = = Vector2 ( p_x , p_y ) ) {
2020-03-09 15:56:48 +00:00
return ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
Ref < InputEventScreenDrag > event ;
2021-06-17 22:03:09 +00:00
event . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
event - > set_window_id ( p_window ) ;
event - > set_index ( idx ) ;
event - > set_position ( Vector2 ( p_x , p_y ) ) ;
event - > set_relative ( Vector2 ( p_x , p_y ) - curr - > get ( ) ) ;
2023-10-04 17:20:01 +00:00
event - > set_relative_screen_position ( event - > get_relative ( ) ) ;
2020-03-09 15:56:48 +00:00
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( event ) ;
2020-03-09 15:56:48 +00:00
curr - > get ( ) = Vector2 ( p_x , p_y ) ;
}
void DisplayServerWindows : : _send_window_event ( const WindowData & wd , WindowEvent p_event ) {
2023-07-11 14:18:10 +00:00
if ( wd . event_callback . is_valid ( ) ) {
2020-03-09 15:56:48 +00:00
Variant event = int ( p_event ) ;
2023-07-11 14:18:10 +00:00
wd . event_callback . call ( event ) ;
2020-03-09 15:56:48 +00:00
}
}
void DisplayServerWindows : : _dispatch_input_events ( const Ref < InputEvent > & p_event ) {
2022-04-05 10:40:26 +00:00
static_cast < DisplayServerWindows * > ( get_singleton ( ) ) - > _dispatch_input_event ( p_event ) ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : _dispatch_input_event ( const Ref < InputEvent > & p_event ) {
2020-04-02 14:06:04 +00:00
if ( in_dispatch_input_event ) {
return ;
}
in_dispatch_input_event = true ;
2020-03-09 15:56:48 +00:00
2022-02-24 09:21:23 +00:00
{
2022-04-13 06:58:38 +00:00
List < WindowID > : : Element * E = popup_list . back ( ) ;
2022-02-24 09:21:23 +00:00
if ( E & & Object : : cast_to < InputEventKey > ( * p_event ) ) {
// Redirect keyboard input to active popup.
if ( windows . has ( E - > get ( ) ) ) {
Callable callable = windows [ E - > get ( ) ] . input_event_callback ;
if ( callable . is_valid ( ) ) {
2023-07-11 14:18:10 +00:00
callable . call ( p_event ) ;
2022-02-24 09:21:23 +00:00
}
}
in_dispatch_input_event = false ;
return ;
}
}
2020-03-09 15:56:48 +00:00
Ref < InputEventFromWindow > event_from_window = p_event ;
if ( event_from_window . is_valid ( ) & & event_from_window - > get_window_id ( ) ! = INVALID_WINDOW_ID ) {
2021-09-10 13:44:44 +00:00
// Send to a single window.
2022-02-24 09:21:23 +00:00
if ( windows . has ( event_from_window - > get_window_id ( ) ) ) {
Callable callable = windows [ event_from_window - > get_window_id ( ) ] . input_event_callback ;
if ( callable . is_valid ( ) ) {
2023-07-11 14:18:10 +00:00
callable . call ( p_event ) ;
2022-02-24 09:21:23 +00:00
}
2020-03-09 15:56:48 +00:00
}
} else {
2021-09-10 13:44:44 +00:00
// Send to all windows.
2021-08-09 20:13:42 +00:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
const Callable callable = E . value . input_event_callback ;
2022-02-24 09:21:23 +00:00
if ( callable . is_valid ( ) ) {
2023-07-11 14:18:10 +00:00
callable . call ( p_event ) ;
2020-03-09 15:56:48 +00:00
}
}
}
2020-04-02 14:06:04 +00:00
in_dispatch_input_event = false ;
2020-03-09 15:56:48 +00:00
}
2022-02-24 09:21:23 +00:00
LRESULT CALLBACK MouseProc ( int code , WPARAM wParam , LPARAM lParam ) {
DisplayServerWindows * ds_win = static_cast < DisplayServerWindows * > ( DisplayServer : : get_singleton ( ) ) ;
if ( ds_win ) {
return ds_win - > MouseProc ( code , wParam , lParam ) ;
} else {
return : : CallNextHookEx ( nullptr , code , wParam , lParam ) ;
}
}
DisplayServer : : WindowID DisplayServerWindows : : window_get_active_popup ( ) const {
const List < WindowID > : : Element * E = popup_list . back ( ) ;
if ( E ) {
return E - > get ( ) ;
} else {
return INVALID_WINDOW_ID ;
}
}
void DisplayServerWindows : : window_set_popup_safe_rect ( WindowID p_window , const Rect2i & p_rect ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
wd . parent_safe_rect = p_rect ;
}
Rect2i DisplayServerWindows : : window_get_popup_safe_rect ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Rect2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
return wd . parent_safe_rect ;
}
void DisplayServerWindows : : popup_open ( WindowID p_window ) {
2022-04-05 09:34:27 +00:00
_THREAD_SAFE_METHOD_
2021-04-15 18:22:58 +00:00
bool has_popup_ancestor = false ;
WindowID transient_root = p_window ;
while ( true ) {
WindowID parent = windows [ transient_root ] . transient_parent ;
if ( parent = = INVALID_WINDOW_ID ) {
break ;
} else {
transient_root = parent ;
if ( windows [ parent ] . is_popup ) {
has_popup_ancestor = true ;
break ;
}
}
}
WindowData & wd = windows [ p_window ] ;
if ( wd . is_popup | | has_popup_ancestor ) {
2022-04-05 09:34:27 +00:00
// Find current popup parent, or root popup if new window is not transient.
List < WindowID > : : Element * C = nullptr ;
2022-02-24 09:21:23 +00:00
List < WindowID > : : Element * E = popup_list . back ( ) ;
while ( E ) {
if ( wd . transient_parent ! = E - > get ( ) | | wd . transient_parent = = INVALID_WINDOW_ID ) {
2022-04-05 09:34:27 +00:00
C = E ;
E = E - > prev ( ) ;
2022-02-24 09:21:23 +00:00
} else {
break ;
}
}
2022-04-05 09:34:27 +00:00
if ( C ) {
_send_window_event ( windows [ C - > get ( ) ] , DisplayServerWindows : : WINDOW_EVENT_CLOSE_REQUEST ) ;
}
2022-02-24 09:21:23 +00:00
time_since_popup = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
popup_list . push_back ( p_window ) ;
}
}
void DisplayServerWindows : : popup_close ( WindowID p_window ) {
2022-04-05 09:34:27 +00:00
_THREAD_SAFE_METHOD_
2022-02-24 09:21:23 +00:00
List < WindowID > : : Element * E = popup_list . find ( p_window ) ;
while ( E ) {
List < WindowID > : : Element * F = E - > next ( ) ;
2022-04-05 09:34:27 +00:00
WindowID win_id = E - > get ( ) ;
2022-02-24 09:21:23 +00:00
popup_list . erase ( E ) ;
2022-04-05 09:34:27 +00:00
2022-05-13 12:22:29 +00:00
if ( win_id ! = p_window ) {
// Only request close on related windows, not this window. We are already processing it.
_send_window_event ( windows [ win_id ] , DisplayServerWindows : : WINDOW_EVENT_CLOSE_REQUEST ) ;
}
2022-02-24 09:21:23 +00:00
E = F ;
}
}
LRESULT DisplayServerWindows : : MouseProc ( int code , WPARAM wParam , LPARAM lParam ) {
_THREAD_SAFE_METHOD_
2022-04-05 09:34:27 +00:00
2022-02-24 09:21:23 +00:00
uint64_t delta = OS : : get_singleton ( ) - > get_ticks_msec ( ) - time_since_popup ;
if ( delta > 250 ) {
switch ( wParam ) {
case WM_NCLBUTTONDOWN :
case WM_NCRBUTTONDOWN :
case WM_NCMBUTTONDOWN :
case WM_LBUTTONDOWN :
2022-05-13 12:22:29 +00:00
case WM_RBUTTONDOWN :
2022-02-24 09:21:23 +00:00
case WM_MBUTTONDOWN : {
MOUSEHOOKSTRUCT * ms = ( MOUSEHOOKSTRUCT * ) lParam ;
2023-01-04 22:00:02 +00:00
Point2i pos = Point2i ( ms - > pt . x , ms - > pt . y ) - _get_screens_origin ( ) ;
2022-04-05 09:34:27 +00:00
List < WindowID > : : Element * C = nullptr ;
2022-02-24 09:21:23 +00:00
List < WindowID > : : Element * E = popup_list . back ( ) ;
2022-04-05 09:34:27 +00:00
// Find top popup to close.
2022-02-24 09:21:23 +00:00
while ( E ) {
// Popup window area.
2023-07-14 06:16:06 +00:00
Rect2i win_rect = Rect2i ( window_get_position_with_decorations ( E - > get ( ) ) , window_get_size_with_decorations ( E - > get ( ) ) ) ;
2022-02-24 09:21:23 +00:00
// Area of the parent window, which responsible for opening sub-menu.
Rect2i safe_rect = window_get_popup_safe_rect ( E - > get ( ) ) ;
if ( win_rect . has_point ( pos ) ) {
break ;
} else if ( safe_rect ! = Rect2i ( ) & & safe_rect . has_point ( pos ) ) {
break ;
} else {
2022-04-05 09:34:27 +00:00
C = E ;
E = E - > prev ( ) ;
2022-02-24 09:21:23 +00:00
}
}
2022-04-05 09:34:27 +00:00
if ( C ) {
_send_window_event ( windows [ C - > get ( ) ] , DisplayServerWindows : : WINDOW_EVENT_CLOSE_REQUEST ) ;
2022-05-30 08:06:08 +00:00
return 1 ;
2022-04-05 09:34:27 +00:00
}
2022-02-24 09:21:23 +00:00
} break ;
}
}
return : : CallNextHookEx ( mouse_monitor , code , wParam , lParam ) ;
}
2022-05-08 23:25:49 +00:00
// Handle a single window message received while CreateWindowEx is still on the stack and our data
// structures are not fully initialized.
LRESULT DisplayServerWindows : : _handle_early_window_message ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
switch ( uMsg ) {
case WM_GETMINMAXINFO : {
// We receive this during CreateWindowEx and we haven't initialized the window
// struct, so let Windows figure out the maximized size.
// Silently forward to user/default.
} break ;
case WM_NCCREATE : {
// We tunnel an unowned pointer to our window context (WindowData) through the
// first possible message (WM_NCCREATE) to fix up our window context collection.
CREATESTRUCTW * pCreate = ( CREATESTRUCTW * ) lParam ;
WindowData * pWindowData = reinterpret_cast < WindowData * > ( pCreate - > lpCreateParams ) ;
// Fix this up so we can recognize the remaining messages.
pWindowData - > hWnd = hWnd ;
} break ;
default : {
// Additional messages during window creation should happen after we fixed
// up the data structures on WM_NCCREATE, but this might change in the future,
// so report an error here and then we can implement them.
ERR_PRINT_ONCE ( vformat ( " Unexpected window message 0x%x received for window we cannot recognize in our collection; sequence error. " , uMsg ) ) ;
} break ;
}
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
}
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
// The window procedure for our window class "Engine", used to handle processing of window-related system messages/events.
2021-09-10 13:44:44 +00:00
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
2020-03-09 15:56:48 +00:00
LRESULT DisplayServerWindows : : 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 ) ;
}
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
WindowID window_id = INVALID_WINDOW_ID ;
2020-06-27 16:26:34 +00:00
bool window_created = false ;
2020-03-09 15:56:48 +00:00
2022-05-08 23:25:49 +00:00
// Check whether window exists
// FIXME this is O(n), where n is the set of currently open windows and subwindows
// we should have a secondary map from HWND to WindowID or even WindowData* alias, if we want to eliminate all the map lookups below
2021-08-09 20:13:42 +00:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( E . value . hWnd = = hWnd ) {
window_id = E . key ;
2020-06-27 16:26:34 +00:00
window_created = true ;
2020-03-09 15:56:48 +00:00
break ;
}
}
2022-05-08 23:25:49 +00:00
// WARNING: we get called with events before the window is registered in our collection
// specifically, even the call to CreateWindowEx already calls here while still on the stack,
// so there is no way to store the window handle in our collection before we get here
2020-06-27 16:26:34 +00:00
if ( ! window_created ) {
2022-05-08 23:25:49 +00:00
// don't let code below operate on incompletely initialized window objects or missing window_id
return _handle_early_window_message ( hWnd , uMsg , wParam , lParam ) ;
2020-06-27 16:26:34 +00:00
}
2021-09-10 13:44:44 +00:00
// Process window messages.
switch ( uMsg ) {
2024-03-07 18:42:24 +00:00
case WM_MENUCOMMAND : {
native_menu - > _menu_activate ( HMENU ( lParam ) , ( int ) wParam ) ;
} break ;
2024-01-24 04:44:40 +00:00
case WM_CREATE : {
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
BOOL value = is_dark_mode ( ) ;
: : DwmSetWindowAttribute ( windows [ window_id ] . hWnd , use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
SendMessageW ( windows [ window_id ] . hWnd , WM_PAINT , 0 , 0 ) ;
}
} break ;
2023-09-21 07:09:05 +00:00
case WM_NCPAINT : {
if ( RenderingServer : : get_singleton ( ) & & ( windows [ window_id ] . borderless | | ( windows [ window_id ] . fullscreen & & windows [ window_id ] . multiwindow_fs ) ) ) {
Color color = RenderingServer : : get_singleton ( ) - > get_default_clear_color ( ) ;
HDC hdc = GetWindowDC ( hWnd ) ;
if ( hdc ) {
HPEN pen = CreatePen ( PS_SOLID , 1 , RGB ( color . r * 255.f , color . g * 255.f , color . b * 255.f ) ) ;
if ( pen ) {
HGDIOBJ prev_pen = SelectObject ( hdc , pen ) ;
HGDIOBJ prev_brush = SelectObject ( hdc , GetStockObject ( NULL_BRUSH ) ) ;
RECT rc ;
GetWindowRect ( hWnd , & rc ) ;
OffsetRect ( & rc , - rc . left , - rc . top ) ;
Rectangle ( hdc , rc . left , rc . top , rc . right , rc . bottom ) ;
SelectObject ( hdc , prev_pen ) ;
SelectObject ( hdc , prev_brush ) ;
DeleteObject ( pen ) ;
}
ReleaseDC ( hWnd , hdc ) ;
}
return 0 ;
}
} break ;
2023-01-15 10:05:25 +00:00
case WM_NCHITTEST : {
if ( windows [ window_id ] . mpass ) {
return HTTRANSPARENT ;
}
} break ;
2022-02-24 09:21:23 +00:00
case WM_MOUSEACTIVATE : {
2023-11-28 18:43:07 +00:00
if ( windows [ window_id ] . no_focus | | windows [ window_id ] . is_popup ) {
2022-02-24 09:21:23 +00:00
return MA_NOACTIVATE ; // Do not activate, but process mouse messages.
}
} break ;
2023-11-02 12:44:37 +00:00
case WM_ACTIVATEAPP : {
bool new_app_focused = ( bool ) wParam ;
if ( new_app_focused = = app_focused ) {
break ;
2020-06-29 23:47:18 +00:00
}
2023-11-02 12:44:37 +00:00
app_focused = new_app_focused ;
if ( OS : : get_singleton ( ) - > get_main_loop ( ) ) {
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( app_focused ? MainLoop : : NOTIFICATION_APPLICATION_FOCUS_IN : MainLoop : : NOTIFICATION_APPLICATION_FOCUS_OUT ) ;
2020-06-29 23:47:18 +00:00
}
2021-09-10 13:44:44 +00:00
} break ;
2023-11-02 12:44:37 +00:00
case WM_ACTIVATE : {
2024-04-30 12:31:43 +00:00
// Activation can happen just after the window has been created, even before the callbacks are set.
// Therefore, it's safer to defer the delivery of the event.
if ( ! windows [ window_id ] . activate_timer_id ) {
windows [ window_id ] . activate_timer_id = SetTimer ( windows [ window_id ] . hWnd , 1 , USER_TIMER_MINIMUM , ( TIMERPROC ) nullptr ) ;
}
windows [ window_id ] . activate_state = GET_WM_ACTIVATE_STATE ( wParam , lParam ) ;
return 0 ;
2021-09-10 13:44:44 +00:00
} break ;
2020-03-09 15:56:48 +00:00
case WM_GETMINMAXINFO : {
if ( windows [ window_id ] . resizable & & ! windows [ window_id ] . fullscreen ) {
2021-08-23 02:30:28 +00:00
// Size of window decorations.
2022-11-30 08:28:16 +00:00
Size2 decor = window_get_size_with_decorations ( window_id ) - window_get_size ( window_id ) ;
2021-08-23 02:30:28 +00:00
2020-03-09 15:56:48 +00:00
MINMAXINFO * min_max_info = ( MINMAXINFO * ) lParam ;
if ( windows [ window_id ] . min_size ! = Size2 ( ) ) {
min_max_info - > ptMinTrackSize . x = windows [ window_id ] . min_size . x + decor . x ;
min_max_info - > ptMinTrackSize . y = windows [ window_id ] . min_size . y + decor . y ;
}
if ( windows [ window_id ] . max_size ! = Size2 ( ) ) {
min_max_info - > ptMaxTrackSize . x = windows [ window_id ] . max_size . x + decor . x ;
min_max_info - > ptMaxTrackSize . y = windows [ window_id ] . max_size . y + decor . y ;
}
2024-02-14 14:42:26 +00:00
if ( windows [ window_id ] . borderless ) {
Rect2i screen_rect = screen_get_usable_rect ( window_get_current_screen ( window_id ) ) ;
// Set the size of (borderless) maximized mode to exclude taskbar (or any other panel) if present.
min_max_info - > ptMaxPosition . x = screen_rect . position . x ;
min_max_info - > ptMaxPosition . y = screen_rect . position . y ;
min_max_info - > ptMaxSize . x = screen_rect . size . x ;
min_max_info - > ptMaxSize . y = screen_rect . size . y ;
}
2020-03-09 15:56:48 +00:00
return 0 ;
}
2021-09-10 13:44:44 +00:00
} break ;
2023-01-11 18:15:10 +00:00
case WM_ERASEBKGND : {
Color early_color ;
if ( ! _get_window_early_clear_override ( early_color ) ) {
break ;
}
bool must_recreate_brush = ! window_bkg_brush | | window_bkg_brush_color ! = early_color . to_argb32 ( ) ;
if ( must_recreate_brush ) {
if ( window_bkg_brush ) {
DeleteObject ( window_bkg_brush ) ;
}
window_bkg_brush = CreateSolidBrush ( RGB ( early_color . get_r8 ( ) , early_color . get_g8 ( ) , early_color . get_b8 ( ) ) ) ;
}
HDC hdc = ( HDC ) wParam ;
RECT rect = { } ;
if ( GetUpdateRect ( hWnd , & rect , true ) ) {
FillRect ( hdc , & rect , window_bkg_brush ) ;
}
return 1 ;
} break ;
2021-09-10 13:44:44 +00:00
case WM_PAINT : {
2020-03-09 15:56:48 +00:00
Main : : force_redraw ( ) ;
2021-09-10 13:44:44 +00:00
} break ;
2024-01-19 18:46:26 +00:00
case WM_SETTINGCHANGE :
case WM_SYSCOLORCHANGE : {
2022-08-29 08:24:48 +00:00
if ( lParam & & CompareStringOrdinal ( reinterpret_cast < LPCWCH > ( lParam ) , - 1 , L " ImmersiveColorSet " , - 1 , true ) = = CSTR_EQUAL ) {
2022-09-02 18:44:58 +00:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 08:24:48 +00:00
BOOL value = is_dark_mode ( ) ;
2024-01-24 04:44:40 +00:00
: : DwmSetWindowAttribute ( windows [ window_id ] . hWnd , use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
2022-08-29 08:24:48 +00:00
}
}
2024-01-19 18:46:26 +00:00
if ( system_theme_changed . is_valid ( ) ) {
system_theme_changed . call ( ) ;
}
2022-08-29 08:24:48 +00:00
} break ;
case WM_THEMECHANGED : {
2022-09-02 18:44:58 +00:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 08:24:48 +00:00
BOOL value = is_dark_mode ( ) ;
2024-01-24 04:44:40 +00:00
: : DwmSetWindowAttribute ( windows [ window_id ] . hWnd , use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
2022-08-29 08:24:48 +00:00
}
} break ;
2021-09-10 13:44:44 +00:00
case WM_SYSCOMMAND : // Intercept system commands.
2020-03-09 15:56:48 +00:00
{
2021-09-10 13:44:44 +00:00
switch ( wParam ) // Check system calls.
2020-03-09 15:56:48 +00:00
{
2021-09-10 13:44:44 +00:00
case SC_SCREENSAVE : // Screensaver trying to start?
case SC_MONITORPOWER : // Monitor trying to enter powersave?
return 0 ; // Prevent from happening.
2020-03-09 15:56:48 +00:00
case SC_KEYMENU :
2024-02-14 14:42:26 +00:00
Engine * engine = Engine : : get_singleton ( ) ;
if ( ( ( lParam > > 16 ) < = 0 ) & & ! engine - > is_project_manager_hint ( ) & & ! engine - > is_editor_hint ( ) & & ! GLOBAL_GET ( " application/run/enable_alt_space_menu " ) ) {
return 0 ;
}
if ( ! alt_mem | | ! ( GetAsyncKeyState ( VK_SPACE ) & ( 1 < < 15 ) ) ) {
2020-03-09 15:56:48 +00:00
return 0 ;
2022-02-16 12:56:32 +00:00
}
2024-02-14 14:42:26 +00:00
SendMessage ( windows [ window_id ] . hWnd , WM_SYSKEYUP , VK_SPACE , 0 ) ;
SendMessage ( windows [ window_id ] . hWnd , WM_SYSKEYUP , VK_MENU , 0 ) ;
2020-03-09 15:56:48 +00:00
}
2021-09-10 13:44:44 +00:00
} break ;
2023-08-03 09:45:56 +00:00
case WM_INDICATOR_CALLBACK_MESSAGE : {
if ( lParam = = WM_LBUTTONDOWN | | lParam = = WM_RBUTTONDOWN | | lParam = = WM_MBUTTONDOWN | | lParam = = WM_XBUTTONDOWN ) {
IndicatorID iid = ( IndicatorID ) wParam ;
MouseButton mb = MouseButton : : LEFT ;
if ( lParam = = WM_RBUTTONDOWN ) {
mb = MouseButton : : RIGHT ;
} else if ( lParam = = WM_MBUTTONDOWN ) {
mb = MouseButton : : MIDDLE ;
} else if ( lParam = = WM_XBUTTONDOWN ) {
mb = MouseButton : : MB_XBUTTON1 ;
}
if ( indicators . has ( iid ) ) {
2024-03-16 21:14:30 +00:00
if ( lParam = = WM_RBUTTONDOWN & & indicators [ iid ] . menu_rid . is_valid ( ) & & native_menu - > has_menu ( indicators [ iid ] . menu_rid ) ) {
NOTIFYICONIDENTIFIER nid ;
ZeroMemory ( & nid , sizeof ( NOTIFYICONIDENTIFIER ) ) ;
nid . cbSize = sizeof ( NOTIFYICONIDENTIFIER ) ;
nid . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
nid . uID = iid ;
nid . guidItem = GUID_NULL ;
RECT rect ;
if ( Shell_NotifyIconGetRect ( & nid , & rect ) = = S_OK ) {
native_menu - > popup ( indicators [ iid ] . menu_rid , Vector2i ( ( rect . left + rect . right ) / 2 , ( rect . top + rect . bottom ) / 2 ) ) ;
}
} else if ( indicators [ iid ] . callback . is_valid ( ) ) {
2023-08-03 09:45:56 +00:00
Variant v_button = mb ;
Variant v_pos = mouse_get_position ( ) ;
Variant * v_args [ 2 ] = { & v_button , & v_pos } ;
Variant ret ;
Callable : : CallError ce ;
indicators [ iid ] . callback . callp ( ( const Variant * * ) & v_args , 2 , ret , ce ) ;
}
}
return 0 ;
}
} break ;
2024-04-30 12:31:43 +00:00
case WM_CLOSE : {
if ( windows [ window_id ] . activate_timer_id ) {
KillTimer ( windows [ window_id ] . hWnd , windows [ window_id ] . activate_timer_id ) ;
windows [ window_id ] . activate_timer_id = 0 ;
}
2020-03-09 15:56:48 +00:00
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_CLOSE_REQUEST ) ;
2024-04-30 12:31:43 +00:00
return 0 ;
2020-03-09 15:56:48 +00:00
}
case WM_MOUSELEAVE : {
2022-10-26 09:37:45 +00:00
if ( window_mouseover_id = = window_id ) {
old_invalid = true ;
window_mouseover_id = INVALID_WINDOW_ID ;
2020-03-09 15:56:48 +00:00
2022-10-26 09:37:45 +00:00
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_EXIT ) ;
2022-12-06 21:56:07 +00:00
} else if ( window_mouseover_id ! = INVALID_WINDOW_ID & & windows . has ( window_mouseover_id ) ) {
2022-11-01 17:47:10 +00:00
// This is reached during drag and drop, after dropping in a different window.
// Once-off notification, must call again.
track_mouse_leave_event ( windows [ window_mouseover_id ] . hWnd ) ;
2022-10-26 09:37:45 +00:00
}
2020-03-09 15:56:48 +00:00
} break ;
case WM_INPUT : {
2023-08-03 13:18:26 +00:00
if ( ! use_raw_input ) {
2020-03-09 15:56:48 +00:00
break ;
}
UINT dwSize ;
2020-04-01 23:20:12 +00:00
GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , nullptr , & dwSize , sizeof ( RAWINPUTHEADER ) ) ;
2020-03-09 15:56:48 +00:00
LPBYTE lpb = new BYTE [ dwSize ] ;
2020-04-01 23:20:12 +00:00
if ( lpb = = nullptr ) {
2020-03-09 15:56:48 +00:00
return 0 ;
}
2022-02-16 12:56:32 +00:00
if ( GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , lpb , & dwSize , sizeof ( RAWINPUTHEADER ) ) ! = dwSize ) {
2020-03-09 15:56:48 +00:00
OutputDebugString ( TEXT ( " GetRawInputData does not return correct size ! \n " ) ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
RAWINPUT * raw = ( RAWINPUT * ) lpb ;
2023-08-03 13:18:26 +00:00
if ( raw - > header . dwType = = RIM_TYPEKEYBOARD ) {
if ( raw - > data . keyboard . VKey = = VK_SHIFT ) {
// If multiple Shifts are held down at the same time,
// Windows natively only sends a KEYUP for the last one to be released.
if ( raw - > data . keyboard . Flags & RI_KEY_BREAK ) {
if ( GetAsyncKeyState ( VK_SHIFT ) < 0 ) {
// A Shift is released, but another Shift is still held
ERR_BREAK ( key_event_pos > = KEY_EVENT_BUFFER_SIZE ) ;
KeyEvent ke ;
ke . shift = false ;
ke . alt = alt_mem ;
ke . control = control_mem ;
ke . meta = meta_mem ;
ke . uMsg = WM_KEYUP ;
ke . window_id = window_id ;
ke . wParam = VK_SHIFT ;
// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
// Bit 30 -> key was previously down, bit 31 -> key is being released.
ke . lParam = raw - > data . keyboard . MakeCode < < 16 | 1 < < 30 | 1 < < 31 ;
key_event_buffer [ key_event_pos + + ] = ke ;
}
}
}
} else if ( mouse_mode = = MOUSE_MODE_CAPTURED & & raw - > header . dwType = = RIM_TYPEMOUSE ) {
2020-03-09 15:56:48 +00:00
Ref < InputEventMouseMotion > mm ;
2021-06-17 22:03:09 +00:00
mm . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
mm - > set_window_id ( window_id ) ;
2021-04-24 20:33:50 +00:00
mm - > set_ctrl_pressed ( control_mem ) ;
mm - > set_shift_pressed ( shift_mem ) ;
mm - > set_alt_pressed ( alt_mem ) ;
2020-05-05 11:16:02 +00:00
2020-05-03 12:38:58 +00:00
mm - > set_pressure ( ( raw - > data . mouse . ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN ) ? 1.0f : 0.0f ) ;
2020-03-09 15:56:48 +00:00
mm - > set_button_mask ( last_button_state ) ;
Point2i c ( windows [ window_id ] . width / 2 , windows [ window_id ] . height / 2 ) ;
2021-09-10 13:44:44 +00:00
// Centering just so it works as before.
2020-03-09 15:56:48 +00:00
POINT pos = { ( int ) c . x , ( int ) c . y } ;
ClientToScreen ( windows [ window_id ] . hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
mm - > set_position ( c ) ;
mm - > set_global_position ( c ) ;
2021-12-29 13:22:22 +00:00
mm - > set_velocity ( Vector2 ( 0 , 0 ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_screen_velocity ( Vector2 ( 0 , 0 ) ) ;
2020-03-09 15:56:48 +00:00
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 ) ;
2021-09-10 13:44:44 +00:00
POINT coords ; // Client coords.
2020-03-09 15:56:48 +00:00
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 ;
}
2023-10-04 17:20:01 +00:00
mm - > set_relative_screen_position ( mm - > get_relative ( ) ) ;
2020-03-09 15:56:48 +00:00
2024-04-08 11:02:58 +00:00
if ( ( windows [ window_id ] . window_focused | | windows [ window_id ] . is_popup ) & & mm - > get_relative ( ) ! = Vector2 ( ) ) {
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2021-09-10 13:44:44 +00:00
}
2020-03-09 15:56:48 +00:00
}
delete [ ] lpb ;
} break ;
2020-05-05 11:16:02 +00:00
case WT_CSRCHANGE :
case WT_PROXIMITY : {
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2020-05-05 11:16:02 +00:00
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + windows [ window_id ] . wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
windows [ window_id ] . min_pressure = int ( pressure . axMin ) ;
windows [ window_id ] . max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + windows [ window_id ] . wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
windows [ window_id ] . tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
return 0 ;
}
} break ;
case WT_PACKET : {
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2020-05-05 11:16:02 +00:00
PACKET packet ;
if ( wintab_WTPacket ( windows [ window_id ] . wtctx , wParam , & packet ) ) {
2022-09-01 10:03:51 +00:00
POINT coords ;
GetCursorPos ( & coords ) ;
ScreenToClient ( windows [ window_id ] . hWnd , & coords ) ;
2020-05-05 11:16:02 +00:00
windows [ window_id ] . last_pressure_update = 0 ;
2022-09-01 10:03:51 +00:00
float pressure = float ( packet . pkNormalPressure - windows [ window_id ] . min_pressure ) / float ( windows [ window_id ] . max_pressure - windows [ window_id ] . min_pressure ) ;
2020-05-05 11:16:02 +00:00
double azim = ( packet . pkOrientation . orAzimuth / 10.0f ) * ( Math_PI / 180 ) ;
double alt = Math : : tan ( ( Math : : abs ( packet . pkOrientation . orAltitude / 10.0f ) ) * ( Math_PI / 180 ) ) ;
2022-09-01 10:03:51 +00:00
bool inverted = packet . pkStatus & TPS_INVERT ;
2020-05-05 11:16:02 +00:00
2022-09-01 10:03:51 +00:00
Vector2 tilt = ( windows [ window_id ] . tilt_supported ) ? Vector2 ( Math : : atan ( Math : : sin ( azim ) / alt ) , Math : : atan ( Math : : cos ( azim ) / alt ) ) : Vector2 ( ) ;
2020-05-08 07:56:42 +00:00
2022-09-01 10:03:51 +00:00
// Nothing changed, ignore event.
if ( ! old_invalid & & coords . x = = old_x & & coords . y = = old_y & & windows [ window_id ] . last_pressure = = pressure & & windows [ window_id ] . last_tilt = = tilt & & windows [ window_id ] . last_pen_inverted = = inverted ) {
break ;
}
2022-06-19 09:58:24 +00:00
2022-09-01 10:03:51 +00:00
windows [ window_id ] . last_pressure = pressure ;
windows [ window_id ] . last_tilt = tilt ;
windows [ window_id ] . last_pen_inverted = inverted ;
2020-05-08 07:56:42 +00:00
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2024-04-08 11:02:58 +00:00
if ( ! windows [ window_id ] . window_focused & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-05-08 07:56:42 +00:00
break ;
2022-02-16 12:56:32 +00:00
}
2020-05-08 07:56:42 +00:00
Ref < InputEventMouseMotion > mm ;
2021-06-17 22:03:09 +00:00
mm . instantiate ( ) ;
2020-05-08 07:56:42 +00:00
mm - > set_window_id ( window_id ) ;
2021-04-24 20:33:50 +00:00
mm - > set_ctrl_pressed ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift_pressed ( GetKeyState ( VK_SHIFT ) < 0 ) ;
mm - > set_alt_pressed ( alt_mem ) ;
2020-05-08 07:56:42 +00:00
mm - > set_pressure ( windows [ window_id ] . last_pressure ) ;
mm - > set_tilt ( windows [ window_id ] . last_tilt ) ;
2022-06-19 09:58:24 +00:00
mm - > set_pen_inverted ( windows [ window_id ] . last_pen_inverted ) ;
2020-05-08 07:56:42 +00:00
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 ( windows [ window_id ] . width / 2 , windows [ window_id ] . 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 ( windows [ window_id ] . hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
}
2021-12-29 13:22:22 +00:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_screen_velocity ( mm - > get_velocity ( ) ) ;
2020-05-08 07:56:42 +00:00
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 ) ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_relative_screen_position ( mm - > get_relative ( ) ) ;
2020-05-08 07:56:42 +00:00
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2024-04-08 11:02:58 +00:00
if ( windows [ window_id ] . window_focused | | window_get_active_popup ( ) = = window_id ) {
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2022-02-16 12:56:32 +00:00
}
2020-05-05 11:16:02 +00:00
}
return 0 ;
}
} break ;
2020-05-22 07:44:47 +00:00
case WM_POINTERENTER : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) ! = " winink " ) | | ! winink_available ) {
2020-05-22 07:44:47 +00:00
break ;
}
uint32_t pointer_id = LOWORD ( wParam ) ;
POINTER_INPUT_TYPE pointer_type = PT_POINTER ;
if ( ! win8p_GetPointerType ( pointer_id , & pointer_type ) ) {
break ;
}
if ( pointer_type ! = PT_PEN ) {
break ;
}
windows [ window_id ] . block_mm = true ;
return 0 ;
} break ;
case WM_POINTERLEAVE : {
windows [ window_id ] . block_mm = false ;
return 0 ;
} break ;
2020-03-09 15:56:48 +00:00
case WM_POINTERUPDATE : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) ! = " winink " ) | | ! winink_available ) {
2020-03-09 15:56:48 +00:00
break ;
}
uint32_t pointer_id = LOWORD ( wParam ) ;
POINTER_INPUT_TYPE pointer_type = PT_POINTER ;
if ( ! win8p_GetPointerType ( pointer_id , & pointer_type ) ) {
break ;
}
if ( pointer_type ! = PT_PEN ) {
break ;
}
POINTER_PEN_INFO pen_info ;
if ( ! win8p_GetPointerPenInfo ( pointer_id , & pen_info ) ) {
break ;
}
2020-04-28 13:19:37 +00:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 13:44:44 +00:00
// Universal translation enabled; ignore OS translation.
2020-03-09 15:56:48 +00:00
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
2022-10-26 09:37:45 +00:00
if ( window_mouseover_id ! = window_id ) {
2021-09-10 13:44:44 +00:00
// Mouse enter.
2020-03-09 15:56:48 +00:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
2022-12-06 21:56:07 +00:00
if ( window_mouseover_id ! = INVALID_WINDOW_ID & & windows . has ( window_mouseover_id ) ) {
2022-10-26 09:37:45 +00:00
// Leave previous window.
_send_window_event ( windows [ window_mouseover_id ] , WINDOW_EVENT_MOUSE_EXIT ) ;
}
2020-03-09 15:56:48 +00:00
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_ENTER ) ;
}
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2022-10-26 09:37:45 +00:00
window_mouseover_id = window_id ;
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
// Once-off notification, must call again.
2021-12-28 10:27:46 +00:00
track_mouse_leave_event ( hWnd ) ;
2020-03-09 15:56:48 +00:00
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2024-04-08 11:02:58 +00:00
if ( ! windows [ window_id ] . window_focused & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-03-09 15:56:48 +00:00
break ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
Ref < InputEventMouseMotion > mm ;
2021-06-17 22:03:09 +00:00
mm . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
mm - > set_window_id ( window_id ) ;
2020-05-03 12:38:58 +00: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 ) ) ;
}
2022-06-19 09:58:24 +00:00
mm - > set_pen_inverted ( pen_info . penFlags & ( PEN_FLAG_INVERTED | PEN_FLAG_ERASER ) ) ;
2020-03-09 15:56:48 +00:00
2021-04-24 20:33:50 +00:00
mm - > set_ctrl_pressed ( GetKeyState ( VK_CONTROL ) < 0 ) ;
mm - > set_shift_pressed ( GetKeyState ( VK_SHIFT ) < 0 ) ;
mm - > set_alt_pressed ( alt_mem ) ;
2020-03-09 15:56:48 +00:00
mm - > set_button_mask ( last_button_state ) ;
2021-09-10 13:44:44 +00:00
POINT coords ; // Client coords.
2020-03-09 15:56:48 +00:00
coords . x = GET_X_LPARAM ( lParam ) ;
coords . y = GET_Y_LPARAM ( lParam ) ;
ScreenToClient ( windows [ window_id ] . 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 ( windows [ window_id ] . width / 2 , windows [ window_id ] . 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 ) ;
}
2021-12-29 13:22:22 +00:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_screen_velocity ( mm - > get_velocity ( ) ) ;
2020-03-09 15:56:48 +00:00
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 ) ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_relative_screen_position ( mm - > get_relative ( ) ) ;
2020-03-09 15:56:48 +00:00
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2024-04-08 11:02:58 +00:00
if ( windows [ window_id ] . window_focused | | window_get_active_popup ( ) = = window_id ) {
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2020-03-09 15:56:48 +00:00
}
2021-09-10 13:44:44 +00:00
return 0 ; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.
2020-03-09 15:56:48 +00:00
} break ;
case WM_MOUSEMOVE : {
2020-05-22 07:44:47 +00:00
if ( windows [ window_id ] . block_mm ) {
break ;
}
2020-03-09 15:56:48 +00:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2020-04-28 13:19:37 +00:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 13:44:44 +00:00
// Universal translation enabled; ignore OS translation.
2020-03-09 15:56:48 +00:00
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
2022-10-26 09:37:45 +00:00
DisplayServer : : WindowID over_id = get_window_at_screen_position ( mouse_get_position ( ) ) ;
2022-12-06 21:56:07 +00:00
if ( windows . has ( over_id ) & & ! Rect2 ( window_get_position ( over_id ) , Point2 ( windows [ over_id ] . width , windows [ over_id ] . height ) ) . has_point ( mouse_get_position ( ) ) ) {
2022-10-26 09:37:45 +00:00
// Don't consider the windowborder as part of the window.
over_id = INVALID_WINDOW_ID ;
}
if ( window_mouseover_id ! = over_id ) {
2021-09-10 13:44:44 +00:00
// Mouse enter.
2020-03-09 15:56:48 +00:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
2022-12-06 21:56:07 +00:00
if ( window_mouseover_id ! = INVALID_WINDOW_ID & & windows . has ( window_mouseover_id ) ) {
2022-10-26 09:37:45 +00:00
// Leave previous window.
_send_window_event ( windows [ window_mouseover_id ] , WINDOW_EVENT_MOUSE_EXIT ) ;
}
2022-12-06 21:56:07 +00:00
if ( over_id ! = INVALID_WINDOW_ID & & windows . has ( over_id ) ) {
2022-10-26 09:37:45 +00:00
_send_window_event ( windows [ over_id ] , WINDOW_EVENT_MOUSE_ENTER ) ;
}
2020-03-09 15:56:48 +00:00
}
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2022-10-26 09:37:45 +00:00
window_mouseover_id = over_id ;
2020-03-09 15:56:48 +00:00
2021-09-10 13:44:44 +00:00
// Once-off notification, must call again.
2021-12-28 10:27:46 +00:00
track_mouse_leave_event ( hWnd ) ;
2020-03-09 15:56:48 +00:00
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2024-04-08 11:02:58 +00:00
if ( ! windows [ window_id ] . window_focused & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-03-09 15:56:48 +00:00
break ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
2022-10-26 09:37:45 +00:00
DisplayServer : : WindowID receiving_window_id = _get_focused_window_or_popup ( ) ;
if ( receiving_window_id = = INVALID_WINDOW_ID ) {
receiving_window_id = window_id ;
}
2020-03-09 15:56:48 +00:00
Ref < InputEventMouseMotion > mm ;
2021-06-17 22:03:09 +00:00
mm . instantiate ( ) ;
2022-10-26 09:37:45 +00:00
mm - > set_window_id ( receiving_window_id ) ;
2021-04-24 20:33:50 +00:00
mm - > set_ctrl_pressed ( ( wParam & MK_CONTROL ) ! = 0 ) ;
mm - > set_shift_pressed ( ( wParam & MK_SHIFT ) ! = 0 ) ;
mm - > set_alt_pressed ( alt_mem ) ;
2020-03-09 15:56:48 +00:00
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2021-03-12 13:35:16 +00:00
// Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently.
2020-05-05 11:16:02 +00:00
if ( windows [ window_id ] . last_pressure_update < 10 ) {
windows [ window_id ] . last_pressure_update + + ;
} else {
windows [ window_id ] . last_tilt = Vector2 ( ) ;
windows [ window_id ] . last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-06-19 09:58:24 +00:00
windows [ window_id ] . last_pen_inverted = false ;
2020-05-05 11:16:02 +00:00
}
} else {
windows [ window_id ] . last_tilt = Vector2 ( ) ;
windows [ window_id ] . last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-06-19 09:58:24 +00:00
windows [ window_id ] . last_pen_inverted = false ;
2020-05-05 11:16:02 +00:00
}
mm - > set_pressure ( windows [ window_id ] . last_pressure ) ;
mm - > set_tilt ( windows [ window_id ] . last_tilt ) ;
2022-06-19 09:58:24 +00:00
mm - > set_pen_inverted ( windows [ window_id ] . last_pen_inverted ) ;
2020-05-03 12:38:58 +00:00
2020-03-09 15:56:48 +00: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 ( windows [ window_id ] . width / 2 , windows [ window_id ] . 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 ( windows [ window_id ] . hWnd , & pos ) ;
SetCursorPos ( pos . x , pos . y ) ;
}
2021-12-29 13:22:22 +00:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_screen_velocity ( mm - > get_velocity ( ) ) ;
2020-03-09 15:56:48 +00:00
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 ) ) ) ;
2023-10-04 17:20:01 +00:00
mm - > set_relative_screen_position ( mm - > get_relative ( ) ) ;
2020-03-09 15:56:48 +00:00
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2022-10-26 09:37:45 +00:00
2022-11-30 12:39:31 +00:00
if ( receiving_window_id ! = window_id ) {
// Adjust event position relative to window distance when event is sent to a different window.
mm - > set_position ( mm - > get_position ( ) - window_get_position ( receiving_window_id ) + window_get_position ( window_id ) ) ;
mm - > set_global_position ( mm - > get_position ( ) ) ;
2022-10-31 21:58:56 +00:00
}
2022-10-26 09:37:45 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
2020-04-28 13:19:37 +00:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 13:44:44 +00:00
// Universal translation enabled; ignore OS translations for left button.
2020-03-09 15:56:48 +00:00
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 ;
2021-06-17 22:03:09 +00:00
mb . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
mb - > set_window_id ( window_id ) ;
switch ( uMsg ) {
case WM_LBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_LBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_MBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_MBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_RBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_RBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_LBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2021-04-13 08:25:44 +00:00
mb - > set_double_click ( true ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_RBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2021-04-13 08:25:44 +00:00
mb - > set_double_click ( true ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_MBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2021-04-13 08:25:44 +00:00
mb - > set_double_click ( true ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_MOUSEWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
2021-03-30 22:35:08 +00:00
if ( ! motion ) {
2020-03-09 15:56:48 +00:00
return 0 ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
2021-03-30 22:35:08 +00:00
if ( motion > 0 ) {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : WHEEL_UP ) ;
2021-03-30 22:35:08 +00:00
} else {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : WHEEL_DOWN ) ;
2021-03-30 22:35:08 +00:00
}
2021-08-06 13:06:11 +00:00
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_MOUSEHWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
2021-03-30 22:35:08 +00:00
if ( ! motion ) {
2020-03-09 15:56:48 +00:00
return 0 ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
if ( motion < 0 ) {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : WHEEL_LEFT ) ;
2020-03-09 15:56:48 +00:00
} else {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : WHEEL_RIGHT ) ;
2020-03-09 15:56:48 +00:00
}
2021-08-06 13:06:11 +00:00
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_XBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-03-30 22:35:08 +00:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-30 22:35:08 +00:00
} else {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
} break ;
case WM_XBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-03-30 22:35:08 +00:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-30 22:35:08 +00:00
} else {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
} break ;
case WM_XBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-03-30 22:35:08 +00:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-30 22:35:08 +00:00
} else {
2021-08-13 21:31:57 +00:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-30 22:35:08 +00:00
}
2021-04-13 08:25:44 +00:00
mb - > set_double_click ( true ) ;
2020-03-09 15:56:48 +00:00
} break ;
default : {
return 0 ;
}
}
2021-04-24 20:33:50 +00:00
mb - > set_ctrl_pressed ( ( wParam & MK_CONTROL ) ! = 0 ) ;
mb - > set_shift_pressed ( ( wParam & MK_SHIFT ) ! = 0 ) ;
mb - > set_alt_pressed ( alt_mem ) ;
2021-09-10 13:44:44 +00:00
// mb->is_alt_pressed()=(wParam&MK_MENU)!=0;
2021-03-30 22:35:08 +00:00
if ( mb - > is_pressed ( ) ) {
2023-01-07 23:55:54 +00:00
last_button_state . set_flag ( mouse_button_to_mask ( mb - > get_button_index ( ) ) ) ;
2021-03-30 22:35:08 +00:00
} else {
2023-01-07 23:55:54 +00:00
last_button_state . clear_flag ( mouse_button_to_mask ( mb - > get_button_index ( ) ) ) ;
2021-03-30 22:35:08 +00:00
}
2020-03-09 15:56:48 +00:00
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 ( ) ) {
2022-02-16 12:56:32 +00:00
if ( + + pressrc > 0 & & mouse_mode ! = MOUSE_MODE_CAPTURED ) {
2020-03-09 15:56:48 +00:00
SetCapture ( hWnd ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
} else {
2023-02-04 05:52:33 +00:00
if ( - - pressrc < = 0 | | last_button_state . is_empty ( ) ) {
2020-03-09 15:56:48 +00:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
ReleaseCapture ( ) ;
}
pressrc = 0 ;
}
}
} else {
2022-03-31 12:06:10 +00:00
// For reasons unknown to humanity, wheel comes in screen coordinates.
2020-03-09 15:56:48 +00:00
POINT coords ;
coords . x = mb - > get_position ( ) . x ;
coords . y = mb - > get_position ( ) . y ;
ScreenToClient ( hWnd , & coords ) ;
mb - > set_position ( Vector2 ( coords . x , coords . y ) ) ;
}
mb - > set_global_position ( mb - > get_position ( ) ) ;
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mb ) ;
2021-08-13 21:31:57 +00:00
if ( mb - > is_pressed ( ) & & mb - > get_button_index ( ) > = MouseButton : : WHEEL_UP & & mb - > get_button_index ( ) < = MouseButton : : WHEEL_RIGHT ) {
2021-09-10 13:44:44 +00:00
// Send release for mouse wheel.
2020-03-09 15:56:48 +00:00
Ref < InputEventMouseButton > mbd = mb - > duplicate ( ) ;
mbd - > set_window_id ( window_id ) ;
2023-01-07 23:55:54 +00:00
last_button_state . clear_flag ( mouse_button_to_mask ( mbd - > get_button_index ( ) ) ) ;
2020-03-09 15:56:48 +00:00
mbd - > set_button_mask ( last_button_state ) ;
mbd - > set_pressed ( false ) ;
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( mbd ) ;
2020-03-09 15:56:48 +00:00
}
} break ;
2021-11-05 20:36:13 +00:00
case WM_WINDOWPOSCHANGED : {
Rect2i window_client_rect ;
2022-02-08 08:36:57 +00:00
Rect2i window_rect ;
2021-11-05 20:36:13 +00:00
{
RECT rect ;
GetClientRect ( hWnd , & rect ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . left ) ;
ClientToScreen ( hWnd , ( POINT * ) & rect . right ) ;
window_client_rect = Rect2i ( rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top ) ;
2023-01-04 22:00:02 +00:00
window_client_rect . position - = _get_screens_origin ( ) ;
2022-02-08 08:36:57 +00:00
RECT wrect ;
GetWindowRect ( hWnd , & wrect ) ;
window_rect = Rect2i ( wrect . left , wrect . top , wrect . right - wrect . left , wrect . bottom - wrect . top ) ;
2023-01-04 22:00:02 +00:00
window_rect . position - = _get_screens_origin ( ) ;
2020-03-09 15:56:48 +00:00
}
2021-11-05 20:36:13 +00:00
WINDOWPOS * window_pos_params = ( WINDOWPOS * ) lParam ;
WindowData & window = windows [ window_id ] ;
bool rect_changed = false ;
if ( ! ( window_pos_params - > flags & SWP_NOSIZE ) | | window_pos_params - > flags & SWP_FRAMECHANGED ) {
int screen_id = window_get_current_screen ( window_id ) ;
Size2i screen_size = screen_get_size ( screen_id ) ;
Point2i screen_position = screen_get_position ( screen_id ) ;
window . maximized = false ;
window . minimized = false ;
window . fullscreen = false ;
if ( IsIconic ( hWnd ) ) {
window . minimized = true ;
} else if ( IsZoomed ( hWnd ) ) {
window . maximized = true ;
2024-02-14 14:42:26 +00:00
// If maximized_window_size == screen_size add 1px border to prevent switching to exclusive_fs.
if ( ! window . maximized_fs & & window . borderless & & window_rect . position = = screen_position & & window_rect . size = = screen_size ) {
// Window (borderless) was just maximized and the covers the entire screen.
window . maximized_fs = true ;
_update_window_style ( window_id , false ) ;
}
2022-02-08 08:36:57 +00:00
} else if ( window_rect . position = = screen_position & & window_rect . size = = screen_size ) {
2021-11-05 20:36:13 +00:00
window . fullscreen = true ;
}
2024-02-14 14:42:26 +00:00
if ( window . maximized_fs & & ! window . maximized ) {
// Window (maximized and covering fullscreen) was just non-maximized.
window . maximized_fs = false ;
_update_window_style ( window_id , false ) ;
}
2021-11-05 20:36:13 +00:00
if ( ! window . minimized ) {
window . width = window_client_rect . size . width ;
window . height = window_client_rect . size . height ;
2020-03-09 15:56:48 +00:00
2021-11-05 20:36:13 +00:00
rect_changed = true ;
2020-03-09 15:56:48 +00:00
}
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2023-12-19 17:57:56 +00:00
if ( rendering_context & & window . context_created ) {
2023-12-19 11:48:02 +00:00
// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
2023-12-19 17:57:56 +00:00
rendering_context - > window_set_size ( window_id , window . width , window . height ) ;
2023-01-09 15:56:16 +00:00
}
2023-12-19 11:48:02 +00:00
# endif
2020-03-09 15:56:48 +00:00
}
2021-11-05 20:36:13 +00:00
if ( ! window . minimized & & ( ! ( window_pos_params - > flags & SWP_NOMOVE ) | | window_pos_params - > flags & SWP_FRAMECHANGED ) ) {
window . last_pos = window_client_rect . position ;
rect_changed = true ;
2020-03-09 15:56:48 +00:00
}
2021-11-05 20:36:13 +00:00
if ( rect_changed ) {
2024-04-27 09:56:39 +00:00
if ( window . rect_changed_callback . is_valid ( ) ) {
2023-07-11 14:18:10 +00:00
window . rect_changed_callback . call ( Rect2i ( window . last_pos . x , window . last_pos . y , window . width , window . height ) ) ;
2021-11-05 20:36:13 +00:00
}
2023-01-10 18:39:28 +00:00
// Update cursor clip region after window rect has changed.
if ( mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
RECT crect ;
GetClientRect ( window . hWnd , & crect ) ;
ClientToScreen ( window . hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( window . hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
}
2020-03-09 15:56:48 +00:00
}
2021-11-05 20:36:13 +00:00
// Return here to prevent WM_MOVE and WM_SIZE from being sent
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/wm-windowposchanged#remarks
return 0 ;
2020-03-09 15:56:48 +00:00
} break ;
2021-11-05 20:36:13 +00:00
2020-03-09 15:56:48 +00:00
case WM_ENTERSIZEMOVE : {
2020-04-28 13:19:37 +00:00
Input : : get_singleton ( ) - > release_pressed_events ( ) ;
2021-01-05 19:37:45 +00:00
windows [ window_id ] . move_timer_id = SetTimer ( windows [ window_id ] . hWnd , 1 , USER_TIMER_MINIMUM , ( TIMERPROC ) nullptr ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_EXITSIZEMOVE : {
2021-01-05 19:37:45 +00:00
KillTimer ( windows [ window_id ] . hWnd , windows [ window_id ] . move_timer_id ) ;
2020-03-09 15:56:48 +00:00
} break ;
case WM_TIMER : {
2021-01-05 19:37:45 +00:00
if ( wParam = = windows [ window_id ] . move_timer_id ) {
2020-03-09 15:56:48 +00:00
_process_key_events ( ) ;
if ( ! Main : : is_iterating ( ) ) {
Main : : iteration ( ) ;
}
2024-04-30 12:31:43 +00:00
} else if ( wParam = = windows [ window_id ] . activate_timer_id ) {
_process_activate_event ( window_id ) ;
KillTimer ( windows [ window_id ] . hWnd , windows [ window_id ] . activate_timer_id ) ;
windows [ window_id ] . activate_timer_id = 0 ;
2020-03-09 15:56:48 +00:00
}
} break ;
case WM_SYSKEYUP :
case WM_KEYUP :
2022-12-10 23:21:22 +00:00
case WM_SYSKEYDOWN :
2020-03-09 15:56:48 +00:00
case WM_KEYDOWN : {
2022-02-16 12:56:32 +00:00
if ( wParam = = VK_SHIFT ) {
2020-06-10 03:22:57 +00:00
shift_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 12:56:32 +00:00
}
if ( wParam = = VK_CONTROL ) {
2020-06-10 03:22:57 +00:00
control_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
if ( wParam = = VK_MENU ) {
alt_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 12:56:32 +00:00
if ( lParam & ( 1 < < 24 ) ) {
2020-03-09 15:56:48 +00:00
gr_mem = alt_mem ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
}
2023-03-06 08:31:23 +00:00
if ( windows [ window_id ] . ime_suppress_next_keyup & & ( uMsg = = WM_KEYUP | | uMsg = = WM_SYSKEYUP ) ) {
windows [ window_id ] . ime_suppress_next_keyup = false ;
break ;
}
if ( windows [ window_id ] . ime_in_progress ) {
break ;
}
2020-03-09 15:56:48 +00:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
if ( wParam = = VK_F4 & & alt_mem & & ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ) {
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_CLOSE_REQUEST ) ;
}
}
[[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 ;
ke . window_id = window_id ;
2022-02-16 12:56:32 +00:00
if ( ke . uMsg = = WM_SYSKEYDOWN ) {
2020-03-09 15:56:48 +00:00
ke . uMsg = WM_KEYDOWN ;
2022-02-16 12:56:32 +00:00
}
if ( ke . uMsg = = WM_SYSKEYUP ) {
2020-03-09 15:56:48 +00:00
ke . uMsg = WM_KEYUP ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
ke . wParam = wParam ;
ke . lParam = lParam ;
key_event_buffer [ key_event_pos + + ] = ke ;
} break ;
2022-12-10 23:21:22 +00:00
case WM_IME_COMPOSITION : {
CANDIDATEFORM cf ;
cf . dwIndex = 0 ;
2023-01-25 13:29:08 +00:00
cf . dwStyle = CFS_CANDIDATEPOS ;
2022-12-10 23:21:22 +00:00
cf . ptCurrentPos . x = windows [ window_id ] . im_position . x ;
cf . ptCurrentPos . y = windows [ window_id ] . im_position . y ;
2023-01-25 13:29:08 +00:00
ImmSetCandidateWindow ( windows [ window_id ] . im_himc , & cf ) ;
cf . dwStyle = CFS_EXCLUDE ;
2022-12-10 23:21:22 +00:00
cf . rcArea . left = windows [ window_id ] . im_position . x ;
cf . rcArea . right = windows [ window_id ] . im_position . x ;
cf . rcArea . top = windows [ window_id ] . im_position . y ;
cf . rcArea . bottom = windows [ window_id ] . im_position . y ;
ImmSetCandidateWindow ( windows [ window_id ] . im_himc , & cf ) ;
2023-01-25 13:29:08 +00:00
2022-12-10 23:21:22 +00:00
if ( windows [ window_id ] . ime_active ) {
2023-01-25 13:29:08 +00:00
SetCaretPos ( windows [ window_id ] . im_position . x , windows [ window_id ] . im_position . y ) ;
2022-12-10 23:21:22 +00:00
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_OS_IME_UPDATE ) ;
}
} break ;
2020-03-09 15:56:48 +00:00
case WM_INPUTLANGCHANGEREQUEST : {
// FIXME: Do something?
} break ;
2022-12-10 23:21:22 +00:00
case WM_IME_STARTCOMPOSITION : {
if ( windows [ window_id ] . ime_active ) {
windows [ window_id ] . ime_in_progress = true ;
if ( key_event_pos > 0 ) {
key_event_pos - - ;
}
}
return 0 ;
} break ;
case WM_IME_ENDCOMPOSITION : {
if ( windows [ window_id ] . ime_active ) {
windows [ window_id ] . ime_in_progress = false ;
windows [ window_id ] . ime_suppress_next_keyup = true ;
}
return 0 ;
} break ;
case WM_IME_NOTIFY : {
return 0 ;
} break ;
2020-03-09 15:56:48 +00:00
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 ) ;
2021-09-10 13:44:44 +00:00
// Do something with each touch input entry.
2020-03-09 15:56:48 +00:00
if ( ti . dwFlags & TOUCHEVENTF_MOVE ) {
_drag_event ( window_id , touch_pos . x , touch_pos . y , ti . dwID ) ;
} else if ( ti . dwFlags & ( TOUCHEVENTF_UP | TOUCHEVENTF_DOWN ) ) {
_touch_event ( window_id , ti . dwFlags & TOUCHEVENTF_DOWN , touch_pos . x , touch_pos . y , ti . dwID ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
}
bHandled = TRUE ;
} else {
2021-09-10 13:44:44 +00:00
// TODO: Handle the error here.
2020-03-09 15:56:48 +00:00
}
memdelete_arr ( pInputs ) ;
} else {
2021-09-10 13:44:44 +00:00
// TODO: Handle the error here, probably out of memory.
2020-03-09 15:56:48 +00:00
}
if ( bHandled ) {
CloseTouchInputHandle ( ( HTOUCHINPUT ) lParam ) ;
return 0 ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
} break ;
case WM_DEVICECHANGE : {
joypad - > probe_joypads ( ) ;
} break ;
2022-01-14 13:45:16 +00:00
case WM_DESTROY : {
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2023-08-01 22:17:25 +00:00
if ( window_mouseover_id = = window_id ) {
window_mouseover_id = INVALID_WINDOW_ID ;
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_EXIT ) ;
}
2022-01-14 13:45:16 +00:00
} break ;
2020-03-09 15:56:48 +00:00
case WM_SETCURSOR : {
if ( LOWORD ( lParam ) = = HTCLIENT ) {
2024-04-08 11:02:58 +00:00
if ( windows [ window_id ] . window_focused & & ( mouse_mode = = MOUSE_MODE_HIDDEN | | mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) ) {
2021-09-10 13:44:44 +00:00
// Hide the cursor.
2020-06-22 10:05:18 +00:00
if ( hCursor = = nullptr ) {
2020-04-01 23:20:12 +00:00
hCursor = SetCursor ( nullptr ) ;
2020-06-22 10:05:18 +00:00
} else {
2020-04-01 23:20:12 +00:00
SetCursor ( nullptr ) ;
2020-06-22 10:05:18 +00:00
}
2020-03-09 15:56:48 +00:00
} else {
2020-04-01 23:20:12 +00:00
if ( hCursor ! = nullptr ) {
2020-03-09 15:56:48 +00:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2020-04-01 23:20:12 +00:00
hCursor = nullptr ;
2020-03-09 15:56:48 +00:00
}
}
}
} break ;
case WM_DROPFILES : {
HDROP hDropInfo = ( HDROP ) wParam ;
const int buffsize = 4096 ;
2020-07-27 10:43:20 +00:00
WCHAR buf [ buffsize ] ;
2020-03-09 15:56:48 +00:00
2020-04-01 23:20:12 +00:00
int fcount = DragQueryFileW ( hDropInfo , 0xFFFFFFFF , nullptr , 0 ) ;
2020-03-09 15:56:48 +00:00
Vector < String > files ;
for ( int i = 0 ; i < fcount ; i + + ) {
DragQueryFileW ( hDropInfo , i , buf , buffsize ) ;
2020-07-27 10:43:20 +00:00
String file = String : : utf16 ( ( const char16_t * ) buf ) ;
2020-03-09 15:56:48 +00:00
files . push_back ( file ) ;
}
2024-04-27 09:56:39 +00:00
if ( files . size ( ) & & windows [ window_id ] . drop_files_callback . is_valid ( ) ) {
2023-07-11 14:18:10 +00:00
windows [ window_id ] . drop_files_callback . call ( files ) ;
2020-03-09 15:56:48 +00:00
}
} break ;
default : {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
2022-02-16 12:56:32 +00:00
}
}
2020-03-09 15:56:48 +00:00
}
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
}
LRESULT CALLBACK WndProc ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam ) {
DisplayServerWindows * ds_win = static_cast < DisplayServerWindows * > ( DisplayServer : : get_singleton ( ) ) ;
2022-02-16 12:56:32 +00:00
if ( ds_win ) {
2020-03-09 15:56:48 +00:00
return ds_win - > WndProc ( hWnd , uMsg , wParam , lParam ) ;
2022-02-16 12:56:32 +00:00
} else {
2020-03-09 15:56:48 +00:00
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
}
2024-04-30 12:31:43 +00:00
void DisplayServerWindows : : _process_activate_event ( WindowID p_window_id ) {
WindowData & wd = windows [ p_window_id ] ;
if ( wd . activate_state = = WA_ACTIVE | | wd . activate_state = = WA_CLICKACTIVE ) {
2023-11-02 12:44:37 +00:00
last_focused_window = p_window_id ;
2021-01-05 19:37:45 +00:00
alt_mem = false ;
control_mem = false ;
shift_mem = false ;
2023-01-20 13:04:55 +00:00
gr_mem = false ;
2022-01-26 11:58:54 +00:00
_set_mouse_mode_impl ( mouse_mode ) ;
2024-04-30 12:31:43 +00:00
if ( ! IsIconic ( wd . hWnd ) ) {
SetFocus ( wd . hWnd ) ;
2023-11-02 12:44:37 +00:00
}
2024-04-30 12:31:43 +00:00
wd . window_focused = true ;
_send_window_event ( wd , WINDOW_EVENT_FOCUS_IN ) ;
2021-09-10 13:44:44 +00:00
} else { // WM_INACTIVE.
2021-01-05 19:37:45 +00:00
Input : : get_singleton ( ) - > release_pressed_events ( ) ;
2024-04-30 12:31:43 +00:00
track_mouse_leave_event ( wd . hWnd ) ;
2023-11-02 12:44:37 +00:00
// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.
ReleaseCapture ( ) ;
2021-01-05 19:37:45 +00:00
alt_mem = false ;
2024-04-30 12:31:43 +00:00
wd . window_focused = false ;
_send_window_event ( wd , WINDOW_EVENT_FOCUS_OUT ) ;
2021-01-06 12:08:51 +00:00
}
2021-01-05 19:37:45 +00:00
2024-04-30 12:31:43 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & wd . wtctx ) {
wintab_WTEnable ( wd . wtctx , wd . activate_state ) ;
2021-01-05 19:37:45 +00:00
}
}
2020-03-09 15:56:48 +00:00
void DisplayServerWindows : : _process_key_events ( ) {
for ( int i = 0 ; i < key_event_pos ; i + + ) {
KeyEvent & ke = key_event_buffer [ i ] ;
switch ( ke . uMsg ) {
case WM_CHAR : {
2021-09-10 13:44:44 +00:00
// Extended keys should only be processed as WM_KEYDOWN message.
2020-05-18 12:47:54 +00:00
if ( ! KeyMappingWindows : : is_extended_key ( ke . wParam ) & & ( ( i = = 0 & & ke . uMsg = = WM_CHAR ) | | ( i > 0 & & key_event_buffer [ i - 1 ] . uMsg = = WM_CHAR ) ) ) {
2020-09-03 11:22:16 +00:00
static char32_t prev_wc = 0 ;
char32_t unicode = ke . wParam ;
if ( ( unicode & 0xfffffc00 ) = = 0xd800 ) {
if ( prev_wc ! = 0 ) {
ERR_PRINT ( " invalid utf16 surrogate input " ) ;
}
prev_wc = unicode ;
break ; // Skip surrogate.
} else if ( ( unicode & 0xfffffc00 ) = = 0xdc00 ) {
if ( prev_wc = = 0 ) {
ERR_PRINT ( " invalid utf16 surrogate input " ) ;
break ; // Skip invalid surrogate.
}
unicode = ( prev_wc < < 10UL ) + unicode - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
prev_wc = 0 ;
} else {
prev_wc = 0 ;
}
2020-03-09 15:56:48 +00:00
Ref < InputEventKey > k ;
2021-06-17 22:03:09 +00:00
k . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
2022-12-10 23:21:22 +00:00
Key keycode = KeyMappingWindows : : get_keysym ( MapVirtualKey ( ( ke . lParam > > 16 ) & 0xFF , MAPVK_VSC_TO_VK ) ) ;
Key key_label = keycode ;
Key physical_keycode = KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ;
static BYTE keyboard_state [ 256 ] ;
memset ( keyboard_state , 0 , 256 ) ;
wchar_t chars [ 256 ] = { } ;
UINT extended_code = MapVirtualKey ( ( ke . lParam > > 16 ) & 0xFF , MAPVK_VSC_TO_VK_EX ) ;
2023-01-25 07:49:14 +00:00
if ( ! ( ke . lParam & ( 1 < < 24 ) ) & & ToUnicodeEx ( extended_code , ( ke . lParam > > 16 ) & 0xFF , keyboard_state , chars , 255 , 4 , GetKeyboardLayout ( 0 ) ) > 0 ) {
2022-12-10 23:21:22 +00:00
String keysym = String : : utf16 ( ( char16_t * ) chars , 255 ) ;
if ( ! keysym . is_empty ( ) ) {
key_label = fix_key_label ( keysym [ 0 ] , keycode ) ;
}
}
2020-03-09 15:56:48 +00:00
k - > set_window_id ( ke . window_id ) ;
2023-02-14 07:05:58 +00:00
if ( keycode ! = Key : : SHIFT ) {
k - > set_shift_pressed ( ke . shift ) ;
}
if ( keycode ! = Key : : ALT ) {
k - > set_alt_pressed ( ke . alt ) ;
}
if ( keycode ! = Key : : CTRL ) {
k - > set_ctrl_pressed ( ke . control ) ;
}
if ( keycode ! = Key : : META ) {
k - > set_meta_pressed ( ke . meta ) ;
}
2020-03-09 15:56:48 +00:00
k - > set_pressed ( true ) ;
2022-12-10 23:21:22 +00:00
k - > set_keycode ( keycode ) ;
k - > set_physical_keycode ( physical_keycode ) ;
k - > set_key_label ( key_label ) ;
k - > set_unicode ( fix_unicode ( unicode ) ) ;
2020-03-09 15:56:48 +00:00
if ( k - > get_unicode ( ) & & gr_mem ) {
2021-04-24 20:33:50 +00:00
k - > set_alt_pressed ( false ) ;
k - > set_ctrl_pressed ( false ) ;
2020-03-09 15:56:48 +00:00
}
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( k ) ;
2021-09-10 13:44:44 +00:00
} else {
// Do nothing.
2020-03-09 15:56:48 +00:00
}
} break ;
case WM_KEYUP :
case WM_KEYDOWN : {
Ref < InputEventKey > k ;
2021-06-17 22:03:09 +00:00
k . instantiate ( ) ;
2020-03-09 15:56:48 +00:00
k - > set_window_id ( ke . window_id ) ;
k - > set_pressed ( ke . uMsg = = WM_KEYDOWN ) ;
2022-12-10 23:21:22 +00:00
Key keycode = KeyMappingWindows : : get_keysym ( ke . wParam ) ;
2020-03-09 15:56:48 +00:00
if ( ( ke . lParam & ( 1 < < 24 ) ) & & ( ke . wParam = = VK_RETURN ) ) {
2021-09-10 13:44:44 +00:00
// Special case for Numpad Enter key.
2022-12-10 23:21:22 +00:00
keycode = Key : : KP_ENTER ;
}
Key key_label = keycode ;
Key physical_keycode = KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ;
2023-08-03 13:18:26 +00:00
KeyLocation location = KeyMappingWindows : : get_location ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ;
2022-12-10 23:21:22 +00:00
static BYTE keyboard_state [ 256 ] ;
memset ( keyboard_state , 0 , 256 ) ;
wchar_t chars [ 256 ] = { } ;
UINT extended_code = MapVirtualKey ( ( ke . lParam > > 16 ) & 0xFF , MAPVK_VSC_TO_VK_EX ) ;
2023-01-25 07:49:14 +00:00
if ( ! ( ke . lParam & ( 1 < < 24 ) ) & & ToUnicodeEx ( extended_code , ( ke . lParam > > 16 ) & 0xFF , keyboard_state , chars , 255 , 4 , GetKeyboardLayout ( 0 ) ) > 0 ) {
2022-12-10 23:21:22 +00:00
String keysym = String : : utf16 ( ( char16_t * ) chars , 255 ) ;
if ( ! keysym . is_empty ( ) ) {
key_label = fix_key_label ( keysym [ 0 ] , keycode ) ;
}
2020-03-09 15:56:48 +00:00
}
2023-02-14 07:05:58 +00:00
if ( keycode ! = Key : : SHIFT ) {
k - > set_shift_pressed ( ke . shift ) ;
}
if ( keycode ! = Key : : ALT ) {
k - > set_alt_pressed ( ke . alt ) ;
}
if ( keycode ! = Key : : CTRL ) {
k - > set_ctrl_pressed ( ke . control ) ;
}
if ( keycode ! = Key : : META ) {
k - > set_meta_pressed ( ke . meta ) ;
}
2022-12-10 23:21:22 +00:00
k - > set_keycode ( keycode ) ;
k - > set_physical_keycode ( physical_keycode ) ;
2023-08-03 13:18:26 +00:00
k - > set_location ( location ) ;
2022-12-10 23:21:22 +00:00
k - > set_key_label ( key_label ) ;
2020-03-09 15:56:48 +00:00
if ( i + 1 < key_event_pos & & key_event_buffer [ i + 1 ] . uMsg = = WM_CHAR ) {
2020-09-03 11:22:16 +00:00
char32_t unicode = key_event_buffer [ i + 1 ] . wParam ;
static char32_t prev_wck = 0 ;
if ( ( unicode & 0xfffffc00 ) = = 0xd800 ) {
if ( prev_wck ! = 0 ) {
ERR_PRINT ( " invalid utf16 surrogate input " ) ;
}
prev_wck = unicode ;
break ; // Skip surrogate.
} else if ( ( unicode & 0xfffffc00 ) = = 0xdc00 ) {
if ( prev_wck = = 0 ) {
ERR_PRINT ( " invalid utf16 surrogate input " ) ;
break ; // Skip invalid surrogate.
}
unicode = ( prev_wck < < 10UL ) + unicode - ( ( 0xd800 < < 10UL ) + 0xdc00 - 0x10000 ) ;
prev_wck = 0 ;
} else {
prev_wck = 0 ;
}
2022-12-10 23:21:22 +00:00
k - > set_unicode ( fix_unicode ( unicode ) ) ;
2020-03-09 15:56:48 +00:00
}
if ( k - > get_unicode ( ) & & gr_mem ) {
2021-04-24 20:33:50 +00:00
k - > set_alt_pressed ( false ) ;
k - > set_ctrl_pressed ( false ) ;
2020-03-09 15:56:48 +00:00
}
k - > set_echo ( ( ke . uMsg = = WM_KEYDOWN & & ( ke . lParam & ( 1 < < 30 ) ) ) ) ;
2021-08-12 22:31:16 +00:00
Input : : get_singleton ( ) - > parse_input_event ( k ) ;
2020-03-09 15:56:48 +00:00
} break ;
}
}
key_event_pos = 0 ;
}
2020-05-19 20:34:26 +00:00
void DisplayServerWindows : : _update_tablet_ctx ( const String & p_old_driver , const String & p_new_driver ) {
2021-08-09 20:13:42 +00:00
for ( KeyValue < WindowID , WindowData > & E : windows ) {
WindowData & wd = E . value ;
2020-05-22 07:44:47 +00:00
wd . block_mm = false ;
2020-05-19 20:34:26 +00:00
if ( ( p_old_driver = = " wintab " ) & & wintab_available & & wd . wtctx ) {
wintab_WTEnable ( wd . wtctx , false ) ;
wintab_WTClose ( wd . wtctx ) ;
2024-03-12 14:40:40 +00:00
wd . wtctx = nullptr ;
2020-05-19 20:34:26 +00:00
}
if ( ( p_new_driver = = " wintab " ) & & wintab_available ) {
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wd . wtlc ) ;
wd . wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-19 09:58:24 +00:00
wd . wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wd . wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2020-05-19 20:34:26 +00:00
wd . wtlc . lcPktMode = 0 ;
wd . wtlc . lcOutOrgX = 0 ;
wd . wtlc . lcOutExtX = wd . wtlc . lcInExtX ;
wd . wtlc . lcOutOrgY = 0 ;
wd . wtlc . lcOutExtY = - wd . wtlc . lcInExtY ;
wd . wtctx = wintab_WTOpen ( wd . hWnd , & wd . wtlc , false ) ;
if ( wd . wtctx ) {
wintab_WTEnable ( wd . wtctx , true ) ;
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + wd . wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
wd . min_pressure = int ( pressure . axMin ) ;
wd . max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + wd . wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
wd . tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
wintab_WTEnable ( wd . wtctx , true ) ;
} else {
print_verbose ( " WinTab context creation failed. " ) ;
}
}
}
}
2023-01-04 22:00:02 +00:00
DisplayServer : : WindowID DisplayServerWindows : : _create_window ( WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Rect2i & p_rect ) {
2020-03-09 15:56:48 +00:00
DWORD dwExStyle ;
DWORD dwStyle ;
2024-02-14 14:42:26 +00:00
_get_window_style ( window_id_counter = = MAIN_WINDOW_ID , ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) , p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN , p_flags & WINDOW_FLAG_BORDERLESS_BIT , ! ( p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT ) , p_mode = = WINDOW_MODE_MAXIMIZED , false , ( p_flags & WINDOW_FLAG_NO_FOCUS_BIT ) | ( p_flags & WINDOW_FLAG_POPUP ) , dwStyle , dwExStyle ) ;
2020-03-14 16:06:39 +00:00
2020-03-09 15:56:48 +00:00
RECT WindowRect ;
WindowRect . left = p_rect . position . x ;
WindowRect . right = p_rect . position . x + p_rect . size . x ;
WindowRect . top = p_rect . position . y ;
WindowRect . bottom = p_rect . position . y + p_rect . size . y ;
2023-01-04 22:00:02 +00:00
int rq_screen = get_screen_from_rect ( p_rect ) ;
if ( rq_screen < 0 ) {
rq_screen = get_primary_screen ( ) ; // Requested window rect is outside any screen bounds.
}
2022-01-28 09:19:53 +00:00
if ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2023-01-04 22:00:02 +00:00
Rect2i screen_rect = Rect2i ( screen_get_position ( rq_screen ) , screen_get_size ( rq_screen ) ) ;
2022-12-08 18:34:15 +00:00
WindowRect . left = screen_rect . position . x ;
WindowRect . right = screen_rect . position . x + screen_rect . size . x ;
WindowRect . top = screen_rect . position . y ;
WindowRect . bottom = screen_rect . position . y + screen_rect . size . y ;
} else {
2023-01-04 22:00:02 +00:00
Rect2i srect = screen_get_usable_rect ( rq_screen ) ;
Point2i wpos = p_rect . position ;
if ( srect ! = Rect2i ( ) ) {
2024-03-03 11:49:08 +00:00
wpos = wpos . clamp ( srect . position , srect . position + srect . size - p_rect . size / 3 ) ;
2020-07-03 01:08:06 +00:00
}
2022-12-08 18:34:15 +00:00
WindowRect . left = wpos . x ;
WindowRect . right = wpos . x + p_rect . size . x ;
WindowRect . top = wpos . y ;
WindowRect . bottom = wpos . y + p_rect . size . y ;
2020-07-03 01:08:06 +00:00
}
2023-01-04 22:00:02 +00:00
Point2i offset = _get_screens_origin ( ) ;
WindowRect . left + = offset . x ;
WindowRect . right + = offset . x ;
WindowRect . top + = offset . y ;
WindowRect . bottom + = offset . y ;
2023-07-04 10:09:26 +00:00
if ( p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
AdjustWindowRectEx ( & WindowRect , dwStyle , FALSE , dwExStyle ) ;
}
2020-03-09 15:56:48 +00:00
WindowID id = window_id_counter ;
{
2020-06-27 16:26:34 +00:00
WindowData & wd = windows [ id ] ;
2020-03-09 15:56:48 +00:00
wd . hWnd = CreateWindowExW (
dwExStyle ,
L " Engine " , L " " ,
2020-03-14 19:56:37 +00:00
dwStyle ,
2020-03-12 12:37:40 +00:00
WindowRect . left ,
WindowRect . top ,
2020-03-09 15:56:48 +00:00
WindowRect . right - WindowRect . left ,
WindowRect . bottom - WindowRect . top ,
2022-05-08 23:25:49 +00:00
nullptr ,
nullptr ,
hInstance ,
// tunnel the WindowData we need to handle creation message
// lifetime is ensured because we are still on the stack when this is
// processed in the window proc
reinterpret_cast < void * > ( & wd ) ) ;
2020-03-09 15:56:48 +00:00
if ( ! wd . hWnd ) {
2020-04-01 23:20:12 +00:00
MessageBoxW ( nullptr , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2020-06-27 16:26:34 +00:00
windows . erase ( id ) ;
2022-05-08 23:25:49 +00:00
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create Windows OS window. " ) ;
2020-03-09 15:56:48 +00:00
}
2022-07-19 09:58:02 +00:00
if ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
wd . fullscreen = true ;
if ( p_mode = = WINDOW_MODE_FULLSCREEN ) {
wd . multiwindow_fs = true ;
}
}
2022-01-28 09:19:53 +00:00
if ( p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-12-31 11:49:03 +00:00
wd . pre_fs_valid = true ;
}
2020-03-09 15:56:48 +00:00
2022-09-02 18:44:58 +00:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 08:24:48 +00:00
BOOL value = is_dark_mode ( ) ;
2024-01-24 04:44:40 +00:00
: : DwmSetWindowAttribute ( wd . hWnd , use_legacy_dark_mode_before_20H1 ? DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 : DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
2022-08-29 08:24:48 +00:00
}
2023-12-19 11:48:02 +00:00
# ifdef RD_ENABLED
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
2023-12-19 11:48:02 +00:00
union {
2021-09-10 13:44:44 +00:00
# ifdef VULKAN_ENABLED
2023-12-19 17:57:56 +00:00
RenderingContextDriverVulkanWindows : : WindowPlatformData vulkan ;
2023-12-19 11:48:02 +00:00
# endif
# ifdef D3D12_ENABLED
2023-12-19 17:57:56 +00:00
RenderingContextDriverD3D12 : : WindowPlatformData d3d12 ;
2023-12-19 11:48:02 +00:00
# endif
} wpd ;
# ifdef VULKAN_ENABLED
if ( rendering_driver = = " vulkan " ) {
wpd . vulkan . window = wd . hWnd ;
wpd . vulkan . instance = hInstance ;
2020-03-09 15:56:48 +00:00
}
# endif
2023-01-09 15:56:16 +00:00
# ifdef D3D12_ENABLED
2023-12-19 11:48:02 +00:00
if ( rendering_driver = = " d3d12 " ) {
wpd . d3d12 . window = wd . hWnd ;
}
# endif
2023-12-19 17:57:56 +00:00
if ( rendering_context - > window_create ( id , & wpd ) ! = OK ) {
ERR_PRINT ( vformat ( " Failed to create %s window. " , rendering_driver ) ) ;
memdelete ( rendering_context ) ;
rendering_context = nullptr ;
2023-01-09 15:56:16 +00:00
windows . erase ( id ) ;
2024-01-05 17:27:10 +00:00
return INVALID_WINDOW_ID ;
2023-01-09 15:56:16 +00:00
}
2023-12-19 17:57:56 +00:00
rendering_context - > window_set_size ( id , WindowRect . right - WindowRect . left , WindowRect . bottom - WindowRect . top ) ;
rendering_context - > window_set_vsync_mode ( id , p_vsync_mode ) ;
2023-01-09 15:56:16 +00:00
wd . context_created = true ;
}
# endif
2021-10-26 15:18:39 +00:00
# ifdef GLES3_ENABLED
2021-11-12 12:49:49 +00:00
if ( gl_manager_native ) {
if ( gl_manager_native - > window_create ( id , wd . hWnd , hInstance , WindowRect . right - WindowRect . left , WindowRect . bottom - WindowRect . top ) ! = OK ) {
memdelete ( gl_manager_native ) ;
gl_manager_native = nullptr ;
windows . erase ( id ) ;
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create an OpenGL window. " ) ;
}
window_set_vsync_mode ( p_vsync_mode , id ) ;
}
if ( gl_manager_angle ) {
if ( gl_manager_angle - > window_create ( id , nullptr , wd . hWnd , WindowRect . right - WindowRect . left , WindowRect . bottom - WindowRect . top ) ! = OK ) {
memdelete ( gl_manager_angle ) ;
gl_manager_angle = nullptr ;
2022-05-08 23:25:49 +00:00
windows . erase ( id ) ;
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create an OpenGL window. " ) ;
}
2022-11-15 22:14:21 +00:00
window_set_vsync_mode ( p_vsync_mode , id ) ;
2020-11-18 18:11:30 +00:00
}
# endif
2020-03-09 15:56:48 +00:00
RegisterTouchWindow ( wd . hWnd , 0 ) ;
DragAcceptFiles ( wd . hWnd , true ) ;
2021-02-17 14:07:56 +00:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available ) {
2020-05-05 11:16:02 +00:00
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wd . wtlc ) ;
wd . wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-19 09:58:24 +00:00
wd . wtlc . lcPktData = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION ;
wd . wtlc . lcMoveMask = PK_STATUS | PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE ;
2020-05-05 11:16:02 +00:00
wd . wtlc . lcPktMode = 0 ;
wd . wtlc . lcOutOrgX = 0 ;
wd . wtlc . lcOutExtX = wd . wtlc . lcInExtX ;
wd . wtlc . lcOutOrgY = 0 ;
wd . wtlc . lcOutExtY = - wd . wtlc . lcInExtY ;
wd . wtctx = wintab_WTOpen ( wd . hWnd , & wd . wtlc , false ) ;
if ( wd . wtctx ) {
wintab_WTEnable ( wd . wtctx , true ) ;
AXIS pressure ;
if ( wintab_WTInfo ( WTI_DEVICES + wd . wtlc . lcDevice , DVC_NPRESSURE , & pressure ) ) {
wd . min_pressure = int ( pressure . axMin ) ;
wd . max_pressure = int ( pressure . axMax ) ;
}
AXIS orientation [ 3 ] ;
if ( wintab_WTInfo ( WTI_DEVICES + wd . wtlc . lcDevice , DVC_ORIENTATION , & orientation ) ) {
wd . tilt_supported = orientation [ 0 ] . axResolution & & orientation [ 1 ] . axResolution ;
}
} else {
2020-05-07 12:51:19 +00:00
print_verbose ( " WinTab context creation failed. " ) ;
2020-05-05 11:16:02 +00:00
}
} else {
2024-03-12 14:40:40 +00:00
wd . wtctx = nullptr ;
2020-05-05 11:16:02 +00:00
}
2022-09-15 07:05:41 +00:00
if ( p_mode = = WINDOW_MODE_MAXIMIZED ) {
wd . maximized = true ;
wd . minimized = false ;
}
if ( p_mode = = WINDOW_MODE_MINIMIZED ) {
wd . maximized = false ;
wd . minimized = true ;
}
2020-05-05 11:16:02 +00:00
wd . last_pressure = 0 ;
wd . last_pressure_update = 0 ;
wd . last_tilt = Vector2 ( ) ;
2023-12-07 20:51:53 +00:00
IPropertyStore * prop_store ;
HRESULT hr = SHGetPropertyStoreForWindow ( wd . hWnd , IID_IPropertyStore , ( void * * ) & prop_store ) ;
if ( hr = = S_OK ) {
PROPVARIANT val ;
String appname ;
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
appname = " Godot.GodotEditor. " + String ( VERSION_BRANCH ) ;
} else {
String name = GLOBAL_GET ( " application/config/name " ) ;
String version = GLOBAL_GET ( " application/config/version " ) ;
if ( version . is_empty ( ) ) {
version = " 0 " ;
}
String clean_app_name = name . to_pascal_case ( ) ;
for ( int i = 0 ; i < clean_app_name . length ( ) ; i + + ) {
if ( ! is_ascii_alphanumeric_char ( clean_app_name [ i ] ) & & clean_app_name [ i ] ! = ' _ ' & & clean_app_name [ i ] ! = ' . ' ) {
clean_app_name [ i ] = ' _ ' ;
}
}
clean_app_name = clean_app_name . substr ( 0 , 120 - version . length ( ) ) . trim_suffix ( " . " ) ;
appname = " Godot. " + clean_app_name + " . " + version ;
}
InitPropVariantFromString ( ( PCWSTR ) appname . utf16 ( ) . get_data ( ) , & val ) ;
prop_store - > SetValue ( PKEY_AppUserModel_ID , val ) ;
prop_store - > Release ( ) ;
}
2021-09-10 13:44:44 +00:00
// IME.
2020-03-09 15:56:48 +00:00
wd . im_himc = ImmGetContext ( wd . hWnd ) ;
2024-03-12 14:40:40 +00:00
ImmAssociateContext ( wd . hWnd , ( HIMC ) nullptr ) ;
2020-03-09 15:56:48 +00:00
wd . im_position = Vector2 ( ) ;
2022-05-08 23:25:49 +00:00
2023-02-02 20:30:09 +00:00
if ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN | | p_mode = = WINDOW_MODE_MAXIMIZED ) {
RECT r ;
GetClientRect ( wd . hWnd , & r ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & r . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & r . right ) ;
wd . last_pos = Point2i ( r . left , r . top ) - _get_screens_origin ( ) ;
wd . width = r . right - r . left ;
wd . height = r . bottom - r . top ;
} else {
wd . last_pos = p_rect . position ;
wd . width = p_rect . size . width ;
wd . height = p_rect . size . height ;
}
2020-03-09 15:56:48 +00:00
2024-02-14 14:42:26 +00:00
// Set size of maximized borderless window (by default it covers the entire screen).
if ( p_mode = = WINDOW_MODE_MAXIMIZED & & ( p_flags & WINDOW_FLAG_BORDERLESS_BIT ) ) {
Rect2i srect = screen_get_usable_rect ( rq_screen ) ;
SetWindowPos ( wd . hWnd , HWND_TOP , srect . position . x , srect . position . y , srect . size . width , srect . size . height , SWP_NOZORDER | SWP_NOACTIVATE ) ;
}
2020-03-09 15:56:48 +00:00
window_id_counter + + ;
}
return id ;
}
2021-09-10 13:44:44 +00:00
// WinTab API.
2020-05-05 11:16:02 +00:00
bool DisplayServerWindows : : wintab_available = false ;
WTOpenPtr DisplayServerWindows : : wintab_WTOpen = nullptr ;
WTClosePtr DisplayServerWindows : : wintab_WTClose = nullptr ;
WTInfoPtr DisplayServerWindows : : wintab_WTInfo = nullptr ;
WTPacketPtr DisplayServerWindows : : wintab_WTPacket = nullptr ;
WTEnablePtr DisplayServerWindows : : wintab_WTEnable = nullptr ;
2022-08-29 08:24:48 +00:00
// UXTheme API.
2022-09-02 18:44:58 +00:00
bool DisplayServerWindows : : dark_title_available = false ;
2024-01-24 04:44:40 +00:00
bool DisplayServerWindows : : use_legacy_dark_mode_before_20H1 = false ;
2022-08-29 08:24:48 +00:00
bool DisplayServerWindows : : ux_theme_available = false ;
ShouldAppsUseDarkModePtr DisplayServerWindows : : ShouldAppsUseDarkMode = nullptr ;
GetImmersiveColorFromColorSetExPtr DisplayServerWindows : : GetImmersiveColorFromColorSetEx = nullptr ;
GetImmersiveColorTypeFromNamePtr DisplayServerWindows : : GetImmersiveColorTypeFromName = nullptr ;
GetImmersiveUserColorSetPreferencePtr DisplayServerWindows : : GetImmersiveUserColorSetPreference = nullptr ;
2021-09-10 13:44:44 +00:00
// Windows Ink API.
2020-05-19 20:34:26 +00:00
bool DisplayServerWindows : : winink_available = false ;
2020-03-09 15:56:48 +00:00
GetPointerTypePtr DisplayServerWindows : : win8p_GetPointerType = nullptr ;
GetPointerPenInfoPtr DisplayServerWindows : : win8p_GetPointerPenInfo = nullptr ;
2023-02-27 18:36:13 +00:00
LogicalToPhysicalPointForPerMonitorDPIPtr DisplayServerWindows : : win81p_LogicalToPhysicalPointForPerMonitorDPI = nullptr ;
2023-08-08 08:31:56 +00:00
PhysicalToLogicalPointForPerMonitorDPIPtr DisplayServerWindows : : win81p_PhysicalToLogicalPointForPerMonitorDPI = nullptr ;
2020-03-09 15:56:48 +00:00
2024-01-09 17:26:23 +00:00
Vector2i _get_device_ids ( const String & p_device_name ) {
if ( p_device_name . is_empty ( ) ) {
return Vector2i ( ) ;
}
REFCLSID clsid = CLSID_WbemLocator ; // Unmarshaler CLSID
REFIID uuid = IID_IWbemLocator ; // Interface UUID
2024-03-12 14:40:40 +00:00
IWbemLocator * wbemLocator = nullptr ; // to get the services
IWbemServices * wbemServices = nullptr ; // to get the class
IEnumWbemClassObject * iter = nullptr ;
2024-01-09 17:26:23 +00:00
IWbemClassObject * pnpSDriverObject [ 1 ] ; // contains driver name, version, etc.
2024-03-12 14:40:40 +00:00
HRESULT hr = CoCreateInstance ( clsid , nullptr , CLSCTX_INPROC_SERVER , uuid , ( LPVOID * ) & wbemLocator ) ;
2024-01-09 17:26:23 +00:00
if ( hr ! = S_OK ) {
return Vector2i ( ) ;
}
BSTR resource_name = SysAllocString ( L " root \\ CIMV2 " ) ;
2024-03-12 14:40:40 +00:00
hr = wbemLocator - > ConnectServer ( resource_name , nullptr , nullptr , nullptr , 0 , nullptr , nullptr , & wbemServices ) ;
2024-01-09 17:26:23 +00:00
SysFreeString ( resource_name ) ;
SAFE_RELEASE ( wbemLocator ) // from now on, use `wbemServices`
if ( hr ! = S_OK ) {
SAFE_RELEASE ( wbemServices )
return Vector2i ( ) ;
}
Vector2i ids ;
const String gpu_device_class_query = vformat ( " SELECT * FROM Win32_PnPSignedDriver WHERE DeviceName = \" %s \" " , p_device_name ) ;
BSTR query = SysAllocString ( ( const WCHAR * ) gpu_device_class_query . utf16 ( ) . get_data ( ) ) ;
BSTR query_lang = SysAllocString ( L " WQL " ) ;
2024-03-12 14:40:40 +00:00
hr = wbemServices - > ExecQuery ( query_lang , query , WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY , nullptr , & iter ) ;
2024-01-09 17:26:23 +00:00
SysFreeString ( query_lang ) ;
SysFreeString ( query ) ;
if ( hr = = S_OK ) {
ULONG resultCount ;
hr = iter - > Next ( 5000 , 1 , pnpSDriverObject , & resultCount ) ; // Get exactly 1. Wait max 5 seconds.
if ( hr = = S_OK & & resultCount > 0 ) {
VARIANT did ;
VariantInit ( & did ) ;
BSTR object_name = SysAllocString ( L " DeviceID " ) ;
2024-03-12 14:40:40 +00:00
hr = pnpSDriverObject [ 0 ] - > Get ( object_name , 0 , & did , nullptr , nullptr ) ;
2024-01-09 17:26:23 +00:00
SysFreeString ( object_name ) ;
if ( hr = = S_OK ) {
String device_id = String ( V_BSTR ( & did ) ) ;
ids . x = device_id . get_slice ( " & " , 0 ) . lstrip ( " PCI \\ VEN_ " ) . hex_to_int ( ) ;
ids . y = device_id . get_slice ( " & " , 1 ) . lstrip ( " DEV_ " ) . hex_to_int ( ) ;
}
for ( ULONG i = 0 ; i < resultCount ; i + + ) {
SAFE_RELEASE ( pnpSDriverObject [ i ] )
}
}
}
SAFE_RELEASE ( wbemServices )
SAFE_RELEASE ( iter )
return ids ;
}
2020-03-09 15:56:48 +00:00
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 ;
2022-08-29 08:24:48 +00:00
bool DisplayServerWindows : : is_dark_mode_supported ( ) const {
2022-12-27 02:15:33 +00:00
return ux_theme_available ;
2022-08-29 08:24:48 +00:00
}
bool DisplayServerWindows : : is_dark_mode ( ) const {
return ux_theme_available & & ShouldAppsUseDarkMode ( ) ;
}
Color DisplayServerWindows : : get_accent_color ( ) const {
if ( ! ux_theme_available ) {
return Color ( 0 , 0 , 0 , 0 ) ;
}
int argb = GetImmersiveColorFromColorSetEx ( ( UINT ) GetImmersiveUserColorSetPreference ( false , false ) , GetImmersiveColorTypeFromName ( L " ImmersiveSystemAccent " ) , false , 0 ) ;
return Color ( ( argb & 0xFF ) / 255.f , ( ( argb & 0xFF00 ) > > 8 ) / 255.f , ( ( argb & 0xFF0000 ) > > 16 ) / 255.f , ( ( argb & 0xFF000000 ) > > 24 ) / 255.f ) ;
}
2024-01-19 18:46:26 +00:00
Color DisplayServerWindows : : get_base_color ( ) const {
if ( ! ux_theme_available ) {
return Color ( 0 , 0 , 0 , 0 ) ;
}
int argb = GetImmersiveColorFromColorSetEx ( ( UINT ) GetImmersiveUserColorSetPreference ( false , false ) , GetImmersiveColorTypeFromName ( ShouldAppsUseDarkMode ( ) ? L " ImmersiveDarkChromeMediumLow " : L " ImmersiveLightChromeMediumLow " ) , false , 0 ) ;
return Color ( ( argb & 0xFF ) / 255.f , ( ( argb & 0xFF00 ) > > 8 ) / 255.f , ( ( argb & 0xFF0000 ) > > 16 ) / 255.f , ( ( argb & 0xFF000000 ) > > 24 ) / 255.f ) ;
}
void DisplayServerWindows : : set_system_theme_change_callback ( const Callable & p_callable ) {
system_theme_changed = p_callable ;
}
2021-02-17 14:07:56 +00:00
int DisplayServerWindows : : tablet_get_driver_count ( ) const {
return tablet_drivers . size ( ) ;
}
String DisplayServerWindows : : tablet_get_driver_name ( int p_driver ) const {
if ( p_driver < 0 | | p_driver > = tablet_drivers . size ( ) ) {
return " " ;
} else {
return tablet_drivers [ p_driver ] ;
}
}
String DisplayServerWindows : : tablet_get_current_driver ( ) const {
return tablet_driver ;
}
void DisplayServerWindows : : tablet_set_current_driver ( const String & p_driver ) {
if ( tablet_get_driver_count ( ) = = 0 ) {
return ;
}
bool found = false ;
for ( int i = 0 ; i < tablet_get_driver_count ( ) ; i + + ) {
if ( p_driver = = tablet_get_driver_name ( i ) ) {
found = true ;
}
}
if ( found ) {
_update_tablet_ctx ( tablet_driver , p_driver ) ;
tablet_driver = p_driver ;
} else {
ERR_PRINT ( " Unknown tablet driver " + p_driver + " . " ) ;
}
}
2023-01-04 22:00:02 +00:00
DisplayServerWindows : : DisplayServerWindows ( const String & p_rendering_driver , WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Vector2i * p_position , const Vector2i & p_resolution , int p_screen , Error & r_error ) {
2022-12-10 23:21:22 +00:00
KeyMappingWindows : : initialize ( ) ;
2020-03-09 15:56:48 +00:00
drop_events = false ;
key_event_pos = 0 ;
alt_mem = false ;
gr_mem = false ;
shift_mem = false ;
control_mem = false ;
meta_mem = false ;
2022-04-05 10:40:26 +00:00
hInstance = static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) - > get_hinstance ( ) ;
2020-03-09 15:56:48 +00:00
pressrc = 0 ;
old_invalid = true ;
mouse_mode = MOUSE_MODE_VISIBLE ;
2021-09-26 23:07:10 +00:00
rendering_driver = p_rendering_driver ;
2021-11-04 12:33:37 +00:00
// Init TTS
2023-05-16 11:18:12 +00:00
bool tts_enabled = GLOBAL_GET ( " audio/general/text_to_speech " ) ;
if ( tts_enabled ) {
tts = memnew ( TTS_Windows ) ;
}
2024-03-07 18:42:24 +00:00
native_menu = memnew ( NativeMenuWindows ) ;
2021-11-04 12:33:37 +00:00
2022-08-03 16:29:07 +00:00
// Enforce default keep screen on value.
2022-08-06 20:10:24 +00:00
screen_set_keep_on ( GLOBAL_GET ( " display/window/energy_saving/keep_screen_on " ) ) ;
2022-08-03 16:29:07 +00:00
2022-09-02 18:44:58 +00:00
// Load Windows version info.
OSVERSIONINFOW os_ver ;
ZeroMemory ( & os_ver , sizeof ( OSVERSIONINFOW ) ) ;
os_ver . dwOSVersionInfoSize = sizeof ( OSVERSIONINFOW ) ;
HMODULE nt_lib = LoadLibraryW ( L " ntdll.dll " ) ;
if ( nt_lib ) {
RtlGetVersionPtr RtlGetVersion = ( RtlGetVersionPtr ) GetProcAddress ( nt_lib , " RtlGetVersion " ) ;
if ( RtlGetVersion ) {
RtlGetVersion ( & os_ver ) ;
}
FreeLibrary ( nt_lib ) ;
}
// Load UXTheme.
2022-08-29 08:24:48 +00:00
HMODULE ux_theme_lib = LoadLibraryW ( L " uxtheme.dll " ) ;
if ( ux_theme_lib ) {
ShouldAppsUseDarkMode = ( ShouldAppsUseDarkModePtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 132 ) ) ;
GetImmersiveColorFromColorSetEx = ( GetImmersiveColorFromColorSetExPtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 95 ) ) ;
GetImmersiveColorTypeFromName = ( GetImmersiveColorTypeFromNamePtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 96 ) ) ;
GetImmersiveUserColorSetPreference = ( GetImmersiveUserColorSetPreferencePtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 98 ) ) ;
2024-03-15 11:59:47 +00:00
if ( os_ver . dwBuildNumber > = 17763 ) {
AllowDarkModeForAppPtr AllowDarkModeForApp = nullptr ;
SetPreferredAppModePtr SetPreferredAppMode = nullptr ;
FlushMenuThemesPtr FlushMenuThemes = nullptr ;
if ( os_ver . dwBuildNumber < 18362 ) {
AllowDarkModeForApp = ( AllowDarkModeForAppPtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 135 ) ) ;
} else {
SetPreferredAppMode = ( SetPreferredAppModePtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 135 ) ) ;
FlushMenuThemes = ( FlushMenuThemesPtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 136 ) ) ;
}
RefreshImmersiveColorPolicyStatePtr RefreshImmersiveColorPolicyState = ( RefreshImmersiveColorPolicyStatePtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 104 ) ) ;
if ( ShouldAppsUseDarkMode ) {
bool dark_mode = ShouldAppsUseDarkMode ( ) ;
if ( SetPreferredAppMode ) {
SetPreferredAppMode ( dark_mode ? APPMODE_ALLOWDARK : APPMODE_DEFAULT ) ;
} else if ( AllowDarkModeForApp ) {
AllowDarkModeForApp ( dark_mode ) ;
}
if ( RefreshImmersiveColorPolicyState ) {
RefreshImmersiveColorPolicyState ( ) ;
}
if ( FlushMenuThemes ) {
FlushMenuThemes ( ) ;
}
}
}
2022-08-29 08:24:48 +00:00
2022-12-27 02:15:33 +00:00
ux_theme_available = ShouldAppsUseDarkMode & & GetImmersiveColorFromColorSetEx & & GetImmersiveColorTypeFromName & & GetImmersiveUserColorSetPreference ;
2024-01-24 04:44:40 +00:00
if ( os_ver . dwBuildNumber > = 18363 ) {
2022-09-02 18:44:58 +00:00
dark_title_available = true ;
2024-01-24 04:44:40 +00:00
if ( os_ver . dwBuildNumber < 19041 ) {
use_legacy_dark_mode_before_20H1 = true ;
}
2022-09-02 18:44:58 +00:00
}
2022-08-29 08:24:48 +00:00
}
2023-11-10 11:17:20 +00:00
// Note: Windows Ink API for pen input, available on Windows 8+ only.
// Note: DPI conversion API, available on Windows 8.1+ 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 " ) ;
win81p_LogicalToPhysicalPointForPerMonitorDPI = ( LogicalToPhysicalPointForPerMonitorDPIPtr ) GetProcAddress ( user32_lib , " LogicalToPhysicalPointForPerMonitorDPI " ) ;
win81p_PhysicalToLogicalPointForPerMonitorDPI = ( PhysicalToLogicalPointForPerMonitorDPIPtr ) GetProcAddress ( user32_lib , " PhysicalToLogicalPointForPerMonitorDPI " ) ;
winink_available = win8p_GetPointerType & & win8p_GetPointerPenInfo ;
}
if ( winink_available ) {
tablet_drivers . push_back ( " winink " ) ;
}
2021-09-10 13:44:44 +00:00
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
2021-02-17 14:07:56 +00:00
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 " ) ;
}
2023-11-10 11:17:20 +00:00
tablet_drivers . push_back ( " dummy " ) ;
2021-02-17 14:07:56 +00:00
2020-03-09 15:56:48 +00:00
if ( OS : : get_singleton ( ) - > is_hidpi_allowed ( ) ) {
HMODULE Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
2020-04-01 23:20:12 +00:00
if ( Shcore ! = nullptr ) {
2020-03-09 15:56:48 +00:00
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 ) ;
}
}
}
2024-02-19 00:56:32 +00:00
HMODULE comctl32 = LoadLibraryW ( L " comctl32.dll " ) ;
if ( comctl32 ) {
typedef BOOL ( WINAPI * InitCommonControlsExPtr ) ( _In_ const INITCOMMONCONTROLSEX * picce ) ;
InitCommonControlsExPtr init_common_controls_ex = ( InitCommonControlsExPtr ) GetProcAddress ( comctl32 , " InitCommonControlsEx " ) ;
// Fails if the incorrect version was loaded. Probably not a big enough deal to print an error about.
if ( init_common_controls_ex ) {
INITCOMMONCONTROLSEX icc = { } ;
icc . dwICC = ICC_STANDARD_CLASSES ;
icc . dwSize = sizeof ( INITCOMMONCONTROLSEX ) ;
if ( ! init_common_controls_ex ( & icc ) ) {
WARN_PRINT ( " Unable to initialize Windows common controls. Native dialogs may not work properly. " ) ;
}
}
FreeLibrary ( comctl32 ) ;
}
2020-03-09 15:56:48 +00:00
memset ( & wc , 0 , sizeof ( WNDCLASSEXW ) ) ;
wc . cbSize = sizeof ( WNDCLASSEXW ) ;
2023-01-11 18:15:10 +00:00
wc . style = CS_OWNDC | CS_DBLCLKS ;
2020-03-09 15:56:48 +00:00
wc . lpfnWndProc = ( WNDPROC ) : : WndProc ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = 0 ;
2020-04-01 23:20:12 +00:00
wc . hInstance = hInstance ? hInstance : GetModuleHandle ( nullptr ) ;
wc . hIcon = LoadIcon ( nullptr , IDI_WINLOGO ) ;
2022-02-16 12:56:32 +00:00
wc . hCursor = nullptr ;
2020-04-01 23:20:12 +00:00
wc . hbrBackground = nullptr ;
wc . lpszMenuName = nullptr ;
2020-03-09 15:56:48 +00:00
wc . lpszClassName = L " Engine " ;
if ( ! RegisterClassExW ( & wc ) ) {
2022-12-10 23:21:22 +00:00
MessageBoxW ( nullptr , L " Failed To Register The Window Class. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2020-03-09 15:56:48 +00:00
r_error = ERR_UNAVAILABLE ;
return ;
}
2022-09-07 17:31:57 +00:00
_register_raw_input_devices ( INVALID_WINDOW_ID ) ;
2020-03-09 15:56:48 +00:00
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2020-03-09 15:56:48 +00:00
# if defined(VULKAN_ENABLED)
if ( rendering_driver = = " vulkan " ) {
2023-12-19 17:57:56 +00:00
rendering_context = memnew ( RenderingContextDriverVulkanWindows ) ;
2020-03-09 15:56:48 +00:00
}
2023-01-09 15:56:16 +00:00
# endif
# if defined(D3D12_ENABLED)
if ( rendering_driver = = " d3d12 " ) {
2023-12-19 17:57:56 +00:00
rendering_context = memnew ( RenderingContextDriverD3D12 ) ;
2023-12-19 11:48:02 +00:00
}
# endif
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
if ( rendering_context - > initialize ( ) ! = OK ) {
memdelete ( rendering_context ) ;
rendering_context = nullptr ;
2023-01-09 15:56:16 +00:00
r_error = ERR_UNAVAILABLE ;
return ;
}
}
2020-03-09 15:56:48 +00:00
# endif
2023-12-19 11:48:02 +00:00
// Init context and rendering device
2021-10-26 15:18:39 +00:00
# if defined(GLES3_ENABLED)
2021-09-10 13:44:44 +00:00
2023-12-09 18:47:46 +00:00
# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
// There's no native OpenGL drivers on Windows for ARM, switch to ANGLE over DX.
if ( rendering_driver = = " opengl3 " ) {
rendering_driver = " opengl3_angle " ;
}
2024-03-10 11:18:03 +00:00
# elif defined(EGL_STATIC)
2023-09-26 11:19:41 +00:00
bool fallback = GLOBAL_GET ( " rendering/gl_compatibility/fallback_to_angle " ) ;
if ( fallback & & ( rendering_driver = = " opengl3 " ) ) {
Dictionary gl_info = detect_wgl ( ) ;
bool force_angle = false ;
2024-01-09 17:26:23 +00:00
Vector2i device_id = _get_device_ids ( gl_info [ " name " ] ) ;
2023-09-26 11:19:41 +00:00
Array device_list = GLOBAL_GET ( " rendering/gl_compatibility/force_angle_on_devices " ) ;
for ( int i = 0 ; i < device_list . size ( ) ; i + + ) {
const Dictionary & device = device_list [ i ] ;
2024-01-09 17:26:23 +00:00
if ( device . has ( " vendor " ) & & device . has ( " name " ) ) {
const String & vendor = device [ " vendor " ] ;
const String & name = device [ " name " ] ;
if ( device_id ! = Vector2i ( ) & & vendor . begins_with ( " 0x " ) & & name . begins_with ( " 0x " ) & & device_id . x = = vendor . lstrip ( " 0x " ) . hex_to_int ( ) & & device_id . y = = name . lstrip ( " 0x " ) . hex_to_int ( ) ) {
// Check vendor/device IDs.
force_angle = true ;
break ;
} else if ( gl_info [ " vendor " ] . operator String ( ) . to_upper ( ) . contains ( vendor . to_upper ( ) ) & & ( name = = " * " | | gl_info [ " name " ] . operator String ( ) . to_upper ( ) . contains ( name . to_upper ( ) ) ) ) {
// Check vendor/device names.
force_angle = true ;
break ;
}
2023-09-26 11:19:41 +00:00
}
}
if ( force_angle | | ( gl_info [ " version " ] . operator int ( ) < 30003 ) ) {
2021-11-12 12:49:49 +00:00
WARN_PRINT ( " Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE. " ) ;
rendering_driver = " opengl3_angle " ;
}
}
2023-12-09 18:47:46 +00:00
# endif
2021-11-12 12:49:49 +00:00
if ( rendering_driver = = " opengl3 " ) {
gl_manager_native = memnew ( GLManagerNative_Windows ) ;
2020-11-18 18:11:30 +00:00
2021-11-12 12:49:49 +00:00
if ( gl_manager_native - > initialize ( ) ! = OK ) {
memdelete ( gl_manager_native ) ;
gl_manager_native = nullptr ;
r_error = ERR_UNAVAILABLE ;
return ;
}
2020-11-18 18:11:30 +00:00
2021-11-12 12:49:49 +00:00
RasterizerGLES3 : : make_current ( true ) ;
}
if ( rendering_driver = = " opengl3_angle " ) {
gl_manager_angle = memnew ( GLManagerANGLE_Windows ) ;
if ( gl_manager_angle - > initialize ( ) ! = OK ) {
memdelete ( gl_manager_angle ) ;
gl_manager_angle = nullptr ;
2020-11-18 18:11:30 +00:00
r_error = ERR_UNAVAILABLE ;
return ;
}
2021-11-12 12:49:49 +00:00
RasterizerGLES3 : : make_current ( false ) ;
2020-11-18 18:11:30 +00:00
}
# endif
2023-12-07 20:51:53 +00:00
String appname ;
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) ) {
appname = " Godot.GodotEditor. " + String ( VERSION_BRANCH ) ;
} else {
String name = GLOBAL_GET ( " application/config/name " ) ;
String version = GLOBAL_GET ( " application/config/version " ) ;
if ( version . is_empty ( ) ) {
version = " 0 " ;
}
String clean_app_name = name . to_pascal_case ( ) ;
for ( int i = 0 ; i < clean_app_name . length ( ) ; i + + ) {
if ( ! is_ascii_alphanumeric_char ( clean_app_name [ i ] ) & & clean_app_name [ i ] ! = ' _ ' & & clean_app_name [ i ] ! = ' . ' ) {
clean_app_name [ i ] = ' _ ' ;
}
}
clean_app_name = clean_app_name . substr ( 0 , 120 - version . length ( ) ) . trim_suffix ( " . " ) ;
appname = " Godot. " + clean_app_name + " . " + version ;
}
SetCurrentProcessExplicitAppUserModelID ( ( PCWSTR ) appname . utf16 ( ) . get_data ( ) ) ;
2022-03-16 14:48:53 +00:00
mouse_monitor = SetWindowsHookEx ( WH_MOUSE , : : MouseProc , nullptr , GetCurrentThreadId ( ) ) ;
2022-02-24 09:21:23 +00:00
2023-01-04 22:00:02 +00:00
Point2i window_position ;
2022-10-23 10:34:41 +00:00
if ( p_position ! = nullptr ) {
window_position = * p_position ;
2023-01-04 22:00:02 +00:00
} else {
if ( p_screen = = SCREEN_OF_MAIN_WINDOW ) {
p_screen = SCREEN_PRIMARY ;
}
2023-03-30 08:37:15 +00:00
Rect2i scr_rect = screen_get_usable_rect ( p_screen ) ;
window_position = scr_rect . position + ( scr_rect . size - p_resolution ) / 2 ;
2022-10-23 10:34:41 +00:00
}
2023-02-22 08:52:26 +00:00
WindowID main_window = _create_window ( p_mode , p_vsync_mode , p_flags , Rect2i ( window_position , p_resolution ) ) ;
2020-06-27 16:26:34 +00:00
ERR_FAIL_COND_MSG ( main_window = = INVALID_WINDOW_ID , " Failed to create main window. " ) ;
2022-11-28 02:38:47 +00:00
joypad = new JoypadWindows ( & windows [ MAIN_WINDOW_ID ] . hWnd ) ;
2020-03-09 15:56:48 +00:00
for ( int i = 0 ; i < WINDOW_FLAG_MAX ; i + + ) {
if ( p_flags & ( 1 < < i ) ) {
window_set_flag ( WindowFlags ( i ) , true , main_window ) ;
}
}
2020-08-21 07:39:30 +00:00
show_window ( MAIN_WINDOW_ID ) ;
2020-03-14 16:06:39 +00:00
2023-12-19 11:48:02 +00:00
# if defined(RD_ENABLED)
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
2023-12-19 11:48:02 +00:00
rendering_device = memnew ( RenderingDevice ) ;
2023-12-19 17:57:56 +00:00
rendering_device - > initialize ( rendering_context , MAIN_WINDOW_ID ) ;
rendering_device - > screen_create ( MAIN_WINDOW_ID ) ;
2023-01-09 15:56:16 +00:00
RendererCompositorRD : : make_current ( ) ;
}
# endif
2020-03-09 15:56:48 +00:00
2022-03-29 07:01:31 +00: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.
2020-03-09 15:56:48 +00:00
SetPriorityClass ( GetCurrentProcess ( ) , ABOVE_NORMAL_PRIORITY_CLASS ) ;
DWORD index = 0 ;
2022-12-10 23:21:22 +00:00
HANDLE handle = AvSetMmThreadCharacteristicsW ( L " Games " , & index ) ;
2022-02-16 12:56:32 +00:00
if ( handle ) {
2020-03-09 15:56:48 +00:00
AvSetMmThreadPriority ( handle , AVRT_PRIORITY_CRITICAL ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
// This is needed to make sure that background work does not starve the main thread.
2021-09-10 13:44:44 +00:00
// This is only setting the priority of this thread, not the whole process.
2020-03-09 15:56:48 +00:00
SetThreadPriority ( GetCurrentThread ( ) , THREAD_PRIORITY_TIME_CRITICAL ) ;
}
cursor_shape = CURSOR_ARROW ;
_update_real_mouse_position ( MAIN_WINDOW_ID ) ;
r_error = OK ;
2022-04-05 10:40:26 +00:00
static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) - > set_main_window ( windows [ MAIN_WINDOW_ID ] . hWnd ) ;
2020-04-28 13:19:37 +00:00
Input : : get_singleton ( ) - > set_event_dispatch_function ( _dispatch_input_events ) ;
2020-03-09 15:56:48 +00:00
}
Vector < String > DisplayServerWindows : : get_rendering_drivers_func ( ) {
Vector < String > drivers ;
# ifdef VULKAN_ENABLED
drivers . push_back ( " vulkan " ) ;
# endif
2023-01-09 15:56:16 +00:00
# ifdef D3D12_ENABLED
drivers . push_back ( " d3d12 " ) ;
# endif
2021-10-26 15:18:39 +00:00
# ifdef GLES3_ENABLED
drivers . push_back ( " opengl3 " ) ;
2021-11-12 12:49:49 +00:00
drivers . push_back ( " opengl3_angle " ) ;
2020-03-09 15:56:48 +00:00
# endif
return drivers ;
}
2023-01-04 22:00:02 +00:00
DisplayServer * DisplayServerWindows : : create_func ( const String & p_rendering_driver , WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Vector2i * p_position , const Vector2i & p_resolution , int p_screen , Error & r_error ) {
DisplayServer * ds = memnew ( DisplayServerWindows ( p_rendering_driver , p_mode , p_vsync_mode , p_flags , p_position , p_resolution , p_screen , r_error ) ) ;
2020-07-13 16:24:04 +00:00
if ( r_error ! = OK ) {
2022-10-17 17:47:32 +00:00
if ( p_rendering_driver = = " vulkan " ) {
String executable_name = OS : : get_singleton ( ) - > get_executable_path ( ) . get_file ( ) ;
2023-01-17 14:26:10 +00:00
OS : : get_singleton ( ) - > alert (
vformat ( " Your video card drivers seem not to support the required Vulkan version. \n \n "
" If possible, consider updating your video card drivers or using the OpenGL 3 driver. \n \n "
" You can enable the OpenGL 3 driver by starting the engine from the \n "
2023-03-08 21:27:42 +00:00
" command line with the command: \n \n \" %s \" --rendering-driver opengl3 \n \n "
2023-01-17 14:26:10 +00:00
" If you have recently updated your video card drivers, try rebooting. " ,
executable_name ) ,
" Unable to initialize Vulkan video driver " ) ;
2023-12-19 11:48:02 +00:00
} else if ( p_rendering_driver = = " d3d12 " ) {
String executable_name = OS : : get_singleton ( ) - > get_executable_path ( ) . get_file ( ) ;
OS : : get_singleton ( ) - > alert (
vformat ( " Your video card drivers seem not to support the required DirectX 12 version. \n \n "
" If possible, consider updating your video card drivers or using the OpenGL 3 driver. \n \n "
" You can enable the OpenGL 3 driver by starting the engine from the \n "
" command line with the command: \n \n \" %s \" --rendering-driver opengl3 \n \n "
" If you have recently updated your video card drivers, try rebooting. " ,
executable_name ) ,
" Unable to initialize DirectX 12 video driver " ) ;
2022-10-17 17:47:32 +00:00
} else {
2023-01-17 14:26:10 +00:00
OS : : get_singleton ( ) - > alert (
" Your video card drivers seem not to support the required OpenGL 3.3 version. \n \n "
" If possible, consider updating your video card drivers. \n \n "
" If you have recently updated your video card drivers, try rebooting. " ,
" Unable to initialize OpenGL video driver " ) ;
2022-10-17 17:47:32 +00:00
}
2020-07-13 16:24:04 +00:00
}
return ds ;
2020-03-09 15:56:48 +00:00
}
void DisplayServerWindows : : register_windows_driver ( ) {
register_create_function ( " windows " , create_func , get_rendering_drivers_func ) ;
}
DisplayServerWindows : : ~ DisplayServerWindows ( ) {
delete joypad ;
touch_state . clear ( ) ;
cursors_cache . clear ( ) ;
2023-08-03 09:45:56 +00:00
// Destroy all status indicators.
2024-02-27 20:19:47 +00:00
for ( HashMap < IndicatorID , IndicatorData > : : Iterator E = indicators . begin ( ) ; E ; + + E ) {
2023-08-03 09:45:56 +00:00
NOTIFYICONDATAW ndat ;
ZeroMemory ( & ndat , sizeof ( NOTIFYICONDATAW ) ) ;
ndat . cbSize = sizeof ( NOTIFYICONDATAW ) ;
ndat . hWnd = windows [ MAIN_WINDOW_ID ] . hWnd ;
ndat . uID = E - > key ;
ndat . uVersion = NOTIFYICON_VERSION ;
Shell_NotifyIconW ( NIM_DELETE , & ndat ) ;
}
2022-02-24 09:21:23 +00:00
if ( mouse_monitor ) {
UnhookWindowsHookEx ( mouse_monitor ) ;
}
2020-03-09 15:56:48 +00:00
if ( user_proc ) {
SetWindowLongPtr ( windows [ MAIN_WINDOW_ID ] . hWnd , GWLP_WNDPROC , ( LONG_PTR ) user_proc ) ;
2022-02-16 12:56:32 +00:00
}
2020-03-09 15:56:48 +00:00
2022-08-03 16:29:07 +00:00
// Close power request handle.
screen_set_keep_on ( false ) ;
2024-01-19 17:41:01 +00:00
if ( native_menu ) {
memdelete ( native_menu ) ;
native_menu = nullptr ;
}
2021-10-26 15:18:39 +00:00
# ifdef GLES3_ENABLED
2022-02-16 12:56:32 +00:00
// destroy windows .. NYI?
2022-05-08 23:25:49 +00:00
// FIXME wglDeleteContext is never called
2020-11-18 18:11:30 +00:00
# endif
2020-03-09 15:56:48 +00:00
if ( windows . has ( MAIN_WINDOW_ID ) ) {
2023-12-19 11:48:02 +00:00
# ifdef RD_ENABLED
2023-12-19 17:57:56 +00:00
if ( rendering_device ) {
rendering_device - > screen_free ( MAIN_WINDOW_ID ) ;
}
if ( rendering_context ) {
rendering_context - > window_destroy ( MAIN_WINDOW_ID ) ;
2023-01-09 15:56:16 +00:00
}
2020-03-09 15:56:48 +00:00
# endif
2020-05-05 11:16:02 +00:00
if ( wintab_available & & windows [ MAIN_WINDOW_ID ] . wtctx ) {
wintab_WTClose ( windows [ MAIN_WINDOW_ID ] . wtctx ) ;
2024-03-12 14:40:40 +00:00
windows [ MAIN_WINDOW_ID ] . wtctx = nullptr ;
2020-05-05 11:16:02 +00:00
}
2020-03-09 15:56:48 +00:00
DestroyWindow ( windows [ MAIN_WINDOW_ID ] . hWnd ) ;
}
2020-05-21 16:39:28 +00:00
2023-12-19 11:48:02 +00:00
# ifdef RD_ENABLED
if ( rendering_device ) {
memdelete ( rendering_device ) ;
rendering_device = nullptr ;
2023-01-09 15:56:16 +00:00
}
2023-12-19 17:57:56 +00:00
if ( rendering_context ) {
memdelete ( rendering_context ) ;
rendering_context = nullptr ;
2023-01-09 15:56:16 +00:00
}
# endif
2021-09-20 04:42:56 +00:00
if ( restore_mouse_trails > 1 ) {
2024-03-12 14:40:40 +00:00
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , nullptr , 0 ) ;
2021-09-20 04:42:56 +00:00
}
2021-10-26 15:18:39 +00:00
# ifdef GLES3_ENABLED
2021-11-12 12:49:49 +00:00
if ( gl_manager_angle ) {
memdelete ( gl_manager_angle ) ;
gl_manager_angle = nullptr ;
}
if ( gl_manager_native ) {
memdelete ( gl_manager_native ) ;
gl_manager_native = nullptr ;
2020-11-18 18:11:30 +00:00
}
# endif
2021-11-04 12:33:37 +00:00
if ( tts ) {
memdelete ( tts ) ;
}
2020-03-09 15:56:48 +00:00
}