2020-03-28 13:29:29 +01:00
/*************************************************************************/
/* display_server_windows.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2020-03-28 13:29:29 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2020-03-09 16:56:48 +01:00
# include "display_server_windows.h"
2020-06-29 11:31:36 +02:00
2020-03-09 16:56:48 +01:00
# include "core/io/marshalls.h"
2020-06-29 11:31:36 +02:00
# include "core/math/geometry_2d.h"
2020-03-09 16:56:48 +01:00
# include "main/main.h"
# include "os_windows.h"
# include "scene/resources/texture.h"
# include <avrt.h>
2022-08-29 10:24:48 +02:00
# include <dwmapi.h>
# ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
# define DWMWA_USE_IMMERSIVE_DARK_MODE 20
# endif
2020-03-09 16:56:48 +01:00
2021-10-26 17:18:39 +02:00
# if defined(GLES3_ENABLED)
# include "drivers/gles3/rasterizer_gles3.h"
2020-11-18 19:11:30 +01:00
# endif
2021-11-10 15:09:56 +01:00
# if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
# define GetProcAddress (void *)GetProcAddress
# endif
2020-03-09 16:56:48 +01:00
static String format_error_message ( DWORD id ) {
2020-04-02 01:20:12 +02:00
LPWSTR messageBuffer = nullptr ;
2020-03-09 16:56:48 +01:00
size_t size = FormatMessageW ( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS ,
2020-04-02 01:20:12 +02:00
nullptr , id , MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) , ( LPWSTR ) & messageBuffer , 0 , nullptr ) ;
2020-03-09 16:56:48 +01:00
2020-07-27 12:43:20 +02:00
String msg = " Error " + itos ( id ) + " : " + String : : utf16 ( ( const char16_t * ) messageBuffer , size ) ;
2020-03-09 16:56:48 +01:00
LocalFree ( messageBuffer ) ;
return msg ;
}
2021-12-28 11:27:46 +01: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 16:56:48 +01:00
bool DisplayServerWindows : : has_feature ( Feature p_feature ) const {
switch ( p_feature ) {
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 :
case FEATURE_SWAP_BUFFERS :
case FEATURE_KEEP_SCREEN_ON :
2021-11-04 13:33:37 +01:00
case FEATURE_TEXT_TO_SPEECH :
2020-03-09 16:56:48 +01: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 15:44:44 +02: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-31 00:35:08 +02:00
// Mouse is grabbed (captured or confined).
2022-02-01 03:49:51 +01:00
WindowID window_id = windows . has ( last_focused_window ) ? last_focused_window : MAIN_WINDOW_ID ;
WindowData & wd = windows [ window_id ] ;
2020-03-09 16:56:48 +01: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 ) ;
}
} else {
2021-03-31 00:35:08 +02:00
// Mouse is free to move around (not captured or confined).
2020-03-09 16:56:48 +01:00
ReleaseCapture ( ) ;
2020-04-02 01:20:12 +02:00
ClipCursor ( nullptr ) ;
2020-03-09 16:56:48 +01:00
}
2021-03-31 00:35:08 +02:00
if ( p_mode = = MOUSE_MODE_HIDDEN | | p_mode = = MOUSE_MODE_CAPTURED | | p_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2020-06-22 12:05:18 +02:00
if ( hCursor = = nullptr ) {
hCursor = SetCursor ( nullptr ) ;
} else {
SetCursor ( nullptr ) ;
}
2020-03-09 16:56:48 +01:00
} else {
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
}
}
2020-05-14 14:29:06 +02:00
2021-11-04 13:33:37 +01:00
bool DisplayServerWindows : : tts_is_speaking ( ) const {
ERR_FAIL_COND_V ( ! tts , false ) ;
return tts - > is_speaking ( ) ;
}
bool DisplayServerWindows : : tts_is_paused ( ) const {
ERR_FAIL_COND_V ( ! tts , false ) ;
return tts - > is_paused ( ) ;
}
2022-08-05 03:41:48 +02:00
TypedArray < Dictionary > DisplayServerWindows : : tts_get_voices ( ) const {
ERR_FAIL_COND_V ( ! tts , TypedArray < Dictionary > ( ) ) ;
2021-11-04 13:33:37 +01: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 ) {
ERR_FAIL_COND ( ! tts ) ;
tts - > speak ( p_text , p_voice , p_volume , p_pitch , p_rate , p_utterance_id , p_interrupt ) ;
}
void DisplayServerWindows : : tts_pause ( ) {
ERR_FAIL_COND ( ! tts ) ;
tts - > pause ( ) ;
}
void DisplayServerWindows : : tts_resume ( ) {
ERR_FAIL_COND ( ! tts ) ;
tts - > resume ( ) ;
}
void DisplayServerWindows : : tts_stop ( ) {
ERR_FAIL_COND ( ! tts ) ;
tts - > stop ( ) ;
}
2020-03-09 16:56:48 +01:00
void DisplayServerWindows : : mouse_set_mode ( MouseMode p_mode ) {
_THREAD_SAFE_METHOD_
2021-09-10 15:44:44 +02:00
if ( mouse_mode = = p_mode ) {
// Already in the same mode; do nothing.
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
mouse_mode = p_mode ;
2020-06-22 12:05:18 +02:00
_set_mouse_mode_impl ( p_mode ) ;
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
DisplayServer : : MouseMode DisplayServerWindows : : mouse_get_mode ( ) const {
return mouse_mode ;
}
2022-03-27 11:17:36 +02:00
void DisplayServerWindows : : warp_mouse ( const Point2i & p_position ) {
2020-03-09 16:56:48 +01:00
_THREAD_SAFE_METHOD_
if ( ! windows . has ( last_focused_window ) ) {
2021-09-10 15:44:44 +02:00
return ; // No focused window?
2020-03-09 16:56:48 +01:00
}
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
2022-03-27 11:17:36 +02:00
old_x = p_position . x ;
old_y = p_position . y ;
2020-03-09 16:56:48 +01:00
} else {
POINT p ;
2022-03-27 11:17:36 +02:00
p . x = p_position . x ;
p . y = p_position . y ;
2020-03-09 16:56:48 +01:00
ClientToScreen ( windows [ last_focused_window ] . hWnd , & p ) ;
SetCursorPos ( p . x , p . y ) ;
}
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
Point2i DisplayServerWindows : : mouse_get_position ( ) const {
2020-03-20 03:32:09 +01:00
POINT p ;
GetCursorPos ( & p ) ;
return Point2i ( p . x , p . y ) ;
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2021-03-25 21:56:12 +01:00
MouseButton DisplayServerWindows : : mouse_get_button_state ( ) const {
2020-03-09 16:56:48 +01:00
return last_button_state ;
}
void DisplayServerWindows : : clipboard_set ( const String & p_text ) {
_THREAD_SAFE_METHOD_
if ( ! windows . has ( last_focused_window ) ) {
2021-09-10 15:44:44 +02:00
return ; // No focused window?
2020-03-09 16:56:48 +01:00
}
2021-09-10 15:44:44 +02: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 16:56:48 +01:00
if ( ! OpenClipboard ( windows [ last_focused_window ] . hWnd ) ) {
ERR_FAIL_MSG ( " Unable to open clipboard. " ) ;
}
EmptyClipboard ( ) ;
2020-07-27 12:43:20 +02:00
Char16String utf16 = text . utf16 ( ) ;
HGLOBAL mem = GlobalAlloc ( GMEM_MOVEABLE , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_MSG ( mem = = nullptr , " Unable to allocate memory for clipboard contents. " ) ;
2020-03-09 16:56:48 +01:00
LPWSTR lptstrCopy = ( LPWSTR ) GlobalLock ( mem ) ;
2020-07-27 12:43:20 +02:00
memcpy ( lptstrCopy , utf16 . get_data ( ) , ( utf16 . length ( ) + 1 ) * sizeof ( WCHAR ) ) ;
2020-03-09 16:56:48 +01:00
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_UNICODETEXT , mem ) ;
2021-09-10 15:44:44 +02:00
// Set the CF_TEXT version (not needed?).
2020-03-09 16:56:48 +01:00
CharString utf8 = text . utf8 ( ) ;
mem = GlobalAlloc ( GMEM_MOVEABLE , utf8 . length ( ) + 1 ) ;
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_MSG ( mem = = nullptr , " Unable to allocate memory for clipboard contents. " ) ;
2020-03-09 16:56:48 +01:00
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
memcpy ( ptr , utf8 . get_data ( ) , utf8 . length ( ) ) ;
ptr [ utf8 . length ( ) ] = 0 ;
GlobalUnlock ( mem ) ;
SetClipboardData ( CF_TEXT , mem ) ;
CloseClipboard ( ) ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
String DisplayServerWindows : : clipboard_get ( ) const {
_THREAD_SAFE_METHOD_
if ( ! windows . has ( last_focused_window ) ) {
2021-09-10 15:44:44 +02:00
return String ( ) ; // No focused window?
2020-03-09 16:56:48 +01:00
}
String ret ;
if ( ! OpenClipboard ( windows [ last_focused_window ] . hWnd ) ) {
ERR_FAIL_V_MSG ( " " , " Unable to open clipboard. " ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
if ( IsClipboardFormatAvailable ( CF_UNICODETEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
2020-04-02 01:20:12 +02:00
if ( mem ! = nullptr ) {
2020-03-09 16:56:48 +01:00
LPWSTR ptr = ( LPWSTR ) GlobalLock ( mem ) ;
2020-04-02 01:20:12 +02:00
if ( ptr ! = nullptr ) {
2020-07-27 12:43:20 +02:00
ret = String : : utf16 ( ( const char16_t * ) ptr ) ;
2020-03-09 16:56:48 +01:00
GlobalUnlock ( mem ) ;
2022-02-16 13:56:32 +01:00
}
}
2020-03-09 16:56:48 +01:00
} else if ( IsClipboardFormatAvailable ( CF_TEXT ) ) {
HGLOBAL mem = GetClipboardData ( CF_UNICODETEXT ) ;
2020-04-02 01:20:12 +02:00
if ( mem ! = nullptr ) {
2020-03-09 16:56:48 +01:00
LPTSTR ptr = ( LPTSTR ) GlobalLock ( mem ) ;
2020-04-02 01:20:12 +02:00
if ( ptr ! = nullptr ) {
2020-03-09 16:56:48 +01:00
ret . parse_utf8 ( ( const char * ) ptr ) ;
GlobalUnlock ( mem ) ;
2022-02-16 13:56:32 +01:00
}
}
}
2020-03-09 16:56:48 +01:00
CloseClipboard ( ) ;
return ret ;
}
typedef struct {
int count ;
int screen ;
HMONITOR monitor ;
} EnumScreenData ;
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-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcCount , ( LPARAM ) & data ) ;
2020-03-09 16:56:48 +01:00
return data ;
}
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 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
Point2i DisplayServerWindows : : screen_get_position ( int p_screen ) const {
_THREAD_SAFE_METHOD_
EnumPosData data = { 0 , p_screen = = SCREEN_OF_MAIN_WINDOW ? window_get_current_screen ( ) : p_screen , Point2 ( ) } ;
2020-04-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcPos , ( LPARAM ) & data ) ;
2020-03-09 16:56:48 +01:00
return data . pos ;
}
typedef struct {
int count ;
int screen ;
Size2 size ;
} EnumSizeData ;
2020-03-20 03:32:09 +01:00
typedef struct {
int count ;
int screen ;
Rect2i rect ;
} EnumRectData ;
2022-01-27 20:46:57 +01:00
typedef struct {
int count ;
int screen ;
float rate ;
} EnumRefreshRateData ;
2020-03-09 16:56:48 +01:00
static BOOL CALLBACK _MonitorEnumProcSize ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumSizeData * data = ( EnumSizeData * ) dwData ;
if ( data - > count = = data - > screen ) {
data - > size . x = lprcMonitor - > right - lprcMonitor - > left ;
data - > size . y = lprcMonitor - > bottom - lprcMonitor - > top ;
}
data - > count + + ;
return TRUE ;
}
Size2i DisplayServerWindows : : screen_get_size ( int p_screen ) const {
_THREAD_SAFE_METHOD_
EnumSizeData data = { 0 , p_screen = = SCREEN_OF_MAIN_WINDOW ? window_get_current_screen ( ) : p_screen , Size2 ( ) } ;
2020-04-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcSize , ( LPARAM ) & data ) ;
2020-03-09 16:56:48 +01:00
return data . size ;
}
2020-03-20 03:32:09 +01: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 16:19:21 +02:00
memset ( & minfo , 0 , sizeof ( MONITORINFO ) ) ;
2020-03-20 03:32:09 +01: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 20:46:57 +01:00
static BOOL CALLBACK _MonitorEnumProcRefreshRate ( HMONITOR hMonitor , HDC hdcMonitor , LPRECT lprcMonitor , LPARAM dwData ) {
EnumRefreshRateData * data = ( EnumRefreshRateData * ) dwData ;
if ( data - > count = = data - > screen ) {
MONITORINFOEXW minfo ;
memset ( & minfo , 0 , sizeof ( minfo ) ) ;
minfo . cbSize = sizeof ( minfo ) ;
GetMonitorInfoW ( hMonitor , & minfo ) ;
DEVMODEW dm ;
memset ( & dm , 0 , sizeof ( dm ) ) ;
dm . dmSize = sizeof ( dm ) ;
EnumDisplaySettingsW ( minfo . szDevice , ENUM_CURRENT_SETTINGS , & dm ) ;
data - > rate = dm . dmDisplayFrequency ;
}
data - > count + + ;
return TRUE ;
}
2020-03-20 03:32:09 +01:00
Rect2i DisplayServerWindows : : screen_get_usable_rect ( int p_screen ) const {
_THREAD_SAFE_METHOD_
EnumRectData data = { 0 , p_screen = = SCREEN_OF_MAIN_WINDOW ? window_get_current_screen ( ) : p_screen , Rect2i ( ) } ;
2020-04-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcUsableSize , ( LPARAM ) & data ) ;
2020-03-20 03:32:09 +01:00
return data . rect ;
}
2020-03-09 16:56:48 +01: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-02 01:20:12 +02:00
static HMODULE Shcore = nullptr ;
2020-03-09 16:56:48 +01:00
typedef HRESULT ( WINAPI * GetDPIForMonitor_t ) ( HMONITOR hmonitor , _MonitorDpiType dpiType , UINT * dpiX , UINT * dpiY ) ;
2020-04-02 01:20:12 +02:00
static GetDPIForMonitor_t getDPIForMonitor = nullptr ;
2020-03-09 16:56:48 +01:00
2020-04-02 01:20:12 +02:00
if ( Shcore = = nullptr ) {
2020-03-09 16:56:48 +01:00
Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
2020-04-02 01:20:12 +02:00
getDPIForMonitor = Shcore ? ( GetDPIForMonitor_t ) GetProcAddress ( Shcore , " GetDpiForMonitor " ) : nullptr ;
2020-03-09 16:56:48 +01:00
2020-04-02 01:20:12 +02:00
if ( ( Shcore = = nullptr ) | | ( getDPIForMonitor = = nullptr ) ) {
2022-02-16 13:56:32 +01:00
if ( Shcore ) {
2020-03-09 16:56:48 +01:00
FreeLibrary ( Shcore ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
Shcore = ( HMODULE ) INVALID_HANDLE_VALUE ;
}
}
UINT x = 0 , y = 0 ;
if ( hmon & & ( Shcore ! = ( HMODULE ) INVALID_HANDLE_VALUE ) ) {
2022-04-05 12:40:26 +02:00
HRESULT hr = getDPIForMonitor ( hmon , dpiType /*MDT_Effective_DPI*/ , & x , & y ) ;
2020-03-09 16:56:48 +01: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-02 01:20:12 +02:00
HDC hdc = GetDC ( nullptr ) ;
2020-03-09 16:56:48 +01:00
if ( hdc ) {
overallX = GetDeviceCaps ( hdc , LOGPIXELSX ) ;
overallY = GetDeviceCaps ( hdc , LOGPIXELSY ) ;
2020-04-02 01:20:12 +02:00
ReleaseDC ( nullptr , hdc ) ;
2020-03-09 16:56:48 +01: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_
EnumDpiData data = { 0 , p_screen = = SCREEN_OF_MAIN_WINDOW ? window_get_current_screen ( ) : p_screen , 72 } ;
2020-04-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcDpi , ( LPARAM ) & data ) ;
2020-03-09 16:56:48 +01:00
return data . dpi ;
}
2022-01-27 20:46:57 +01:00
float DisplayServerWindows : : screen_get_refresh_rate ( int p_screen ) const {
_THREAD_SAFE_METHOD_
EnumRefreshRateData data = { 0 , p_screen = = SCREEN_OF_MAIN_WINDOW ? window_get_current_screen ( ) : p_screen , SCREEN_REFRESH_RATE_FALLBACK } ;
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcRefreshRate , ( LPARAM ) & data ) ;
return data . rate ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
bool DisplayServerWindows : : screen_is_touchscreen ( int p_screen ) const {
# ifndef _MSC_VER
# warning touchscreen not working
# endif
return false ;
}
void DisplayServerWindows : : screen_set_orientation ( ScreenOrientation p_orientation , int p_screen ) {
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
DisplayServer : : ScreenOrientation DisplayServerWindows : : screen_get_orientation ( int p_screen ) const {
return SCREEN_LANDSCAPE ;
}
void DisplayServerWindows : : screen_set_keep_on ( bool p_enable ) {
2022-08-03 18:29:07 +02: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 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
bool DisplayServerWindows : : screen_is_kept_on ( ) const {
2022-08-03 18:29:07 +02:00
return keep_screen_on ;
2020-03-09 16:56:48 +01:00
}
2020-03-25 00:15:35 +01:00
Vector < DisplayServer : : WindowID > DisplayServerWindows : : get_window_list ( ) const {
2020-03-09 16:56:48 +01:00
_THREAD_SAFE_METHOD_
2020-03-25 00:15:35 +01:00
Vector < DisplayServer : : WindowID > ret ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
ret . push_back ( E . key ) ;
2020-03-09 16:56:48 +01:00
}
return ret ;
}
2020-03-25 00:15:35 +01:00
DisplayServer : : WindowID DisplayServerWindows : : get_window_at_screen_position ( const Point2i & p_position ) const {
POINT p ;
p . x = p_position . x ;
p . y = p_position . y ;
HWND hwnd = WindowFromPoint ( p ) ;
2021-08-09 22:13:42 +02:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( E . value . hWnd = = hwnd ) {
return E . key ;
2020-03-25 00:15:35 +01:00
}
}
return INVALID_WINDOW_ID ;
}
2021-06-19 17:44:59 +02: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 16:56:48 +01:00
_THREAD_SAFE_METHOD_
2021-06-19 17:44:59 +02:00
WindowID window_id = _create_window ( p_mode , p_vsync_mode , p_flags , p_rect ) ;
2020-06-27 18:26:34 +02:00
ERR_FAIL_COND_V_MSG ( window_id = = INVALID_WINDOW_ID , INVALID_WINDOW_ID , " Failed to create sub window. " ) ;
2020-03-20 21:51:53 +01: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 10:19:53 +01:00
if ( p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT & & p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-03-20 21:51:53 +01:00
wd . always_on_top = true ;
}
if ( p_flags & WINDOW_FLAG_NO_FOCUS_BIT ) {
wd . no_focus = true ;
2020-03-09 16:56:48 +01:00
}
2022-02-24 10:21:23 +01:00
if ( p_flags & WINDOW_FLAG_POPUP_BIT ) {
wd . is_popup = true ;
}
2020-03-09 16:56:48 +01:00
2022-02-07 02:28:42 +01: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 ) ;
}
2020-08-21 09:39:30 +02:00
return window_id ;
}
void DisplayServerWindows : : show_window ( WindowID p_id ) {
2021-09-10 15:44:44 +02:00
ERR_FAIL_COND ( ! windows . has ( p_id ) ) ;
2020-08-21 09:39:30 +02:00
WindowData & wd = windows [ p_id ] ;
2022-02-24 10:21:23 +01:00
popup_open ( p_id ) ;
2020-08-21 09:39:30 +02:00
2020-08-22 17:50:06 +02:00
if ( p_id ! = MAIN_WINDOW_ID ) {
_update_window_style ( p_id ) ;
}
2022-05-09 01:25:49 +02:00
if ( wd . no_focus | | wd . is_popup ) {
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
ShowWindow ( wd . hWnd , SW_SHOWNA ) ;
} else {
ShowWindow ( wd . hWnd , SW_SHOW ) ;
2021-09-10 15:44:44 +02:00
SetForegroundWindow ( wd . hWnd ) ; // Slightly higher priority.
SetFocus ( wd . hWnd ) ; // Set keyboard focus.
2020-03-20 21:51:53 +01:00
}
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 10:21:23 +01:00
popup_close ( p_window ) ;
2020-03-09 16:56:48 +01:00
WindowData & wd = windows [ p_window ] ;
while ( wd . transient_children . size ( ) ) {
2022-05-19 17:00:06 +02:00
window_set_transient ( * wd . transient_children . begin ( ) , INVALID_WINDOW_ID ) ;
2020-03-09 16:56:48 +01:00
}
if ( wd . transient_parent ! = INVALID_WINDOW_ID ) {
window_set_transient ( p_window , INVALID_WINDOW_ID ) ;
}
# ifdef VULKAN_ENABLED
2021-12-14 02:49:29 +01:00
if ( context_vulkan ) {
2020-03-09 16:56:48 +01:00
context_vulkan - > window_destroy ( p_window ) ;
}
# endif
2021-10-26 17:18:39 +02:00
# ifdef GLES3_ENABLED
2021-12-14 02:49:29 +01:00
if ( gl_manager ) {
2020-11-18 19:11:30 +01:00
gl_manager - > window_destroy ( p_window ) ;
}
# endif
2020-03-09 16:56:48 +01:00
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ p_window ] . wtctx ) {
2020-05-05 13:16:02 +02:00
wintab_WTClose ( windows [ p_window ] . wtctx ) ;
windows [ p_window ] . wtctx = 0 ;
}
2020-03-09 16:56:48 +01:00
DestroyWindow ( windows [ p_window ] . hWnd ) ;
windows . erase ( p_window ) ;
}
2020-11-18 19:11:30 +01:00
void DisplayServerWindows : : gl_window_make_current ( DisplayServer : : WindowID p_window_id ) {
2021-10-26 17:18:39 +02:00
# if defined(GLES3_ENABLED)
2022-07-06 08:56:47 +02:00
if ( gl_manager ) {
gl_manager - > window_make_current ( p_window_id ) ;
}
2020-11-18 19:11:30 +01:00
# endif
}
2022-01-14 11:41:04 +01: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 ;
}
case WINDOW_VIEW : {
return 0 ; // Not supported.
}
default : {
return 0 ;
}
}
}
2020-03-25 00:15:35 +01: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 17:06:39 +01:00
void DisplayServerWindows : : window_set_rect_changed_callback ( const Callable & p_callable , WindowID p_window ) {
2020-03-09 16:56:48 +01:00
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-03-14 17:06:39 +01:00
windows [ p_window ] . rect_changed_callback = p_callable ;
2020-03-09 16:56:48 +01: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 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 12:43:20 +02:00
SetWindowTextW ( windows [ p_window ] . hWnd , ( LPCWSTR ) ( p_title . utf16 ( ) . get_data ( ) ) ) ;
2020-03-09 16:56:48 +01:00
}
2020-06-29 11:31:36 +02: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 15:44:44 +02:00
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-06-29 11:31:36 +02:00
if ( windows [ p_window ] . mpath . size ( ) = = 0 ) {
SetWindowRgn ( windows [ p_window ] . hWnd , nullptr , TRUE ) ;
} 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 ) ;
SetWindowRgn ( windows [ p_window ] . hWnd , region , TRUE ) ;
DeleteObject ( region ) ;
memfree ( points ) ;
}
}
2020-03-09 16:56:48 +01: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-02 01:20:12 +02:00
EnumDisplayMonitors ( nullptr , nullptr , _MonitorEnumProcScreen , ( LPARAM ) & data ) ;
2020-03-09 16:56:48 +01:00
return data . screen ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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-01-15 19:47:34 +01:00
const WindowData & wd = windows [ p_window ] ;
if ( wd . fullscreen ) {
int cs = window_get_current_screen ( p_window ) ;
if ( cs = = p_screen ) {
return ;
}
Point2 pos = screen_get_position ( p_screen ) ;
Size2 size = screen_get_size ( p_screen ) ;
MoveWindow ( wd . hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
} else {
Vector2 ofs = window_get_position ( p_window ) - screen_get_position ( window_get_current_screen ( p_window ) ) ;
window_set_position ( ofs + screen_get_position ( p_screen ) , p_window ) ;
}
2022-01-26 12:58:54 +01:00
// Don't let the mouse leave the window when resizing to a smaller resolution.
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
RECT crect ;
GetClientRect ( wd . hWnd , & crect ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
}
2020-03-09 16:56:48 +01: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 13:37:40 +01:00
POINT point ;
point . x = 0 ;
point . y = 0 ;
ClientToScreen ( wd . hWnd , & point ) ;
return Point2i ( point . x , point . y ) ;
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
void DisplayServerWindows : : _update_real_mouse_position ( WindowID p_window ) {
2021-09-10 15:44:44 +02:00
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2020-03-09 16:56:48 +01: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 15:19:37 +02:00
Input : : get_singleton ( ) - > set_mouse_position ( Point2i ( mouse_pos . x , mouse_pos . y ) ) ;
2020-03-09 16:56:48 +01:00
}
}
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 ] ;
2021-09-10 15:44:44 +02:00
if ( wd . fullscreen ) {
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-12 13:37:40 +01:00
RECT rc ;
rc . left = p_position . x ;
rc . right = p_position . x + wd . width ;
rc . bottom = p_position . y + wd . height ;
rc . top = p_position . y ;
2020-03-09 16:56:48 +01:00
2020-03-12 13:37:40 +01: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 15:44:44 +02:00
// Don't let the mouse leave the window when moved.
2021-03-31 00:35:08 +02:00
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2020-03-09 16:56:48 +01:00
RECT rect ;
GetClientRect ( wd . hWnd , & rect ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & rect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & rect . right ) ;
ClipCursor ( & rect ) ;
}
wd . last_pos = p_position ;
_update_real_mouse_position ( p_window ) ;
}
2022-01-19 13:04:05 +01: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 12:40:26 +02:00
WindowData & wd_parent = windows [ wd . transient_parent ] ;
2022-01-19 13:04:05 +01:00
SetWindowLongPtr ( wd . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) wd_parent . hWnd ) ;
} else {
SetWindowLongPtr ( wd . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) nullptr ) ;
}
}
}
}
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
// Remove transient.
2020-03-09 16:56:48 +01: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 13:04:05 +01:00
if ( wd_window . exclusive ) {
SetWindowLongPtr ( wd_window . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) nullptr ) ;
}
2020-03-09 16:56:48 +01: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 13:04:05 +01:00
if ( wd_window . exclusive ) {
SetWindowLongPtr ( wd_window . hWnd , GWLP_HWNDPARENT , ( LONG_PTR ) wd_parent . hWnd ) ;
}
2020-03-09 16:56:48 +01: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 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 ] ;
int w = p_size . width ;
int h = p_size . height ;
wd . width = w ;
wd . height = h ;
2020-03-20 03:32:09 +01:00
2020-03-09 16:56:48 +01:00
# if defined(VULKAN_ENABLED)
2021-12-14 02:49:29 +01:00
if ( context_vulkan ) {
2020-03-09 16:56:48 +01:00
context_vulkan - > window_resize ( p_window , w , h ) ;
}
# endif
2021-10-26 17:18:39 +02:00
# if defined(GLES3_ENABLED)
2021-12-14 02:49:29 +01:00
if ( gl_manager ) {
2020-11-18 19:11:30 +01:00
gl_manager - > window_resize ( p_window , w , h ) ;
}
# endif
2020-03-09 16:56:48 +01:00
if ( wd . fullscreen ) {
return ;
}
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 ) ;
2021-09-10 15:44:44 +02:00
// Don't let the mouse leave the window when resizing to a smaller resolution.
2021-03-31 00:35:08 +02:00
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
2020-03-09 16:56:48 +01:00
RECT crect ;
GetClientRect ( wd . hWnd , & crect ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
}
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 04:30:28 +02:00
// GetClientRect() returns a zero rect for a minimized window, so we need to get the size in another way.
2020-03-09 16:56:48 +01:00
if ( wd . minimized ) {
return Size2 ( wd . width , wd . height ) ;
}
RECT r ;
2021-09-10 15:44:44 +02:00
if ( GetClientRect ( wd . hWnd , & r ) ) { // Retrieves area inside of window border, including decoration.
2020-03-09 16:56:48 +01:00
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
Size2i DisplayServerWindows : : window_get_real_size ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND_V ( ! windows . has ( p_window ) , Size2i ( ) ) ;
const WindowData & wd = windows [ p_window ] ;
RECT r ;
2021-09-10 15:44:44 +02:00
if ( GetWindowRect ( wd . hWnd , & r ) ) { // Retrieves area inside of window border, including decoration.
2020-03-09 16:56:48 +01:00
return Size2 ( r . right - r . left , r . bottom - r . top ) ;
}
return Size2 ( ) ;
}
2022-01-28 10:19:53 +01: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_no_activate_focus , DWORD & r_style , DWORD & r_style_ex ) {
2021-09-10 15:44:44 +02: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 20:56:37 +01:00
r_style = 0 ;
r_style_ex = WS_EX_WINDOWEDGE ;
if ( p_main_window ) {
r_style_ex | = WS_EX_APPWINDOW ;
2022-02-24 10:21:23 +01:00
r_style | = WS_VISIBLE ;
2020-03-14 17:06:39 +01:00
}
2020-03-14 20:56:37 +01:00
if ( p_fullscreen | | p_borderless ) {
2021-09-10 15:44:44 +02:00
r_style | = WS_POPUP ; // p_borderless was WS_EX_TOOLWINDOW in the past.
2022-01-28 10:19:53 +01:00
if ( p_fullscreen & & p_multiwindow_fs ) {
r_style | = WS_BORDER ; // Allows child windows to be displayed on top of full screen.
}
2020-03-09 16:56:48 +01:00
} else {
2020-03-14 20:56:37 +01:00
if ( p_resizable ) {
2020-03-09 16:56:48 +01:00
if ( p_maximized ) {
2020-03-14 20:56:37 +01:00
r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE ;
2020-03-09 16:56:48 +01:00
} else {
2020-03-14 20:56:37 +01:00
r_style = WS_OVERLAPPEDWINDOW ;
2020-03-09 16:56:48 +01:00
}
} else {
2020-03-14 20:56:37 +01:00
r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU ;
2020-03-09 16:56:48 +01:00
}
}
2020-03-20 21:51:53 +01:00
if ( p_no_activate_focus ) {
r_style_ex | = WS_EX_TOPMOST | WS_EX_NOACTIVATE ;
}
2022-02-24 10:21:23 +01:00
if ( ! p_borderless & & ! p_no_activate_focus ) {
r_style | = WS_VISIBLE ;
}
2020-03-14 20:56:37 +01:00
r_style | = WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
2022-02-11 08:21:58 +01:00
r_style_ex | = WS_EX_ACCEPTFILES ;
2020-03-14 20:56:37 +01:00
}
2020-12-31 12:49:03 +01:00
void DisplayServerWindows : : _update_window_style ( WindowID p_window , bool p_repaint ) {
2020-03-14 20:56:37 +01:00
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
WindowData & wd = windows [ p_window ] ;
DWORD style = 0 ;
DWORD style_ex = 0 ;
2022-02-24 10:21:23 +01:00
_get_window_style ( p_window = = MAIN_WINDOW_ID , wd . fullscreen , wd . multiwindow_fs , wd . borderless , wd . resizable , wd . maximized , wd . no_focus | | wd . is_popup , style , style_ex ) ;
2020-03-14 20:56:37 +01:00
2020-03-14 17:06:39 +01:00
SetWindowLongPtr ( wd . hWnd , GWL_STYLE , style ) ;
SetWindowLongPtr ( wd . hWnd , GWL_EXSTYLE , style_ex ) ;
2022-05-10 10:12:04 +02:00
if ( icon . is_valid ( ) ) {
set_icon ( icon ) ;
}
2022-02-24 10:21:23 +01: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 16:56:48 +01: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 10:19:53 +01:00
if ( wd . fullscreen & & p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-03-09 16:56:48 +01:00
RECT rect ;
wd . fullscreen = false ;
2022-01-28 10:19:53 +01:00
wd . multiwindow_fs = false ;
2020-12-31 12:49:03 +01:00
wd . maximized = wd . was_maximized ;
2020-03-09 16:56:48 +01: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 12:49:03 +01:00
wd . pre_fs_valid = true ;
2020-03-09 16:56:48 +01:00
}
2020-12-31 12:49:03 +01:00
_update_window_style ( p_window , false ) ;
2020-03-09 16:56:48 +01:00
MoveWindow ( wd . hWnd , rect . left , rect . top , rect . right - rect . left , rect . bottom - rect . top , TRUE ) ;
2021-09-20 06:42:56 +02:00
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
restore_mouse_trails = 0 ;
}
2020-12-31 12:49:03 +01:00
} else if ( p_mode = = WINDOW_MODE_WINDOWED ) {
ShowWindow ( wd . hWnd , SW_RESTORE ) ;
wd . maximized = false ;
wd . minimized = false ;
2020-03-09 16:56:48 +01: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 10:19:53 +01:00
if ( p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
wd . multiwindow_fs = false ;
2022-02-11 08:21:58 +01:00
_update_window_style ( p_window , false ) ;
2022-01-28 10:19:53 +01:00
} else {
wd . multiwindow_fs = true ;
2022-02-11 08:21:58 +01:00
_update_window_style ( p_window , false ) ;
2022-01-28 10:19:53 +01:00
}
if ( ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) & & ! wd . fullscreen ) {
2020-03-09 16:56:48 +01:00
if ( wd . minimized ) {
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 ) ;
Point2 pos = screen_get_position ( cs ) ;
Size2 size = screen_get_size ( cs ) ;
wd . fullscreen = true ;
wd . maximized = false ;
wd . minimized = false ;
2022-02-11 08:21:58 +01:00
_update_window_style ( p_window , false ) ;
2020-03-09 16:56:48 +01:00
MoveWindow ( wd . hWnd , pos . x , pos . y , size . width , size . height , TRUE ) ;
2021-09-20 06:42:56 +02:00
// If the user has mouse trails enabled in windows, then sometimes the cursor disappears in fullscreen mode.
// Save number of trails so we can restore when exiting, then turn off mouse trails
SystemParametersInfoA ( SPI_GETMOUSETRAILS , 0 , & restore_mouse_trails , 0 ) ;
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , 0 , 0 , 0 ) ;
}
2020-03-09 16:56:48 +01:00
}
2022-01-26 12:58:54 +01:00
// Don't let the mouse leave the window when resizing to a smaller resolution.
if ( mouse_mode = = MOUSE_MODE_CONFINED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) {
RECT crect ;
GetClientRect ( wd . hWnd , & crect ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . left ) ;
ClientToScreen ( wd . hWnd , ( POINT * ) & crect . right ) ;
ClipCursor ( & crect ) ;
}
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 10:19:53 +01:00
if ( wd . multiwindow_fs ) {
return WINDOW_MODE_FULLSCREEN ;
} else {
return WINDOW_MODE_EXCLUSIVE_FULLSCREEN ;
}
2020-03-09 16:56:48 +01: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 15:35:00 +02:00
// FIXME: Implement this, or confirm that it should always be true.
2020-03-09 16:56:48 +01:00
2021-09-10 15:44:44 +02:00
return true ;
2020-03-09 16:56:48 +01: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 11:31:36 +02:00
_update_window_mouse_passthrough ( p_window ) ;
2022-02-24 10:21:23 +01:00
ShowWindow ( wd . hWnd , ( wd . no_focus | | wd . is_popup ) ? SW_SHOWNOACTIVATE : SW_SHOW ) ; // Show the window.
2020-03-09 16:56:48 +01: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 12:40:53 +02:00
if ( p_enabled ) {
//enable per-pixel alpha
DWM_BLURBEHIND bb = { 0 } ;
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = TRUE ;
DwmEnableBlurBehindWindow ( wd . hWnd , & bb ) ;
wd . layered_window = true ;
} else {
//disable per-pixel alpha
wd . layered_window = false ;
DWM_BLURBEHIND bb = { 0 } ;
HRGN hRgn = CreateRectRgn ( 0 , 0 , - 1 , - 1 ) ;
bb . dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION ;
bb . hRgnBlur = hRgn ;
bb . fEnable = FALSE ;
DwmEnableBlurBehindWindow ( wd . hWnd , & bb ) ;
}
2020-03-09 16:56:48 +01:00
} break ;
2020-03-20 21:51:53 +01:00
case WINDOW_FLAG_NO_FOCUS : {
wd . no_focus = p_enabled ;
_update_window_style ( p_window ) ;
} break ;
2022-02-24 10:21:23 +01:00
case WINDOW_FLAG_POPUP : {
ERR_FAIL_COND_MSG ( p_window = = MAIN_WINDOW_ID , " Main window can't be popup. " ) ;
2022-03-08 10:46:44 +01: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 10:21:23 +01:00
wd . is_popup = p_enabled ;
} break ;
2020-05-10 13:00:47 +02:00
case WINDOW_FLAG_MAX :
break ;
2020-03-09 16:56:48 +01:00
}
}
2020-04-01 15:35:00 +02:00
2020-03-09 16:56:48 +01: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 12:40:53 +02:00
return wd . layered_window ;
2020-04-01 15:35:00 +02:00
} break ;
case WINDOW_FLAG_NO_FOCUS : {
return wd . no_focus ;
2020-03-09 16:56:48 +01:00
} break ;
2022-02-24 10:21:23 +01:00
case WINDOW_FLAG_POPUP : {
return wd . is_popup ;
} break ;
2020-05-10 13:00:47 +02:00
case WINDOW_FLAG_MAX :
break ;
2020-03-09 16:56:48 +01:00
}
return false ;
}
void DisplayServerWindows : : window_request_attention ( WindowID p_window ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! windows . has ( p_window ) ) ;
2022-04-05 12:40:26 +02:00
const WindowData & wd = windows [ p_window ] ;
2020-03-09 16:56:48 +01:00
FLASHWINFO info ;
info . cbSize = sizeof ( FLASHWINFO ) ;
info . hwnd = wd . hWnd ;
info . dwFlags = FLASHW_TRAY ;
info . dwTimeout = 0 ;
info . uCount = 2 ;
FlashWindowEx ( & info ) ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 10:21:23 +01:00
if ( ! wd . no_focus & & ! wd . is_popup ) {
SetForegroundWindow ( wd . hWnd ) ;
}
2020-03-09 16:56:48 +01: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 12:51:36 +02:00
return ! wd . minimized ;
2020-03-09 16:56:48 +01:00
}
bool DisplayServerWindows : : can_any_window_draw ( ) const {
_THREAD_SAFE_METHOD_
2021-08-09 22:13:42 +02:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( ! E . value . minimized ) {
2020-03-09 16:56:48 +01:00
return true ;
}
}
return false ;
}
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 ) {
ImmAssociateContext ( wd . hWnd , wd . im_himc ) ;
window_set_ime_position ( wd . im_position , p_window ) ;
} else {
ImmAssociateContext ( wd . hWnd , ( HIMC ) 0 ) ;
}
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01: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 ) ;
2022-02-16 13:56:32 +01:00
if ( himc = = ( HIMC ) 0 ) {
2020-03-09 16:56:48 +01:00
return ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
COMPOSITIONFORM cps ;
cps . dwStyle = CFS_FORCE_POSITION ;
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 15:44:44 +02:00
if ( cursor_shape = = p_shape ) {
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
IDC_HAND , // Finger.
2020-03-09 16:56:48 +01:00
IDC_CROSS ,
IDC_WAIT ,
IDC_APPSTARTING ,
2021-10-06 13:13:42 +02:00
IDC_SIZEALL ,
2020-03-09 16:56:48 +01:00
IDC_ARROW ,
IDC_NO ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_SIZENESW ,
IDC_SIZENWSE ,
IDC_SIZEALL ,
IDC_SIZENS ,
IDC_SIZEWE ,
IDC_HELP
} ;
2022-08-30 17:50:56 +02:00
if ( cursors_cache . has ( p_shape ) ) {
2020-03-09 16:56:48 +01:00
SetCursor ( cursors [ p_shape ] ) ;
} else {
SetCursor ( LoadCursor ( hInstance , win_cursors [ p_shape ] ) ) ;
}
cursor_shape = p_shape ;
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
DisplayServer : : CursorShape DisplayServerWindows : : cursor_get_shape ( ) const {
return cursor_shape ;
}
2022-05-03 01:43:50 +02:00
void DisplayServerWindows : : cursor_set_custom_image ( const Ref < Resource > & p_cursor , CursorShape p_shape , const Vector2 & p_hotspot ) {
2020-03-09 16:56:48 +01:00
_THREAD_SAFE_METHOD_
if ( p_cursor . is_valid ( ) ) {
2022-05-13 15:04:37 +02:00
RBMap < CursorShape , Vector < Variant > > : : Element * cursor_c = cursors_cache . find ( p_shape ) ;
2020-03-09 16:56:48 +01: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 ) ;
}
Ref < Texture2D > texture = p_cursor ;
Ref < AtlasTexture > atlas_texture = p_cursor ;
Ref < Image > image ;
Size2 texture_size ;
Rect2 atlas_rect ;
if ( texture . is_valid ( ) ) {
2021-03-28 13:32:17 +02:00
image = texture - > get_image ( ) ;
2020-03-09 16:56:48 +01:00
}
if ( ! image . is_valid ( ) & & atlas_texture . is_valid ( ) ) {
texture = atlas_texture - > get_atlas ( ) ;
atlas_rect . size . width = texture - > get_width ( ) ;
atlas_rect . size . height = texture - > get_height ( ) ;
atlas_rect . position . x = atlas_texture - > get_region ( ) . position . x ;
atlas_rect . position . y = atlas_texture - > get_region ( ) . position . y ;
texture_size . width = atlas_texture - > get_region ( ) . size . x ;
texture_size . height = atlas_texture - > get_region ( ) . size . y ;
} else if ( image . is_valid ( ) ) {
texture_size . width = texture - > get_width ( ) ;
texture_size . height = texture - > get_height ( ) ;
}
ERR_FAIL_COND ( ! texture . is_valid ( ) ) ;
ERR_FAIL_COND ( p_hotspot . x < 0 | | p_hotspot . y < 0 ) ;
ERR_FAIL_COND ( texture_size . width > 256 | | texture_size . height > 256 ) ;
ERR_FAIL_COND ( p_hotspot . x > texture_size . width | | p_hotspot . y > texture_size . height ) ;
2021-03-28 13:32:17 +02:00
image = texture - > get_image ( ) ;
2020-03-09 16:56:48 +01:00
ERR_FAIL_COND ( ! image . is_valid ( ) ) ;
UINT image_size = texture_size . width * texture_size . height ;
2021-09-10 15:44:44 +02:00
// Create the BITMAP with alpha channel.
2022-08-30 17:50:56 +02: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 16:56:48 +01: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 ;
if ( atlas_texture . is_valid ( ) ) {
column_index = MIN ( column_index , atlas_rect . size . width - 1 ) ;
row_index = MIN ( row_index , atlas_rect . size . height - 1 ) ;
}
2022-08-30 17:50:56 +02:00
const Color & c = image - > get_pixel ( column_index , row_index ) ;
fully_transparent = fully_transparent & & ( c . a = = 0.f ) ;
2020-03-09 16:56:48 +01:00
2022-08-30 17:50:56 +02:00
* ( buffer + index ) = c . to_argb32 ( ) ;
2020-03-09 16:56:48 +01:00
}
2021-09-10 15:44:44 +02:00
// Finally, create the icon.
if ( cursors [ p_shape ] ) {
2020-03-09 16:56:48 +01:00
DestroyIcon ( cursors [ p_shape ] ) ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
2022-08-30 17:50:56 +02: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 16:56:48 +01: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 17:50:56 +02:00
DeleteObject ( mask ) ;
2020-03-09 16:56:48 +01:00
DeleteObject ( bitmap ) ;
2022-08-30 17:50:56 +02:00
ReleaseDC ( nullptr , dc ) ;
2020-03-09 16:56:48 +01:00
} else {
2021-09-10 15:44:44 +02:00
// Reset to default system cursor.
2020-03-09 16:56:48 +01:00
if ( cursors [ p_shape ] ) {
DestroyIcon ( cursors [ p_shape ] ) ;
}
2022-08-30 17:50:56 +02:00
cursors [ p_shape ] = nullptr ;
2020-03-09 16:56:48 +01:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
cursors_cache . erase ( p_shape ) ;
}
}
2020-07-09 01:02:38 +02:00
bool DisplayServerWindows : : get_swap_cancel_ok ( ) {
2020-03-09 16:56:48 +01:00
return true ;
}
void DisplayServerWindows : : enable_for_stealing_focus ( OS : : ProcessID pid ) {
_THREAD_SAFE_METHOD_
AllowSetForegroundWindow ( pid ) ;
}
2020-06-11 11:27:07 +02:00
int DisplayServerWindows : : keyboard_get_layout_count ( ) const {
2021-04-29 11:47:24 +02:00
return GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 11:27:07 +02:00
}
2020-03-09 16:56:48 +01:00
2020-06-11 11:27:07 +02:00
int DisplayServerWindows : : keyboard_get_current_layout ( ) const {
HKL cur_layout = GetKeyboardLayout ( 0 ) ;
2021-04-29 11:47:24 +02:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 11:27:07 +02: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 11:47:24 +02:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 11:27:07 +02: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 11:47:24 +02:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-03-09 16:56:48 +01:00
2020-06-11 11:27:07 +02:00
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
2020-03-09 16:56:48 +01:00
2020-06-11 11:27:07 +02:00
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
2020-03-09 16:56:48 +01:00
2020-07-27 12:43:20 +02:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2020-06-11 11:27:07 +02:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
memfree ( layouts ) ;
2020-07-27 12:43:20 +02:00
return String : : utf16 ( ( const char16_t * ) buf ) . substr ( 0 , 2 ) ;
2020-06-11 11:27:07 +02:00
}
2021-09-17 03:07:59 +02:00
Key DisplayServerWindows : : keyboard_get_keycode_from_physical ( Key p_keycode ) const {
2021-08-13 23:31:57 +02:00
Key modifiers = p_keycode & KeyModifierMask : : MODIFIER_MASK ;
Key keycode_no_mod = ( Key ) ( p_keycode & KeyModifierMask : : CODE_MASK ) ;
2021-09-17 03:07:59 +02:00
2021-08-13 23:31:57 +02: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 03:07:59 +02: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 23:31:57 +02:00
if ( char_code = = ( unsigned int ) Key : : BRACKETLEFT | | char_code = = ( unsigned int ) Key : : BRACKETRIGHT ) {
2021-09-17 03:07:59 +02:00
char_code + = 32 ;
}
2021-08-13 23:31:57 +02:00
return ( Key ) ( char_code | ( unsigned int ) modifiers ) ;
2021-09-17 03:07:59 +02:00
}
return ( Key ) ( KeyMappingWindows : : get_keysym ( vk ) | modifiers ) ;
}
2020-06-11 11:27:07 +02:00
String _get_full_layout_name_from_registry ( HKL p_layout ) {
String id = " SYSTEM \\ CurrentControlSet \\ Control \\ Keyboard Layouts \\ " + String : : num_int64 ( ( int64_t ) p_layout , 16 , false ) . lpad ( 8 , " 0 " ) ;
String ret ;
HKEY hkey ;
2020-07-27 12:43:20 +02:00
WCHAR layout_text [ 1024 ] ;
memset ( layout_text , 0 , 1024 * sizeof ( WCHAR ) ) ;
2020-06-11 11:27:07 +02:00
2020-07-27 12:43:20 +02:00
if ( RegOpenKeyExW ( HKEY_LOCAL_MACHINE , ( LPCWSTR ) ( id . utf16 ( ) . get_data ( ) ) , 0 , KEY_QUERY_VALUE , & hkey ) ! = ERROR_SUCCESS ) {
2020-06-11 11:27:07 +02:00
return ret ;
2020-03-09 16:56:48 +01:00
}
2020-06-11 11:27:07 +02:00
DWORD buffer = 1024 ;
DWORD vtype = REG_SZ ;
2021-04-29 11:47:24 +02:00
if ( RegQueryValueExW ( hkey , L " Layout Text " , nullptr , & vtype , ( LPBYTE ) layout_text , & buffer ) = = ERROR_SUCCESS ) {
2020-07-27 12:43:20 +02:00
ret = String : : utf16 ( ( const char16_t * ) layout_text ) ;
2020-03-09 16:56:48 +01:00
}
2020-06-11 11:27:07 +02:00
RegCloseKey ( hkey ) ;
return ret ;
}
String DisplayServerWindows : : keyboard_get_layout_name ( int p_index ) const {
2021-04-29 11:47:24 +02:00
int layout_count = GetKeyboardLayoutList ( 0 , nullptr ) ;
2020-06-11 11:27:07 +02:00
ERR_FAIL_INDEX_V ( p_index , layout_count , " " ) ;
HKL * layouts = ( HKL * ) memalloc ( layout_count * sizeof ( HKL ) ) ;
GetKeyboardLayoutList ( layout_count , layouts ) ;
2020-03-09 16:56:48 +01:00
2020-06-11 11:27:07 +02: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 10:42:46 +01:00
if ( ret . is_empty ( ) ) {
2020-07-27 12:43:20 +02:00
WCHAR buf [ LOCALE_NAME_MAX_LENGTH ] ;
memset ( buf , 0 , LOCALE_NAME_MAX_LENGTH * sizeof ( WCHAR ) ) ;
2020-06-11 11:27:07 +02:00
LCIDToLocaleName ( MAKELCID ( LOWORD ( layouts [ p_index ] ) , SORT_DEFAULT ) , buf , LOCALE_NAME_MAX_LENGTH , 0 ) ;
2020-07-27 12:43:20 +02:00
WCHAR name [ 1024 ] ;
memset ( name , 0 , 1024 * sizeof ( WCHAR ) ) ;
2020-06-11 11:27:07 +02:00
GetLocaleInfoEx ( buf , LOCALE_SLOCALIZEDDISPLAYNAME , ( LPWSTR ) & name , 1024 ) ;
2020-07-27 12:43:20 +02:00
ret = String : : utf16 ( ( const char16_t * ) name ) ;
2020-03-09 16:56:48 +01:00
}
2020-06-11 11:27:07 +02:00
memfree ( layouts ) ;
2020-03-09 16:56:48 +01:00
2020-06-11 11:27:07 +02:00
return ret ;
2020-03-09 16:56:48 +01:00
}
void DisplayServerWindows : : process_events ( ) {
_THREAD_SAFE_METHOD_
MSG msg ;
if ( ! drop_events ) {
joypad - > process_joypads ( ) ;
}
2020-04-02 01:20:12 +02:00
while ( PeekMessageW ( & msg , nullptr , 0 , 0 , PM_REMOVE ) ) {
2020-03-09 16:56:48 +01:00
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
if ( ! drop_events ) {
_process_key_events ( ) ;
2021-08-13 00:38:41 +02:00
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
2020-03-09 16:56:48 +01:00
}
}
void DisplayServerWindows : : force_process_and_drop_events ( ) {
_THREAD_SAFE_METHOD_
drop_events = true ;
process_events ( ) ;
drop_events = false ;
}
void DisplayServerWindows : : release_rendering_thread ( ) {
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
void DisplayServerWindows : : make_rendering_thread ( ) {
}
2020-05-14 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
void DisplayServerWindows : : swap_buffers ( ) {
2021-10-26 17:18:39 +02:00
# if defined(GLES3_ENABLED)
2022-05-09 01:25:49 +02:00
if ( gl_manager ) {
gl_manager - > swap_buffers ( ) ;
}
2020-11-18 19:11:30 +01:00
# endif
2020-03-09 16:56:48 +01:00
}
void DisplayServerWindows : : set_native_icon ( const String & p_filename ) {
_THREAD_SAFE_METHOD_
2022-03-23 10:08:58 +01: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 16:56:48 +01: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 ) ;
icon_dir = ( ICONDIR * ) memrealloc ( icon_dir , 3 * sizeof ( WORD ) + icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ;
f - > get_buffer ( ( uint8_t * ) & icon_dir - > idEntries [ 0 ] , icon_dir - > idCount * sizeof ( ICONDIRENTRY ) ) ;
2021-09-10 15:44:44 +02:00
int small_icon_index = - 1 ; // Select 16x16 with largest color count.
2020-03-09 16:56:48 +01:00
int small_icon_cc = 0 ;
2021-09-10 15:44:44 +02:00
int big_icon_index = - 1 ; // Select largest.
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
// Read the big icon.
2020-03-09 16:56:48 +01: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 ) ;
ERR_FAIL_COND_MSG ( ! icon_big , " Could not create " + itos ( big_icon_width ) + " x " + itos ( big_icon_width ) + " @ " + itos ( big_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
2021-09-10 15:44:44 +02:00
// Read the small icon.
2020-03-09 16:56:48 +01: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 ) ;
ERR_FAIL_COND_MSG ( ! icon_small , " Could not create 16x16 @ " + itos ( small_icon_cc ) + " icon, error: " + format_error_message ( GetLastError ( ) ) + " . " ) ;
2021-09-10 15:44:44 +02:00
// Online tradition says to be sure last error is cleared and set the small icon first.
2020-03-09 16:56:48 +01: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 14:29:06 +02:00
2020-03-09 16:56:48 +01:00
void DisplayServerWindows : : set_icon ( const Ref < Image > & p_icon ) {
_THREAD_SAFE_METHOD_
ERR_FAIL_COND ( ! p_icon . is_valid ( ) ) ;
2022-05-10 10:12:04 +02:00
if ( icon ! = p_icon ) {
icon = p_icon - > duplicate ( ) ;
if ( icon - > get_format ( ) ! = Image : : FORMAT_RGBA8 ) {
icon - > convert ( Image : : FORMAT_RGBA8 ) ;
}
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
int w = icon - > get_width ( ) ;
int h = icon - > get_height ( ) ;
2021-09-10 15:44:44 +02:00
// Create temporary bitmap buffer.
2020-03-09 16:56:48 +01:00
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 = icon - > 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 hicon = CreateIconFromResource ( icon_bmp , icon_len , TRUE , 0x00030000 ) ;
2021-09-10 15:44:44 +02:00
// Set the icon for the window.
2020-03-09 16:56:48 +01:00
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_SMALL , ( LPARAM ) hicon ) ;
2021-09-10 15:44:44 +02:00
// Set the icon in the task manager (should we do this?).
2020-03-09 16:56:48 +01:00
SendMessage ( windows [ MAIN_WINDOW_ID ] . hWnd , WM_SETICON , ICON_BIG , ( LPARAM ) hicon ) ;
}
2021-06-19 17:44:59 +02:00
void DisplayServerWindows : : window_set_vsync_mode ( DisplayServer : : VSyncMode p_vsync_mode , WindowID p_window ) {
_THREAD_SAFE_METHOD_
# if defined(VULKAN_ENABLED)
2022-05-09 01:25:49 +02:00
if ( context_vulkan ) {
context_vulkan - > set_vsync_mode ( p_window , p_vsync_mode ) ;
}
2021-06-19 17:44:59 +02:00
# endif
2020-03-09 16:56:48 +01:00
}
2020-05-14 14:29:06 +02:00
2021-06-19 17:44:59 +02:00
DisplayServer : : VSyncMode DisplayServerWindows : : window_get_vsync_mode ( WindowID p_window ) const {
_THREAD_SAFE_METHOD_
# if defined(VULKAN_ENABLED)
2022-05-09 01:25:49 +02:00
if ( context_vulkan ) {
return context_vulkan - > get_vsync_mode ( p_window ) ;
}
2021-06-19 17:44:59 +02:00
# endif
2021-09-27 01:07:10 +02:00
return DisplayServer : : VSYNC_ENABLED ;
2020-03-09 16:56:48 +01: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?
# define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
2021-09-10 15:44:44 +02:00
// This one tells whether the event comes from touchscreen (and not from pen).
2020-03-09 16:56:48 +01:00
# define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
void DisplayServerWindows : : _touch_event ( WindowID p_window , bool p_pressed , float p_x , float p_y , int idx ) {
2021-09-10 15:44:44 +02:00
if ( touch_state . has ( idx ) = = p_pressed ) {
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
if ( p_pressed ) {
touch_state . insert ( idx , Vector2 ( p_x , p_y ) ) ;
} else {
touch_state . erase ( idx ) ;
}
Ref < InputEventScreenTouch > event ;
2021-06-18 00:03:09 +02:00
event . instantiate ( ) ;
2020-03-09 16:56:48 +01: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-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( event ) ;
2020-03-09 16:56:48 +01:00
}
void DisplayServerWindows : : _drag_event ( WindowID p_window , float p_x , float p_y , int idx ) {
2022-05-13 15:04:37 +02:00
RBMap < int , Vector2 > : : Element * curr = touch_state . find ( idx ) ;
2021-09-10 15:44:44 +02:00
if ( ! curr ) {
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
2021-09-10 15:44:44 +02:00
if ( curr - > get ( ) = = Vector2 ( p_x , p_y ) ) {
2020-03-09 16:56:48 +01:00
return ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
Ref < InputEventScreenDrag > event ;
2021-06-18 00:03:09 +02:00
event . instantiate ( ) ;
2020-03-09 16:56:48 +01: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 ( ) ) ;
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( event ) ;
2020-03-09 16:56:48 +01:00
curr - > get ( ) = Vector2 ( p_x , p_y ) ;
}
void DisplayServerWindows : : _send_window_event ( const WindowData & wd , WindowEvent p_event ) {
if ( ! wd . event_callback . is_null ( ) ) {
Variant event = int ( p_event ) ;
Variant * eventp = & event ;
Variant ret ;
Callable : : CallError ce ;
2022-07-28 22:56:41 +02:00
wd . event_callback . callp ( ( const Variant * * ) & eventp , 1 , ret , ce ) ;
2020-03-09 16:56:48 +01:00
}
}
void DisplayServerWindows : : _dispatch_input_events ( const Ref < InputEvent > & p_event ) {
2022-04-05 12:40:26 +02:00
static_cast < DisplayServerWindows * > ( get_singleton ( ) ) - > _dispatch_input_event ( p_event ) ;
2020-03-09 16:56:48 +01:00
}
void DisplayServerWindows : : _dispatch_input_event ( const Ref < InputEvent > & p_event ) {
2020-04-02 16:06:04 +02:00
_THREAD_SAFE_METHOD_
if ( in_dispatch_input_event ) {
return ;
}
in_dispatch_input_event = true ;
2020-03-09 16:56:48 +01:00
Variant ev = p_event ;
Variant * evp = & ev ;
Variant ret ;
Callable : : CallError ce ;
2022-02-24 10:21:23 +01:00
{
2022-04-13 08:58:38 +02:00
List < WindowID > : : Element * E = popup_list . back ( ) ;
2022-02-24 10:21:23 +01: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 ( ) ) {
2022-07-28 22:56:41 +02:00
callable . callp ( ( const Variant * * ) & evp , 1 , ret , ce ) ;
2022-02-24 10:21:23 +01:00
}
}
in_dispatch_input_event = false ;
return ;
}
}
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
// Send to a single window.
2022-02-24 10:21:23 +01: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 ( ) ) {
2022-07-28 22:56:41 +02:00
callable . callp ( ( const Variant * * ) & evp , 1 , ret , ce ) ;
2022-02-24 10:21:23 +01:00
}
2020-03-09 16:56:48 +01:00
}
} else {
2021-09-10 15:44:44 +02:00
// Send to all windows.
2021-08-09 22:13:42 +02:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
const Callable callable = E . value . input_event_callback ;
2022-02-24 10:21:23 +01:00
if ( callable . is_valid ( ) ) {
2022-07-28 22:56:41 +02:00
callable . callp ( ( const Variant * * ) & evp , 1 , ret , ce ) ;
2020-03-09 16:56:48 +01:00
}
}
}
2020-04-02 16:06:04 +02:00
in_dispatch_input_event = false ;
2020-03-09 16:56:48 +01:00
}
2022-02-24 10:21:23 +01: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 11:34:27 +02:00
_THREAD_SAFE_METHOD_
2022-04-05 12:40:26 +02:00
const WindowData & wd = windows [ p_window ] ;
2022-02-24 10:21:23 +01:00
if ( wd . is_popup ) {
2022-04-05 11:34:27 +02:00
// Find current popup parent, or root popup if new window is not transient.
List < WindowID > : : Element * C = nullptr ;
2022-02-24 10:21:23 +01: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 11:34:27 +02:00
C = E ;
E = E - > prev ( ) ;
2022-02-24 10:21:23 +01:00
} else {
break ;
}
}
2022-04-05 11:34:27 +02:00
if ( C ) {
_send_window_event ( windows [ C - > get ( ) ] , DisplayServerWindows : : WINDOW_EVENT_CLOSE_REQUEST ) ;
}
2022-02-24 10:21:23 +01: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 11:34:27 +02:00
_THREAD_SAFE_METHOD_
2022-02-24 10:21:23 +01:00
List < WindowID > : : Element * E = popup_list . find ( p_window ) ;
while ( E ) {
List < WindowID > : : Element * F = E - > next ( ) ;
2022-04-05 11:34:27 +02:00
WindowID win_id = E - > get ( ) ;
2022-02-24 10:21:23 +01:00
popup_list . erase ( E ) ;
2022-04-05 11:34:27 +02:00
2022-05-13 14:22:29 +02: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 10:21:23 +01:00
E = F ;
}
}
LRESULT DisplayServerWindows : : MouseProc ( int code , WPARAM wParam , LPARAM lParam ) {
_THREAD_SAFE_METHOD_
2022-04-05 11:34:27 +02:00
2022-02-24 10:21:23 +01: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 14:22:29 +02:00
case WM_RBUTTONDOWN :
2022-02-24 10:21:23 +01:00
case WM_MBUTTONDOWN : {
MOUSEHOOKSTRUCT * ms = ( MOUSEHOOKSTRUCT * ) lParam ;
Point2i pos = Point2i ( ms - > pt . x , ms - > pt . y ) ;
2022-04-05 11:34:27 +02:00
List < WindowID > : : Element * C = nullptr ;
2022-02-24 10:21:23 +01:00
List < WindowID > : : Element * E = popup_list . back ( ) ;
2022-04-05 11:34:27 +02:00
// Find top popup to close.
2022-02-24 10:21:23 +01:00
while ( E ) {
// Popup window area.
Rect2i win_rect = Rect2i ( window_get_position ( E - > get ( ) ) , window_get_size ( E - > get ( ) ) ) ;
// 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 11:34:27 +02:00
C = E ;
E = E - > prev ( ) ;
2022-02-24 10:21:23 +01:00
}
}
2022-04-05 11:34:27 +02:00
if ( C ) {
_send_window_event ( windows [ C - > get ( ) ] , DisplayServerWindows : : WINDOW_EVENT_CLOSE_REQUEST ) ;
2022-05-30 10:06:08 +02:00
return 1 ;
2022-04-05 11:34:27 +02:00
}
2022-02-24 10:21:23 +01:00
} break ;
}
}
return : : CallNextHookEx ( mouse_monitor , code , wParam , lParam ) ;
}
2022-05-09 01:25:49 +02: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 15:44:44 +02:00
// See: https://docs.microsoft.com/en-us/windows/win32/winmsg/window-procedures
2020-03-09 16:56:48 +01: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 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
WindowID window_id = INVALID_WINDOW_ID ;
2020-06-27 18:26:34 +02:00
bool window_created = false ;
2020-03-09 16:56:48 +01:00
2022-05-09 01:25:49 +02: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 22:13:42 +02:00
for ( const KeyValue < WindowID , WindowData > & E : windows ) {
if ( E . value . hWnd = = hWnd ) {
window_id = E . key ;
2020-06-27 18:26:34 +02:00
window_created = true ;
2020-03-09 16:56:48 +01:00
break ;
}
}
2022-05-09 01:25:49 +02: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 18:26:34 +02:00
if ( ! window_created ) {
2022-05-09 01:25:49 +02: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 18:26:34 +02:00
}
2021-09-10 15:44:44 +02:00
// Process window messages.
switch ( uMsg ) {
2022-02-24 10:21:23 +01:00
case WM_MOUSEACTIVATE : {
if ( windows [ window_id ] . no_focus ) {
return MA_NOACTIVATEANDEAT ; // Do not activate, and discard mouse messages.
} else if ( windows [ window_id ] . is_popup ) {
return MA_NOACTIVATE ; // Do not activate, but process mouse messages.
}
} break ;
2020-03-09 16:56:48 +01:00
case WM_SETFOCUS : {
windows [ window_id ] . window_has_focus = true ;
last_focused_window = window_id ;
2021-09-10 15:44:44 +02:00
// Restore mouse mode.
2020-03-09 16:56:48 +01:00
_set_mouse_mode_impl ( mouse_mode ) ;
2020-06-30 01:47:18 +02:00
if ( ! app_focused ) {
if ( OS : : get_singleton ( ) - > get_main_loop ( ) ) {
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_APPLICATION_FOCUS_IN ) ;
}
app_focused = true ;
}
2021-09-10 15:44:44 +02:00
} break ;
2020-03-09 16:56:48 +01:00
case WM_KILLFOCUS : {
windows [ window_id ] . window_has_focus = false ;
last_focused_window = window_id ;
2021-09-10 15:44:44 +02:00
// Release capture unconditionally because it can be set due to dragging, in addition to captured mode.
2020-03-09 16:56:48 +01:00
ReleaseCapture ( ) ;
2021-09-10 15:44:44 +02:00
// Release every touch to avoid sticky points.
2021-08-09 22:13:42 +02:00
for ( const KeyValue < int , Vector2 > & E : touch_state ) {
_touch_event ( window_id , false , E . value . x , E . value . y , E . key ) ;
2020-03-09 16:56:48 +01:00
}
touch_state . clear ( ) ;
2020-06-30 01:47:18 +02:00
bool self_steal = false ;
HWND new_hwnd = ( HWND ) wParam ;
if ( IsWindow ( new_hwnd ) ) {
self_steal = true ;
}
if ( ! self_steal ) {
if ( OS : : get_singleton ( ) - > get_main_loop ( ) ) {
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_APPLICATION_FOCUS_OUT ) ;
}
app_focused = false ;
}
2021-09-10 15:44:44 +02:00
} break ;
case WM_ACTIVATE : { // Watch for window activate message.
2021-01-05 20:37:45 +01:00
if ( ! windows [ window_id ] . window_focused ) {
_process_activate_event ( window_id , wParam , lParam ) ;
} else {
windows [ window_id ] . saved_wparam = wParam ;
windows [ window_id ] . saved_lparam = lParam ;
2020-05-05 13:16:02 +02:00
2021-01-05 20:37:45 +01:00
// Run a timer to prevent event catching warning if the focused window is closing.
windows [ window_id ] . focus_timer_id = SetTimer ( windows [ window_id ] . hWnd , 2 , USER_TIMER_MINIMUM , ( TIMERPROC ) nullptr ) ;
}
2021-12-28 11:27:46 +01:00
if ( wParam ! = WA_INACTIVE ) {
track_mouse_leave_event ( hWnd ) ;
}
2021-09-10 15:44:44 +02:00
return 0 ; // Return to the message loop.
} break ;
2020-03-09 16:56:48 +01:00
case WM_GETMINMAXINFO : {
if ( windows [ window_id ] . resizable & & ! windows [ window_id ] . fullscreen ) {
2021-08-23 04:30:28 +02:00
// Size of window decorations.
Size2 decor = window_get_real_size ( window_id ) - window_get_size ( window_id ) ;
2020-03-09 16:56:48 +01: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 ;
}
return 0 ;
}
2021-09-10 15:44:44 +02:00
} break ;
case WM_PAINT : {
2020-03-09 16:56:48 +01:00
Main : : force_redraw ( ) ;
2021-09-10 15:44:44 +02:00
} break ;
2022-08-29 10:24:48 +02:00
case WM_SETTINGCHANGE : {
if ( lParam & & CompareStringOrdinal ( reinterpret_cast < LPCWCH > ( lParam ) , - 1 , L " ImmersiveColorSet " , - 1 , true ) = = CSTR_EQUAL ) {
2022-09-02 20:44:58 +02:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 10:24:48 +02:00
BOOL value = is_dark_mode ( ) ;
: : DwmSetWindowAttribute ( windows [ window_id ] . hWnd , DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
}
}
} break ;
case WM_THEMECHANGED : {
2022-09-02 20:44:58 +02:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 10:24:48 +02:00
BOOL value = is_dark_mode ( ) ;
: : DwmSetWindowAttribute ( windows [ window_id ] . hWnd , DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
}
} break ;
2021-09-10 15:44:44 +02:00
case WM_SYSCOMMAND : // Intercept system commands.
2020-03-09 16:56:48 +01:00
{
2021-09-10 15:44:44 +02:00
switch ( wParam ) // Check system calls.
2020-03-09 16:56:48 +01:00
{
2021-09-10 15:44:44 +02:00
case SC_SCREENSAVE : // Screensaver trying to start?
case SC_MONITORPOWER : // Monitor trying to enter powersave?
return 0 ; // Prevent from happening.
2020-03-09 16:56:48 +01:00
case SC_KEYMENU :
2022-02-16 13:56:32 +01:00
if ( ( lParam > > 16 ) < = 0 ) {
2020-03-09 16:56:48 +01:00
return 0 ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
}
2021-09-10 15:44:44 +02:00
} break ;
case WM_CLOSE : // Did we receive a close message?
2020-03-09 16:56:48 +01:00
{
2021-01-05 20:37:45 +01:00
if ( windows [ window_id ] . focus_timer_id ! = 0U ) {
KillTimer ( windows [ window_id ] . hWnd , windows [ window_id ] . focus_timer_id ) ;
2020-12-11 15:28:46 +01:00
}
2020-03-09 16:56:48 +01:00
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_CLOSE_REQUEST ) ;
2021-09-10 15:44:44 +02:00
return 0 ; // Jump back.
2020-03-09 16:56:48 +01:00
}
case WM_MOUSELEAVE : {
old_invalid = true ;
2022-07-07 21:09:37 +02:00
windows [ window_id ] . mouse_outside = true ;
2020-03-09 16:56:48 +01:00
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_EXIT ) ;
} break ;
case WM_INPUT : {
if ( mouse_mode ! = MOUSE_MODE_CAPTURED | | ! use_raw_input ) {
break ;
}
UINT dwSize ;
2020-04-02 01:20:12 +02:00
GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , nullptr , & dwSize , sizeof ( RAWINPUTHEADER ) ) ;
2020-03-09 16:56:48 +01:00
LPBYTE lpb = new BYTE [ dwSize ] ;
2020-04-02 01:20:12 +02:00
if ( lpb = = nullptr ) {
2020-03-09 16:56:48 +01:00
return 0 ;
}
2022-02-16 13:56:32 +01:00
if ( GetRawInputData ( ( HRAWINPUT ) lParam , RID_INPUT , lpb , & dwSize , sizeof ( RAWINPUTHEADER ) ) ! = dwSize ) {
2020-03-09 16:56:48 +01:00
OutputDebugString ( TEXT ( " GetRawInputData does not return correct size ! \n " ) ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
RAWINPUT * raw = ( RAWINPUT * ) lpb ;
if ( raw - > header . dwType = = RIM_TYPEMOUSE ) {
Ref < InputEventMouseMotion > mm ;
2021-06-18 00:03:09 +02:00
mm . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
mm - > set_window_id ( window_id ) ;
2021-04-24 22:33:50 +02:00
mm - > set_ctrl_pressed ( control_mem ) ;
mm - > set_shift_pressed ( shift_mem ) ;
mm - > set_alt_pressed ( alt_mem ) ;
2020-05-05 13:16:02 +02:00
2020-05-03 14:38:58 +02:00
mm - > set_pressure ( ( raw - > data . mouse . ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN ) ? 1.0f : 0.0f ) ;
2020-03-09 16:56:48 +01:00
mm - > set_button_mask ( last_button_state ) ;
Point2i c ( windows [ window_id ] . width / 2 , windows [ window_id ] . height / 2 ) ;
2021-09-10 15:44:44 +02:00
// Centering just so it works as before.
2020-03-09 16:56:48 +01: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 14:22:22 +01:00
mm - > set_velocity ( Vector2 ( 0 , 0 ) ) ;
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
POINT coords ; // Client coords.
2020-03-09 16:56:48 +01: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 ;
}
2021-09-10 15:44:44 +02:00
if ( windows [ window_id ] . window_has_focus & & mm - > get_relative ( ) ! = Vector2 ( ) ) {
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2021-09-10 15:44:44 +02:00
}
2020-03-09 16:56:48 +01:00
}
delete [ ] lpb ;
} break ;
2020-05-05 13:16:02 +02:00
case WT_CSRCHANGE :
case WT_PROXIMITY : {
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2020-05-05 13:16:02 +02: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 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2020-05-05 13:16:02 +02:00
PACKET packet ;
if ( wintab_WTPacket ( windows [ window_id ] . wtctx , wParam , & packet ) ) {
float pressure = float ( packet . pkNormalPressure - windows [ window_id ] . min_pressure ) / float ( windows [ window_id ] . max_pressure - windows [ window_id ] . min_pressure ) ;
windows [ window_id ] . last_pressure = pressure ;
windows [ window_id ] . last_pressure_update = 0 ;
double azim = ( packet . pkOrientation . orAzimuth / 10.0f ) * ( Math_PI / 180 ) ;
double alt = Math : : tan ( ( Math : : abs ( packet . pkOrientation . orAltitude / 10.0f ) ) * ( Math_PI / 180 ) ) ;
if ( windows [ window_id ] . tilt_supported ) {
windows [ window_id ] . last_tilt = Vector2 ( Math : : atan ( Math : : sin ( azim ) / alt ) , Math : : atan ( Math : : cos ( azim ) / alt ) ) ;
} else {
windows [ window_id ] . last_tilt = Vector2 ( ) ;
}
2020-05-08 09:56:42 +02:00
2022-06-19 11:58:24 +02:00
windows [ window_id ] . last_pen_inverted = packet . pkStatus & TPS_INVERT ;
2020-05-08 09:56:42 +02:00
POINT coords ;
GetCursorPos ( & coords ) ;
ScreenToClient ( windows [ window_id ] . hWnd , & coords ) ;
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2022-02-16 13:56:32 +01:00
if ( ! windows [ window_id ] . window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-05-08 09:56:42 +02:00
break ;
2022-02-16 13:56:32 +01:00
}
2020-05-08 09:56:42 +02:00
Ref < InputEventMouseMotion > mm ;
2021-06-18 00:03:09 +02:00
mm . instantiate ( ) ;
2020-05-08 09:56:42 +02:00
mm - > set_window_id ( window_id ) ;
2021-04-24 22:33:50 +02: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 09:56:42 +02:00
mm - > set_pressure ( windows [ window_id ] . last_pressure ) ;
mm - > set_tilt ( windows [ window_id ] . last_tilt ) ;
2022-06-19 11:58:24 +02:00
mm - > set_pen_inverted ( windows [ window_id ] . last_pen_inverted ) ;
2020-05-08 09:56:42 +02: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 14:22:22 +01:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2020-05-08 09:56:42 +02: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 ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2022-02-24 10:21:23 +01:00
if ( windows [ window_id ] . window_has_focus | | window_get_active_popup ( ) = = window_id ) {
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2022-02-16 13:56:32 +01:00
}
2020-05-05 13:16:02 +02:00
}
return 0 ;
}
} break ;
2020-05-22 09:44:47 +02:00
case WM_POINTERENTER : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) ! = " winink " ) | | ! winink_available ) {
2020-05-22 09:44:47 +02: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 16:56:48 +01:00
case WM_POINTERUPDATE : {
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) ! = " winink " ) | | ! winink_available ) {
2020-03-09 16:56:48 +01:00
break ;
}
uint32_t pointer_id = LOWORD ( wParam ) ;
POINTER_INPUT_TYPE pointer_type = PT_POINTER ;
if ( ! win8p_GetPointerType ( pointer_id , & pointer_type ) ) {
break ;
}
if ( pointer_type ! = PT_PEN ) {
break ;
}
POINTER_PEN_INFO pen_info ;
if ( ! win8p_GetPointerPenInfo ( pointer_id , & pen_info ) ) {
break ;
}
2020-04-28 15:19:37 +02:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 15:44:44 +02:00
// Universal translation enabled; ignore OS translation.
2020-03-09 16:56:48 +01:00
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
2022-07-07 21:09:37 +02:00
if ( windows [ window_id ] . mouse_outside ) {
2021-09-10 15:44:44 +02:00
// Mouse enter.
2020-03-09 16:56:48 +01:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_ENTER ) ;
}
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2022-07-07 21:09:37 +02:00
windows [ window_id ] . mouse_outside = false ;
2020-03-09 16:56:48 +01:00
2021-09-10 15:44:44 +02:00
// Once-off notification, must call again.
2021-12-28 11:27:46 +01:00
track_mouse_leave_event ( hWnd ) ;
2020-03-09 16:56:48 +01:00
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2021-03-31 00:35:08 +02:00
if ( ! windows [ window_id ] . window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-03-09 16:56:48 +01:00
break ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
Ref < InputEventMouseMotion > mm ;
2021-06-18 00:03:09 +02:00
mm . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
mm - > set_window_id ( window_id ) ;
2020-05-03 14:38:58 +02:00
if ( pen_info . penMask & PEN_MASK_PRESSURE ) {
mm - > set_pressure ( ( float ) pen_info . pressure / 1024 ) ;
} else {
mm - > set_pressure ( ( HIWORD ( wParam ) & POINTER_MESSAGE_FLAG_FIRSTBUTTON ) ? 1.0f : 0.0f ) ;
}
if ( ( pen_info . penMask & PEN_MASK_TILT_X ) & & ( pen_info . penMask & PEN_MASK_TILT_Y ) ) {
mm - > set_tilt ( Vector2 ( ( float ) pen_info . tiltX / 90 , ( float ) pen_info . tiltY / 90 ) ) ;
}
2022-06-19 11:58:24 +02:00
mm - > set_pen_inverted ( pen_info . penFlags & ( PEN_FLAG_INVERTED | PEN_FLAG_ERASER ) ) ;
2020-03-09 16:56:48 +01:00
2021-04-24 22:33:50 +02: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 16:56:48 +01:00
mm - > set_button_mask ( last_button_state ) ;
2021-09-10 15:44:44 +02:00
POINT coords ; // Client coords.
2020-03-09 16:56:48 +01: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 14:22:22 +01:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2020-03-09 16:56:48 +01: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 ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2022-02-24 10:21:23 +01:00
if ( windows [ window_id ] . window_has_focus | | window_get_active_popup ( ) = = window_id ) {
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2020-03-09 16:56:48 +01:00
}
2021-09-10 15:44:44 +02:00
return 0 ; // Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event.
2020-03-09 16:56:48 +01:00
} break ;
case WM_MOUSEMOVE : {
2020-05-22 09:44:47 +02:00
if ( windows [ window_id ] . block_mm ) {
break ;
}
2020-03-09 16:56:48 +01:00
if ( mouse_mode = = MOUSE_MODE_CAPTURED & & use_raw_input ) {
break ;
}
2020-04-28 15:19:37 +02:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 15:44:44 +02:00
// Universal translation enabled; ignore OS translation.
2020-03-09 16:56:48 +01:00
LPARAM extra = GetMessageExtraInfo ( ) ;
if ( IsTouchEvent ( extra ) ) {
break ;
}
}
2022-07-07 21:09:37 +02:00
if ( windows [ window_id ] . mouse_outside ) {
2021-09-10 15:44:44 +02:00
// Mouse enter.
2020-03-09 16:56:48 +01:00
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
_send_window_event ( windows [ window_id ] , WINDOW_EVENT_MOUSE_ENTER ) ;
}
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2022-07-07 21:09:37 +02:00
windows [ window_id ] . mouse_outside = false ;
2020-03-09 16:56:48 +01:00
2021-09-10 15:44:44 +02:00
// Once-off notification, must call again.
2021-12-28 11:27:46 +01:00
track_mouse_leave_event ( hWnd ) ;
2020-03-09 16:56:48 +01:00
}
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
2021-03-31 00:35:08 +02:00
if ( ! windows [ window_id ] . window_has_focus & & mouse_mode = = MOUSE_MODE_CAPTURED ) {
2020-03-09 16:56:48 +01:00
break ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
Ref < InputEventMouseMotion > mm ;
2021-06-18 00:03:09 +02:00
mm . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
mm - > set_window_id ( window_id ) ;
2021-04-24 22:33:50 +02: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 16:56:48 +01:00
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ window_id ] . wtctx ) {
2021-03-12 14:35:16 +01: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 13:16:02 +02: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 11:58:24 +02:00
windows [ window_id ] . last_pen_inverted = false ;
2020-05-05 13:16:02 +02:00
}
} else {
windows [ window_id ] . last_tilt = Vector2 ( ) ;
windows [ window_id ] . last_pressure = ( wParam & MK_LBUTTON ) ? 1.0f : 0.0f ;
2022-06-19 11:58:24 +02:00
windows [ window_id ] . last_pen_inverted = false ;
2020-05-05 13:16:02 +02:00
}
mm - > set_pressure ( windows [ window_id ] . last_pressure ) ;
mm - > set_tilt ( windows [ window_id ] . last_tilt ) ;
2022-06-19 11:58:24 +02:00
mm - > set_pen_inverted ( windows [ window_id ] . last_pen_inverted ) ;
2020-05-03 14:38:58 +02:00
2020-03-09 16:56:48 +01:00
mm - > set_button_mask ( last_button_state ) ;
mm - > set_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
mm - > set_global_position ( Vector2 ( GET_X_LPARAM ( lParam ) , GET_Y_LPARAM ( lParam ) ) ) ;
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
Point2i c ( 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 14:22:22 +01:00
mm - > set_velocity ( Input : : get_singleton ( ) - > get_last_mouse_velocity ( ) ) ;
2020-03-09 16:56:48 +01: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 ) ) ) ;
old_x = mm - > get_position ( ) . x ;
old_y = mm - > get_position ( ) . y ;
2022-02-24 10:21:23 +01:00
if ( windows [ window_id ] . window_has_focus | | window_get_active_popup ( ) = = window_id ) {
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mm ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
} break ;
case WM_LBUTTONDOWN :
case WM_LBUTTONUP :
2020-04-28 15:19:37 +02:00
if ( Input : : get_singleton ( ) - > is_emulating_mouse_from_touch ( ) ) {
2021-09-10 15:44:44 +02:00
// Universal translation enabled; ignore OS translations for left button.
2020-03-09 16:56:48 +01: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-18 00:03:09 +02:00
mb . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
mb - > set_window_id ( window_id ) ;
switch ( uMsg ) {
case WM_LBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_LBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_MBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_MBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_RBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_RBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_LBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : LEFT ) ;
2021-04-13 10:25:44 +02:00
mb - > set_double_click ( true ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_RBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : RIGHT ) ;
2021-04-13 10:25:44 +02:00
mb - > set_double_click ( true ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_MBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MIDDLE ) ;
2021-04-13 10:25:44 +02:00
mb - > set_double_click ( true ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_MOUSEWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
2021-03-31 00:35:08 +02:00
if ( ! motion ) {
2020-03-09 16:56:48 +01:00
return 0 ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
2021-03-31 00:35:08 +02:00
if ( motion > 0 ) {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : WHEEL_UP ) ;
2021-03-31 00:35:08 +02:00
} else {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : WHEEL_DOWN ) ;
2021-03-31 00:35:08 +02:00
}
2021-08-06 15:06:11 +02:00
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_MOUSEHWHEEL : {
mb - > set_pressed ( true ) ;
int motion = ( short ) HIWORD ( wParam ) ;
2021-03-31 00:35:08 +02:00
if ( ! motion ) {
2020-03-09 16:56:48 +01:00
return 0 ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
if ( motion < 0 ) {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : WHEEL_LEFT ) ;
2020-03-09 16:56:48 +01:00
} else {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : WHEEL_RIGHT ) ;
2020-03-09 16:56:48 +01:00
}
2021-08-06 15:06:11 +02:00
mb - > set_factor ( fabs ( ( double ) motion / ( double ) WHEEL_DELTA ) ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_XBUTTONDOWN : {
mb - > set_pressed ( true ) ;
2021-03-31 00:35:08 +02:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-31 00:35:08 +02:00
} else {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
} break ;
case WM_XBUTTONUP : {
mb - > set_pressed ( false ) ;
2021-03-31 00:35:08 +02:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-31 00:35:08 +02:00
} else {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01:00
} break ;
case WM_XBUTTONDBLCLK : {
mb - > set_pressed ( true ) ;
2021-03-31 00:35:08 +02:00
if ( HIWORD ( wParam ) = = XBUTTON1 ) {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON1 ) ;
2021-03-31 00:35:08 +02:00
} else {
2021-08-13 23:31:57 +02:00
mb - > set_button_index ( MouseButton : : MB_XBUTTON2 ) ;
2021-03-31 00:35:08 +02:00
}
2021-04-13 10:25:44 +02:00
mb - > set_double_click ( true ) ;
2020-03-09 16:56:48 +01:00
} break ;
default : {
return 0 ;
}
}
2021-04-24 22:33:50 +02: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 15:44:44 +02:00
// mb->is_alt_pressed()=(wParam&MK_MENU)!=0;
2021-03-31 00:35:08 +02:00
if ( mb - > is_pressed ( ) ) {
2021-08-13 23:31:57 +02:00
last_button_state | = mouse_button_to_mask ( mb - > get_button_index ( ) ) ;
2021-03-31 00:35:08 +02:00
} else {
2021-08-13 23:31:57 +02:00
last_button_state & = ~ mouse_button_to_mask ( mb - > get_button_index ( ) ) ;
2021-03-31 00:35:08 +02:00
}
2020-03-09 16:56:48 +01: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 13:56:32 +01:00
if ( + + pressrc > 0 & & mouse_mode ! = MOUSE_MODE_CAPTURED ) {
2020-03-09 16:56:48 +01:00
SetCapture ( hWnd ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
} else {
if ( - - pressrc < = 0 ) {
if ( mouse_mode ! = MOUSE_MODE_CAPTURED ) {
ReleaseCapture ( ) ;
}
pressrc = 0 ;
}
}
} else {
2022-03-31 14:06:10 +02:00
// For reasons unknown to humanity, wheel comes in screen coordinates.
2020-03-09 16:56:48 +01: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-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mb ) ;
2021-08-13 23:31:57 +02:00
if ( mb - > is_pressed ( ) & & mb - > get_button_index ( ) > = MouseButton : : WHEEL_UP & & mb - > get_button_index ( ) < = MouseButton : : WHEEL_RIGHT ) {
2021-09-10 15:44:44 +02:00
// Send release for mouse wheel.
2020-03-09 16:56:48 +01:00
Ref < InputEventMouseButton > mbd = mb - > duplicate ( ) ;
mbd - > set_window_id ( window_id ) ;
2021-08-13 23:31:57 +02:00
last_button_state & = ~ mouse_button_to_mask ( mbd - > get_button_index ( ) ) ;
2020-03-09 16:56:48 +01:00
mbd - > set_button_mask ( last_button_state ) ;
mbd - > set_pressed ( false ) ;
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( mbd ) ;
2020-03-09 16:56:48 +01:00
}
} break ;
2021-11-05 21:36:13 +01:00
case WM_WINDOWPOSCHANGED : {
Rect2i window_client_rect ;
2022-02-08 09:36:57 +01:00
Rect2i window_rect ;
2021-11-05 21:36:13 +01: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 ) ;
2022-02-08 09:36:57 +01:00
RECT wrect ;
GetWindowRect ( hWnd , & wrect ) ;
window_rect = Rect2i ( wrect . left , wrect . top , wrect . right - wrect . left , wrect . bottom - wrect . top ) ;
2020-03-09 16:56:48 +01:00
}
2021-11-05 21:36:13 +01: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 ;
2022-02-08 09:36:57 +01:00
} else if ( window_rect . position = = screen_position & & window_rect . size = = screen_size ) {
2021-11-05 21:36:13 +01:00
window . fullscreen = true ;
}
if ( ! window . minimized ) {
window . width = window_client_rect . size . width ;
window . height = window_client_rect . size . height ;
2020-03-09 16:56:48 +01:00
2021-11-05 21:36:13 +01:00
rect_changed = true ;
2020-03-09 16:56:48 +01:00
}
2022-02-17 12:09:17 +01:00
# if defined(VULKAN_ENABLED)
if ( context_vulkan & & window_created ) {
// Note: Trigger resize event to update swapchains when window is minimized/restored, even if size is not changed.
context_vulkan - > window_resize ( window_id , window . width , window . height ) ;
}
# endif
2020-03-09 16:56:48 +01:00
}
2021-11-05 21:36:13 +01: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 16:56:48 +01:00
}
2021-11-05 21:36:13 +01:00
if ( rect_changed ) {
if ( ! window . rect_changed_callback . is_null ( ) ) {
Variant size = Rect2i ( window . last_pos . x , window . last_pos . y , window . width , window . height ) ;
const Variant * args [ ] = { & size } ;
Variant ret ;
Callable : : CallError ce ;
2022-07-28 22:56:41 +02:00
window . rect_changed_callback . callp ( args , 1 , ret , ce ) ;
2021-11-05 21:36:13 +01:00
}
2020-03-09 16:56:48 +01:00
}
2021-11-05 21:36:13 +01: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 16:56:48 +01:00
} break ;
2021-11-05 21:36:13 +01:00
2020-03-09 16:56:48 +01:00
case WM_ENTERSIZEMOVE : {
2020-04-28 15:19:37 +02:00
Input : : get_singleton ( ) - > release_pressed_events ( ) ;
2021-01-05 20:37:45 +01:00
windows [ window_id ] . move_timer_id = SetTimer ( windows [ window_id ] . hWnd , 1 , USER_TIMER_MINIMUM , ( TIMERPROC ) nullptr ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_EXITSIZEMOVE : {
2021-01-05 20:37:45 +01:00
KillTimer ( windows [ window_id ] . hWnd , windows [ window_id ] . move_timer_id ) ;
2020-03-09 16:56:48 +01:00
} break ;
case WM_TIMER : {
2021-01-05 20:37:45 +01:00
if ( wParam = = windows [ window_id ] . move_timer_id ) {
2020-03-09 16:56:48 +01:00
_process_key_events ( ) ;
if ( ! Main : : is_iterating ( ) ) {
Main : : iteration ( ) ;
}
2021-01-05 20:37:45 +01:00
} else if ( wParam = = windows [ window_id ] . focus_timer_id ) {
_process_activate_event ( window_id , windows [ window_id ] . saved_wparam , windows [ window_id ] . saved_lparam ) ;
KillTimer ( windows [ window_id ] . hWnd , wParam ) ;
windows [ window_id ] . focus_timer_id = 0U ;
2020-03-09 16:56:48 +01:00
}
} break ;
case WM_SYSKEYDOWN :
case WM_SYSKEYUP :
case WM_KEYUP :
case WM_KEYDOWN : {
2022-02-16 13:56:32 +01:00
if ( wParam = = VK_SHIFT ) {
2020-06-10 05:22:57 +02:00
shift_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 13:56:32 +01:00
}
if ( wParam = = VK_CONTROL ) {
2020-06-10 05:22:57 +02:00
control_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
if ( wParam = = VK_MENU ) {
alt_mem = ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ;
2022-02-16 13:56:32 +01:00
if ( lParam & ( 1 < < 24 ) ) {
2020-03-09 16:56:48 +01:00
gr_mem = alt_mem ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
}
if ( mouse_mode = = MOUSE_MODE_CAPTURED ) {
// When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
if ( wParam = = VK_F4 & & alt_mem & & ( uMsg = = WM_KEYDOWN | | uMsg = = WM_SYSKEYDOWN ) ) {
_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 13:56:32 +01:00
if ( ke . uMsg = = WM_SYSKEYDOWN ) {
2020-03-09 16:56:48 +01:00
ke . uMsg = WM_KEYDOWN ;
2022-02-16 13:56:32 +01:00
}
if ( ke . uMsg = = WM_SYSKEYUP ) {
2020-03-09 16:56:48 +01:00
ke . uMsg = WM_KEYUP ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
ke . wParam = wParam ;
ke . lParam = lParam ;
key_event_buffer [ key_event_pos + + ] = ke ;
} break ;
case WM_INPUTLANGCHANGEREQUEST : {
// FIXME: Do something?
} break ;
case WM_TOUCH : {
BOOL bHandled = FALSE ;
UINT cInputs = LOWORD ( wParam ) ;
PTOUCHINPUT pInputs = memnew_arr ( TOUCHINPUT , cInputs ) ;
if ( pInputs ) {
if ( GetTouchInputInfo ( ( HTOUCHINPUT ) lParam , cInputs , pInputs , sizeof ( TOUCHINPUT ) ) ) {
for ( UINT i = 0 ; i < cInputs ; i + + ) {
TOUCHINPUT ti = pInputs [ i ] ;
POINT touch_pos = {
TOUCH_COORD_TO_PIXEL ( ti . x ) ,
TOUCH_COORD_TO_PIXEL ( ti . y ) ,
} ;
ScreenToClient ( hWnd , & touch_pos ) ;
2021-09-10 15:44:44 +02:00
// Do something with each touch input entry.
2020-03-09 16:56:48 +01: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 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
}
bHandled = TRUE ;
} else {
2021-09-10 15:44:44 +02:00
// TODO: Handle the error here.
2020-03-09 16:56:48 +01:00
}
memdelete_arr ( pInputs ) ;
} else {
2021-09-10 15:44:44 +02:00
// TODO: Handle the error here, probably out of memory.
2020-03-09 16:56:48 +01:00
}
if ( bHandled ) {
CloseTouchInputHandle ( ( HTOUCHINPUT ) lParam ) ;
return 0 ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
} break ;
case WM_DEVICECHANGE : {
joypad - > probe_joypads ( ) ;
} break ;
2022-01-14 14:45:16 +01:00
case WM_DESTROY : {
Input : : get_singleton ( ) - > flush_buffered_events ( ) ;
} break ;
2020-03-09 16:56:48 +01:00
case WM_SETCURSOR : {
if ( LOWORD ( lParam ) = = HTCLIENT ) {
2021-03-31 00:35:08 +02:00
if ( windows [ window_id ] . window_has_focus & & ( mouse_mode = = MOUSE_MODE_HIDDEN | | mouse_mode = = MOUSE_MODE_CAPTURED | | mouse_mode = = MOUSE_MODE_CONFINED_HIDDEN ) ) {
2021-09-10 15:44:44 +02:00
// Hide the cursor.
2020-06-22 12:05:18 +02:00
if ( hCursor = = nullptr ) {
2020-04-02 01:20:12 +02:00
hCursor = SetCursor ( nullptr ) ;
2020-06-22 12:05:18 +02:00
} else {
2020-04-02 01:20:12 +02:00
SetCursor ( nullptr ) ;
2020-06-22 12:05:18 +02:00
}
2020-03-09 16:56:48 +01:00
} else {
2020-04-02 01:20:12 +02:00
if ( hCursor ! = nullptr ) {
2020-03-09 16:56:48 +01:00
CursorShape c = cursor_shape ;
cursor_shape = CURSOR_MAX ;
cursor_set_shape ( c ) ;
2020-04-02 01:20:12 +02:00
hCursor = nullptr ;
2020-03-09 16:56:48 +01:00
}
}
}
} break ;
case WM_DROPFILES : {
HDROP hDropInfo = ( HDROP ) wParam ;
const int buffsize = 4096 ;
2020-07-27 12:43:20 +02:00
WCHAR buf [ buffsize ] ;
2020-03-09 16:56:48 +01:00
2020-04-02 01:20:12 +02:00
int fcount = DragQueryFileW ( hDropInfo , 0xFFFFFFFF , nullptr , 0 ) ;
2020-03-09 16:56:48 +01:00
Vector < String > files ;
for ( int i = 0 ; i < fcount ; i + + ) {
DragQueryFileW ( hDropInfo , i , buf , buffsize ) ;
2020-07-27 12:43:20 +02:00
String file = String : : utf16 ( ( const char16_t * ) buf ) ;
2020-03-09 16:56:48 +01:00
files . push_back ( file ) ;
}
if ( files . size ( ) & & ! windows [ window_id ] . drop_files_callback . is_null ( ) ) {
Variant v = files ;
Variant * vp = & v ;
Variant ret ;
Callable : : CallError ce ;
2022-07-28 22:56:41 +02:00
windows [ window_id ] . drop_files_callback . callp ( ( const Variant * * ) & vp , 1 , ret , ce ) ;
2020-03-09 16:56:48 +01:00
}
} break ;
default : {
if ( user_proc ) {
return CallWindowProcW ( user_proc , hWnd , uMsg , wParam , lParam ) ;
2022-02-16 13:56:32 +01:00
}
}
2020-03-09 16:56:48 +01: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 13:56:32 +01:00
if ( ds_win ) {
2020-03-09 16:56:48 +01:00
return ds_win - > WndProc ( hWnd , uMsg , wParam , lParam ) ;
2022-02-16 13:56:32 +01:00
} else {
2020-03-09 16:56:48 +01:00
return DefWindowProcW ( hWnd , uMsg , wParam , lParam ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
}
2021-01-05 20:37:45 +01:00
void DisplayServerWindows : : _process_activate_event ( WindowID p_window_id , WPARAM wParam , LPARAM lParam ) {
if ( LOWORD ( wParam ) = = WA_ACTIVE | | LOWORD ( wParam ) = = WA_CLICKACTIVE ) {
_send_window_event ( windows [ p_window_id ] , WINDOW_EVENT_FOCUS_IN ) ;
windows [ p_window_id ] . window_focused = true ;
alt_mem = false ;
control_mem = false ;
shift_mem = false ;
2022-01-26 12:58:54 +01:00
// Restore mouse mode.
_set_mouse_mode_impl ( mouse_mode ) ;
2021-09-10 15:44:44 +02:00
} else { // WM_INACTIVE.
2021-01-05 20:37:45 +01:00
Input : : get_singleton ( ) - > release_pressed_events ( ) ;
_send_window_event ( windows [ p_window_id ] , WINDOW_EVENT_FOCUS_OUT ) ;
windows [ p_window_id ] . window_focused = false ;
alt_mem = false ;
2021-01-06 13:08:51 +01:00
}
2021-01-05 20:37:45 +01:00
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available & & windows [ p_window_id ] . wtctx ) {
2021-01-05 20:37:45 +01:00
wintab_WTEnable ( windows [ p_window_id ] . wtctx , GET_WM_ACTIVATE_STATE ( wParam , lParam ) ) ;
}
}
2020-03-09 16:56:48 +01: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 15:44:44 +02:00
// Extended keys should only be processed as WM_KEYDOWN message.
2020-05-18 14:47:54 +02: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 13:22:16 +02: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 16:56:48 +01:00
Ref < InputEventKey > k ;
2021-06-18 00:03:09 +02:00
k . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
k - > set_window_id ( ke . window_id ) ;
2021-04-24 22:33:50 +02:00
k - > set_shift_pressed ( ke . shift ) ;
k - > set_alt_pressed ( ke . alt ) ;
k - > set_ctrl_pressed ( ke . control ) ;
k - > set_meta_pressed ( ke . meta ) ;
2020-03-09 16:56:48 +01:00
k - > set_pressed ( true ) ;
2021-06-20 19:12:33 +02:00
k - > set_keycode ( ( Key ) KeyMappingWindows : : get_keysym ( ke . wParam ) ) ;
k - > set_physical_keycode ( ( Key ) ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ) ;
2020-09-03 13:22:16 +02:00
k - > set_unicode ( unicode ) ;
2020-03-09 16:56:48 +01:00
if ( k - > get_unicode ( ) & & gr_mem ) {
2021-04-24 22:33:50 +02:00
k - > set_alt_pressed ( false ) ;
k - > set_ctrl_pressed ( false ) ;
2020-03-09 16:56:48 +01:00
}
2022-02-16 13:56:32 +01:00
if ( k - > get_unicode ( ) < 32 ) {
2020-03-09 16:56:48 +01:00
k - > set_unicode ( 0 ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( k ) ;
2021-09-10 15:44:44 +02:00
} else {
// Do nothing.
2020-03-09 16:56:48 +01:00
}
} break ;
case WM_KEYUP :
case WM_KEYDOWN : {
Ref < InputEventKey > k ;
2021-06-18 00:03:09 +02:00
k . instantiate ( ) ;
2020-03-09 16:56:48 +01:00
k - > set_window_id ( ke . window_id ) ;
2021-04-24 22:33:50 +02:00
k - > set_shift_pressed ( ke . shift ) ;
k - > set_alt_pressed ( ke . alt ) ;
k - > set_ctrl_pressed ( ke . control ) ;
k - > set_meta_pressed ( ke . meta ) ;
2020-03-09 16:56:48 +01:00
k - > set_pressed ( ke . uMsg = = WM_KEYDOWN ) ;
if ( ( ke . lParam & ( 1 < < 24 ) ) & & ( ke . wParam = = VK_RETURN ) ) {
2021-09-10 15:44:44 +02:00
// Special case for Numpad Enter key.
2021-08-13 23:31:57 +02:00
k - > set_keycode ( Key : : KP_ENTER ) ;
2020-03-09 16:56:48 +01:00
} else {
2021-06-20 19:12:33 +02:00
k - > set_keycode ( ( Key ) KeyMappingWindows : : get_keysym ( ke . wParam ) ) ;
2020-03-09 16:56:48 +01:00
}
2021-06-20 19:12:33 +02:00
k - > set_physical_keycode ( ( Key ) ( KeyMappingWindows : : get_scansym ( ( ke . lParam > > 16 ) & 0xFF , ke . lParam & ( 1 < < 24 ) ) ) ) ;
2020-03-09 16:56:48 +01:00
if ( i + 1 < key_event_pos & & key_event_buffer [ i + 1 ] . uMsg = = WM_CHAR ) {
2020-09-03 13:22:16 +02: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 ;
}
k - > set_unicode ( unicode ) ;
2020-03-09 16:56:48 +01:00
}
if ( k - > get_unicode ( ) & & gr_mem ) {
2021-04-24 22:33:50 +02:00
k - > set_alt_pressed ( false ) ;
k - > set_ctrl_pressed ( false ) ;
2020-03-09 16:56:48 +01:00
}
2022-02-16 13:56:32 +01:00
if ( k - > get_unicode ( ) < 32 ) {
2020-03-09 16:56:48 +01:00
k - > set_unicode ( 0 ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
k - > set_echo ( ( ke . uMsg = = WM_KEYDOWN & & ( ke . lParam & ( 1 < < 30 ) ) ) ) ;
2021-08-13 00:31:16 +02:00
Input : : get_singleton ( ) - > parse_input_event ( k ) ;
2020-03-09 16:56:48 +01:00
} break ;
}
}
key_event_pos = 0 ;
}
2020-05-19 22:34:26 +02:00
void DisplayServerWindows : : _update_tablet_ctx ( const String & p_old_driver , const String & p_new_driver ) {
2021-08-09 22:13:42 +02:00
for ( KeyValue < WindowID , WindowData > & E : windows ) {
WindowData & wd = E . value ;
2020-05-22 09:44:47 +02:00
wd . block_mm = false ;
2020-05-19 22:34:26 +02:00
if ( ( p_old_driver = = " wintab " ) & & wintab_available & & wd . wtctx ) {
wintab_WTEnable ( wd . wtctx , false ) ;
wintab_WTClose ( wd . wtctx ) ;
wd . wtctx = 0 ;
}
if ( ( p_new_driver = = " wintab " ) & & wintab_available ) {
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wd . wtlc ) ;
wd . wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-19 11:58:24 +02: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 22:34:26 +02: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. " ) ;
}
}
}
}
2021-06-19 17:44:59 +02:00
DisplayServer : : WindowID DisplayServerWindows : : _create_window ( WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Rect2i & p_rect ) {
2020-03-09 16:56:48 +01:00
DWORD dwExStyle ;
DWORD dwStyle ;
2022-01-28 10:19:53 +01: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 , ( p_flags & WINDOW_FLAG_NO_FOCUS_BIT ) , dwStyle , dwExStyle ) ;
2020-03-14 17:06:39 +01:00
2020-03-09 16:56:48 +01: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 ;
2022-01-28 10:19:53 +01:00
if ( p_mode = = WINDOW_MODE_FULLSCREEN | | p_mode = = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-07-03 03:08:06 +02:00
int nearest_area = 0 ;
Rect2i screen_rect ;
for ( int i = 0 ; i < get_screen_count ( ) ; i + + ) {
Rect2i r ;
r . position = screen_get_position ( i ) ;
r . size = screen_get_size ( i ) ;
2020-12-19 13:43:35 +01:00
Rect2 inters = r . intersection ( p_rect ) ;
2020-07-03 03:08:06 +02:00
int area = inters . size . width * inters . size . height ;
if ( area > = nearest_area ) {
screen_rect = r ;
nearest_area = area ;
}
}
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 ;
}
2020-03-09 16:56:48 +01:00
AdjustWindowRectEx ( & WindowRect , dwStyle , FALSE , dwExStyle ) ;
WindowID id = window_id_counter ;
{
2020-06-27 18:26:34 +02:00
WindowData & wd = windows [ id ] ;
2020-03-09 16:56:48 +01:00
wd . hWnd = CreateWindowExW (
dwExStyle ,
L " Engine " , L " " ,
2020-03-14 20:56:37 +01:00
dwStyle ,
2020-03-12 13:37:40 +01:00
// (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
// (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
WindowRect . left ,
WindowRect . top ,
2020-03-09 16:56:48 +01:00
WindowRect . right - WindowRect . left ,
WindowRect . bottom - WindowRect . top ,
2022-05-09 01:25:49 +02: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 16:56:48 +01:00
if ( ! wd . hWnd ) {
2020-04-02 01:20:12 +02:00
MessageBoxW ( nullptr , L " Window Creation Error. " , L " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2020-06-27 18:26:34 +02:00
windows . erase ( id ) ;
2022-05-09 01:25:49 +02:00
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create Windows OS window. " ) ;
2020-03-09 16:56:48 +01:00
}
2022-07-19 11:58:02 +02: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 10:19:53 +01:00
if ( p_mode ! = WINDOW_MODE_FULLSCREEN & & p_mode ! = WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
2020-12-31 12:49:03 +01:00
wd . pre_fs_valid = true ;
}
2020-03-09 16:56:48 +01:00
2022-09-02 20:44:58 +02:00
if ( is_dark_mode_supported ( ) & & dark_title_available ) {
2022-08-29 10:24:48 +02:00
BOOL value = is_dark_mode ( ) ;
: : DwmSetWindowAttribute ( wd . hWnd , DWMWA_USE_IMMERSIVE_DARK_MODE , & value , sizeof ( value ) ) ;
}
2021-09-10 15:44:44 +02:00
# ifdef VULKAN_ENABLED
2021-12-14 02:49:29 +01:00
if ( context_vulkan ) {
2021-06-19 17:44:59 +02:00
if ( context_vulkan - > window_create ( id , p_vsync_mode , wd . hWnd , hInstance , WindowRect . right - WindowRect . left , WindowRect . bottom - WindowRect . top ) = = - 1 ) {
2020-03-09 16:56:48 +01:00
memdelete ( context_vulkan ) ;
2020-04-02 01:20:12 +02:00
context_vulkan = nullptr ;
2020-06-27 18:26:34 +02:00
windows . erase ( id ) ;
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create Vulkan Window. " ) ;
2020-03-09 16:56:48 +01:00
}
}
# endif
2021-10-26 17:18:39 +02:00
# ifdef GLES3_ENABLED
2021-12-14 02:49:29 +01:00
if ( gl_manager ) {
2020-11-18 19:11:30 +01:00
Error err = gl_manager - > window_create ( id , wd . hWnd , hInstance , WindowRect . right - WindowRect . left , WindowRect . bottom - WindowRect . top ) ;
2022-05-09 01:25:49 +02:00
// shut down OpenGL, to mirror behavior of Vulkan code
if ( err ! = OK ) {
memdelete ( gl_manager ) ;
gl_manager = nullptr ;
windows . erase ( id ) ;
ERR_FAIL_V_MSG ( INVALID_WINDOW_ID , " Failed to create an OpenGL window. " ) ;
}
2020-11-18 19:11:30 +01:00
}
# endif
2020-03-09 16:56:48 +01:00
RegisterTouchWindow ( wd . hWnd , 0 ) ;
DragAcceptFiles ( wd . hWnd , true ) ;
2021-02-17 15:07:56 +01:00
if ( ( tablet_get_current_driver ( ) = = " wintab " ) & & wintab_available ) {
2020-05-05 13:16:02 +02:00
wintab_WTInfo ( WTI_DEFSYSCTX , 0 , & wd . wtlc ) ;
wd . wtlc . lcOptions | = CXO_MESSAGES ;
2022-06-19 11:58:24 +02: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 13:16:02 +02: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 14:51:19 +02:00
print_verbose ( " WinTab context creation failed. " ) ;
2020-05-05 13:16:02 +02:00
}
} else {
wd . wtctx = 0 ;
}
wd . last_pressure = 0 ;
wd . last_pressure_update = 0 ;
wd . last_tilt = Vector2 ( ) ;
2021-09-10 15:44:44 +02:00
// IME.
2020-03-09 16:56:48 +01:00
wd . im_himc = ImmGetContext ( wd . hWnd ) ;
ImmReleaseContext ( wd . hWnd , wd . im_himc ) ;
wd . im_position = Vector2 ( ) ;
2022-05-09 01:25:49 +02:00
// FIXME this is wrong in cases where the window coordinates were changed due to full screen mode; use WindowRect
2020-03-09 16:56:48 +01:00
wd . last_pos = p_rect . position ;
wd . width = p_rect . size . width ;
wd . height = p_rect . size . height ;
window_id_counter + + ;
}
return id ;
}
2021-09-10 15:44:44 +02:00
// WinTab API.
2020-05-05 13:16:02 +02: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 10:24:48 +02:00
// UXTheme API.
2022-09-02 20:44:58 +02:00
bool DisplayServerWindows : : dark_title_available = false ;
2022-08-29 10:24:48 +02:00
bool DisplayServerWindows : : ux_theme_available = false ;
IsDarkModeAllowedForAppPtr DisplayServerWindows : : IsDarkModeAllowedForApp = nullptr ;
ShouldAppsUseDarkModePtr DisplayServerWindows : : ShouldAppsUseDarkMode = nullptr ;
GetImmersiveColorFromColorSetExPtr DisplayServerWindows : : GetImmersiveColorFromColorSetEx = nullptr ;
GetImmersiveColorTypeFromNamePtr DisplayServerWindows : : GetImmersiveColorTypeFromName = nullptr ;
GetImmersiveUserColorSetPreferencePtr DisplayServerWindows : : GetImmersiveUserColorSetPreference = nullptr ;
2021-09-10 15:44:44 +02:00
// Windows Ink API.
2020-05-19 22:34:26 +02:00
bool DisplayServerWindows : : winink_available = false ;
2020-03-09 16:56:48 +01:00
GetPointerTypePtr DisplayServerWindows : : win8p_GetPointerType = nullptr ;
GetPointerPenInfoPtr DisplayServerWindows : : win8p_GetPointerPenInfo = nullptr ;
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 10:24:48 +02:00
bool DisplayServerWindows : : is_dark_mode_supported ( ) const {
return ux_theme_available & & IsDarkModeAllowedForApp ( ) ;
}
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 ) ;
}
2021-02-17 15:07:56 +01: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 + " . " ) ;
}
}
2021-06-19 17:44:59 +02:00
DisplayServerWindows : : DisplayServerWindows ( const String & p_rendering_driver , WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Vector2i & p_resolution , Error & r_error ) {
2020-03-09 16:56:48 +01: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 12:40:26 +02:00
hInstance = static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) - > get_hinstance ( ) ;
2020-03-09 16:56:48 +01:00
pressrc = 0 ;
old_invalid = true ;
mouse_mode = MOUSE_MODE_VISIBLE ;
2021-09-27 01:07:10 +02:00
rendering_driver = p_rendering_driver ;
2021-11-04 13:33:37 +01:00
// Init TTS
tts = memnew ( TTS_Windows ) ;
2022-08-03 18:29:07 +02:00
// Enforce default keep screen on value.
2022-08-06 22:10:24 +02:00
screen_set_keep_on ( GLOBAL_GET ( " display/window/energy_saving/keep_screen_on " ) ) ;
2022-08-03 18:29:07 +02:00
2022-09-02 20:44:58 +02: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 10:24:48 +02:00
HMODULE ux_theme_lib = LoadLibraryW ( L " uxtheme.dll " ) ;
if ( ux_theme_lib ) {
IsDarkModeAllowedForApp = ( IsDarkModeAllowedForAppPtr ) GetProcAddress ( ux_theme_lib , MAKEINTRESOURCEA ( 136 ) ) ;
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 ) ) ;
ux_theme_available = IsDarkModeAllowedForApp & & ShouldAppsUseDarkMode & & GetImmersiveColorFromColorSetEx & & GetImmersiveColorTypeFromName & & GetImmersiveUserColorSetPreference ;
2022-09-02 20:44:58 +02:00
if ( os_ver . dwBuildNumber > = 22000 ) {
dark_title_available = true ;
}
2022-08-29 10:24:48 +02:00
}
2021-09-10 15:44:44 +02:00
// Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink.
2021-02-17 15:07:56 +01: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 " ) ;
}
2021-09-10 15:44:44 +02:00
// Note: Windows Ink API for pen input, available on Windows 8+ only.
2021-02-17 15:07:56 +01:00
HMODULE user32_lib = LoadLibraryW ( L " user32.dll " ) ;
if ( user32_lib ) {
win8p_GetPointerType = ( GetPointerTypePtr ) GetProcAddress ( user32_lib , " GetPointerType " ) ;
win8p_GetPointerPenInfo = ( GetPointerPenInfoPtr ) GetProcAddress ( user32_lib , " GetPointerPenInfo " ) ;
winink_available = win8p_GetPointerType & & win8p_GetPointerPenInfo ;
}
if ( winink_available ) {
tablet_drivers . push_back ( " winink " ) ;
}
2020-03-09 16:56:48 +01:00
if ( OS : : get_singleton ( ) - > is_hidpi_allowed ( ) ) {
HMODULE Shcore = LoadLibraryW ( L " Shcore.dll " ) ;
2020-04-02 01:20:12 +02:00
if ( Shcore ! = nullptr ) {
2020-03-09 16:56:48 +01: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 ) ;
}
}
}
memset ( & wc , 0 , sizeof ( WNDCLASSEXW ) ) ;
wc . cbSize = sizeof ( WNDCLASSEXW ) ;
wc . style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS ;
wc . lpfnWndProc = ( WNDPROC ) : : WndProc ;
wc . cbClsExtra = 0 ;
wc . cbWndExtra = 0 ;
2020-04-02 01:20:12 +02:00
wc . hInstance = hInstance ? hInstance : GetModuleHandle ( nullptr ) ;
wc . hIcon = LoadIcon ( nullptr , IDI_WINLOGO ) ;
2022-02-16 13:56:32 +01:00
wc . hCursor = nullptr ;
2020-04-02 01:20:12 +02:00
wc . hbrBackground = nullptr ;
wc . lpszMenuName = nullptr ;
2020-03-09 16:56:48 +01:00
wc . lpszClassName = L " Engine " ;
if ( ! RegisterClassExW ( & wc ) ) {
2020-04-02 01:20:12 +02:00
MessageBox ( nullptr , " Failed To Register The Window Class. " , " ERROR " , MB_OK | MB_ICONEXCLAMATION ) ;
2020-03-09 16:56:48 +01:00
r_error = ERR_UNAVAILABLE ;
return ;
}
use_raw_input = true ;
RAWINPUTDEVICE Rid [ 1 ] ;
Rid [ 0 ] . usUsagePage = 0x01 ;
Rid [ 0 ] . usUsage = 0x02 ;
Rid [ 0 ] . dwFlags = 0 ;
Rid [ 0 ] . hwndTarget = 0 ;
if ( RegisterRawInputDevices ( Rid , 1 , sizeof ( Rid [ 0 ] ) ) = = FALSE ) {
2021-09-10 15:44:44 +02:00
// Registration failed.
2020-03-09 16:56:48 +01:00
use_raw_input = false ;
}
# if defined(VULKAN_ENABLED)
if ( rendering_driver = = " vulkan " ) {
context_vulkan = memnew ( VulkanContextWindows ) ;
if ( context_vulkan - > initialize ( ) ! = OK ) {
memdelete ( context_vulkan ) ;
2020-04-02 01:20:12 +02:00
context_vulkan = nullptr ;
2020-03-09 16:56:48 +01:00
r_error = ERR_UNAVAILABLE ;
return ;
}
}
# endif
2020-11-18 19:11:30 +01:00
// Init context and rendering device
2021-10-26 17:18:39 +02:00
# if defined(GLES3_ENABLED)
2021-09-10 15:44:44 +02:00
2021-10-26 17:18:39 +02:00
if ( rendering_driver = = " opengl3 " ) {
2021-09-27 01:07:10 +02:00
GLManager_Windows : : ContextType opengl_api_type = GLManager_Windows : : GLES_3_0_COMPATIBLE ;
2020-11-18 19:11:30 +01:00
gl_manager = memnew ( GLManager_Windows ( opengl_api_type ) ) ;
if ( gl_manager - > initialize ( ) ! = OK ) {
memdelete ( gl_manager ) ;
gl_manager = nullptr ;
r_error = ERR_UNAVAILABLE ;
return ;
}
2021-10-26 17:18:39 +02:00
RasterizerGLES3 : : make_current ( ) ;
2020-11-18 19:11:30 +01:00
}
# endif
2022-03-16 15:48:53 +01:00
mouse_monitor = SetWindowsHookEx ( WH_MOUSE , : : MouseProc , nullptr , GetCurrentThreadId ( ) ) ;
2022-02-24 10:21:23 +01:00
2020-04-10 19:31:09 +02:00
Point2i window_position (
( screen_get_size ( 0 ) . width - p_resolution . width ) / 2 ,
( screen_get_size ( 0 ) . height - p_resolution . height ) / 2 ) ;
2020-06-27 18:26:34 +02:00
2021-06-19 17:44:59 +02:00
WindowID main_window = _create_window ( p_mode , p_vsync_mode , 0 , Rect2i ( window_position , p_resolution ) ) ;
2020-06-27 18:26:34 +02:00
ERR_FAIL_COND_MSG ( main_window = = INVALID_WINDOW_ID , " Failed to create main window. " ) ;
2020-03-09 16:56:48 +01: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 09:39:30 +02:00
show_window ( MAIN_WINDOW_ID ) ;
2020-03-14 17:06:39 +01:00
2020-03-09 16:56:48 +01:00
# if defined(VULKAN_ENABLED)
if ( rendering_driver = = " vulkan " ) {
rendering_device_vulkan = memnew ( RenderingDeviceVulkan ) ;
rendering_device_vulkan - > initialize ( context_vulkan ) ;
2020-12-04 19:26:24 +01:00
RendererCompositorRD : : make_current ( ) ;
2020-03-09 16:56:48 +01:00
}
# endif
2022-03-29 09:01:31 +02:00
if ( ! Engine : : get_singleton ( ) - > is_editor_hint ( ) & & ! OS : : get_singleton ( ) - > is_in_low_processor_usage_mode ( ) ) {
// Increase priority for projects that are not in low-processor mode (typically games)
// to reduce the risk of frame stuttering.
// This is not done for the editor to prevent importers or resource bakers
// from making the system unresponsive.
2020-03-09 16:56:48 +01:00
SetPriorityClass ( GetCurrentProcess ( ) , ABOVE_NORMAL_PRIORITY_CLASS ) ;
DWORD index = 0 ;
HANDLE handle = AvSetMmThreadCharacteristics ( " Games " , & index ) ;
2022-02-16 13:56:32 +01:00
if ( handle ) {
2020-03-09 16:56:48 +01:00
AvSetMmThreadPriority ( handle , AVRT_PRIORITY_CRITICAL ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
// This is needed to make sure that background work does not starve the main thread.
2021-09-10 15:44:44 +02:00
// This is only setting the priority of this thread, not the whole process.
2020-03-09 16:56:48 +01:00
SetThreadPriority ( GetCurrentThread ( ) , THREAD_PRIORITY_TIME_CRITICAL ) ;
}
cursor_shape = CURSOR_ARROW ;
_update_real_mouse_position ( MAIN_WINDOW_ID ) ;
joypad = new JoypadWindows ( & windows [ MAIN_WINDOW_ID ] . hWnd ) ;
r_error = OK ;
2022-04-05 12:40:26 +02:00
static_cast < OS_Windows * > ( OS : : get_singleton ( ) ) - > set_main_window ( windows [ MAIN_WINDOW_ID ] . hWnd ) ;
2020-04-28 15:19:37 +02:00
Input : : get_singleton ( ) - > set_event_dispatch_function ( _dispatch_input_events ) ;
2020-03-09 16:56:48 +01:00
}
Vector < String > DisplayServerWindows : : get_rendering_drivers_func ( ) {
Vector < String > drivers ;
# ifdef VULKAN_ENABLED
drivers . push_back ( " vulkan " ) ;
# endif
2021-10-26 17:18:39 +02:00
# ifdef GLES3_ENABLED
drivers . push_back ( " opengl3 " ) ;
2020-03-09 16:56:48 +01:00
# endif
return drivers ;
}
2021-06-19 17:44:59 +02:00
DisplayServer * DisplayServerWindows : : create_func ( const String & p_rendering_driver , WindowMode p_mode , VSyncMode p_vsync_mode , uint32_t p_flags , const Vector2i & p_resolution , Error & r_error ) {
DisplayServer * ds = memnew ( DisplayServerWindows ( p_rendering_driver , p_mode , p_vsync_mode , p_flags , p_resolution , r_error ) ) ;
2020-07-13 18:24:04 +02:00
if ( r_error ! = OK ) {
2021-09-27 01:07:10 +02:00
OS : : get_singleton ( ) - > alert ( " Your video card driver does not support any of the supported Vulkan or OpenGL versions. \n "
2021-07-22 18:23:48 +02:00
" Please update your drivers or if you have a very old or integrated GPU upgrade it. " ,
2020-07-13 18:24:04 +02:00
" Unable to initialize Video driver " ) ;
}
return ds ;
2020-03-09 16:56:48 +01: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 ( ) ;
2022-02-24 10:21:23 +01:00
if ( mouse_monitor ) {
UnhookWindowsHookEx ( mouse_monitor ) ;
}
2020-03-09 16:56:48 +01:00
if ( user_proc ) {
SetWindowLongPtr ( windows [ MAIN_WINDOW_ID ] . hWnd , GWLP_WNDPROC , ( LONG_PTR ) user_proc ) ;
2022-02-16 13:56:32 +01:00
}
2020-03-09 16:56:48 +01:00
2022-08-03 18:29:07 +02:00
// Close power request handle.
screen_set_keep_on ( false ) ;
2021-10-26 17:18:39 +02:00
# ifdef GLES3_ENABLED
2022-02-16 13:56:32 +01:00
// destroy windows .. NYI?
2022-05-09 01:25:49 +02:00
// FIXME wglDeleteContext is never called
2020-11-18 19:11:30 +01:00
# endif
2020-03-09 16:56:48 +01:00
if ( windows . has ( MAIN_WINDOW_ID ) ) {
# ifdef VULKAN_ENABLED
2021-12-14 02:49:29 +01:00
if ( context_vulkan ) {
2020-03-09 16:56:48 +01:00
context_vulkan - > window_destroy ( MAIN_WINDOW_ID ) ;
}
# endif
2020-05-05 13:16:02 +02:00
if ( wintab_available & & windows [ MAIN_WINDOW_ID ] . wtctx ) {
wintab_WTClose ( windows [ MAIN_WINDOW_ID ] . wtctx ) ;
windows [ MAIN_WINDOW_ID ] . wtctx = 0 ;
}
2020-03-09 16:56:48 +01:00
DestroyWindow ( windows [ MAIN_WINDOW_ID ] . hWnd ) ;
}
2020-05-21 18:39:28 +02:00
# if defined(VULKAN_ENABLED)
2021-12-14 02:49:29 +01:00
if ( rendering_device_vulkan ) {
rendering_device_vulkan - > finalize ( ) ;
memdelete ( rendering_device_vulkan ) ;
rendering_device_vulkan = nullptr ;
}
2020-05-21 18:39:28 +02:00
2021-12-14 02:49:29 +01:00
if ( context_vulkan ) {
memdelete ( context_vulkan ) ;
context_vulkan = nullptr ;
2020-05-21 18:39:28 +02:00
}
# endif
2021-09-20 06:42:56 +02:00
if ( restore_mouse_trails > 1 ) {
SystemParametersInfoA ( SPI_SETMOUSETRAILS , restore_mouse_trails , 0 , 0 ) ;
}
2021-10-26 17:18:39 +02:00
# ifdef GLES3_ENABLED
2020-11-18 19:11:30 +01:00
if ( gl_manager ) {
memdelete ( gl_manager ) ;
gl_manager = nullptr ;
}
# endif
2021-11-04 13:33:37 +01:00
if ( tts ) {
memdelete ( tts ) ;
}
CoUninitialize ( ) ;
2020-03-09 16:56:48 +01:00
}