2009-08-21 22:21:05 +02:00
/*
* This file is part of OpenTTD .
* OpenTTD is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation , version 2.
* OpenTTD is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
2008-05-06 17:11:33 +02:00
/** @file win32_v.cpp Implementation of the Windows (GDI) video driver. */
2005-07-25 09:16:10 +02:00
# include "../stdafx.h"
# include "../openttd.h"
2023-04-19 22:47:36 +02:00
# include "../error_func.h"
2007-12-23 11:56:02 +01:00
# include "../gfx_func.h"
2009-09-01 00:38:37 +02:00
# include "../os/windows/win32.h"
2007-06-17 22:40:02 +02:00
# include "../blitter/factory.hpp"
2021-02-20 10:27:52 +01:00
# include "../core/geometry_func.hpp"
2008-01-13 23:07:33 +01:00
# include "../core/math_func.hpp"
2008-01-29 01:45:24 +01:00
# include "../core/random_func.hpp"
2008-05-07 00:17:12 +02:00
# include "../texteff.hpp"
2019-03-17 01:59:46 +01:00
# include "../thread.h"
2011-12-10 17:54:46 +01:00
# include "../progress.h"
2013-08-05 22:37:14 +02:00
# include "../window_gui.h"
2013-08-05 22:36:20 +02:00
# include "../window_func.h"
2018-07-19 21:17:07 +02:00
# include "../framerate_type.h"
2024-01-10 22:38:58 +01:00
# include "../library_loader.h"
2005-07-25 09:16:10 +02:00
# include "win32_v.h"
2005-07-23 17:16:57 +02:00
# include <windows.h>
2013-08-05 22:37:06 +02:00
# include <imm.h>
2021-05-20 18:40:11 +02:00
# include <versionhelpers.h>
2005-07-23 17:16:57 +02:00
2014-04-23 22:13:33 +02:00
# include "../safeguards.h"
2013-08-05 22:36:20 +02:00
/* Missing define in MinGW headers. */
# ifndef MAPVK_VK_TO_CHAR
# define MAPVK_VK_TO_CHAR (2)
# endif
2017-12-09 20:21:45 +01:00
# ifndef PM_QS_INPUT
# define PM_QS_INPUT 0x20000
# endif
2022-09-07 22:17:10 +02:00
# ifndef WM_DPICHANGED
# define WM_DPICHANGED 0x02E0
# endif
2006-08-13 12:22:34 +02:00
bool _window_maximize ;
2008-06-16 21:38:41 +02:00
static Dimension _bck_resolution ;
2013-08-05 22:37:25 +02:00
DWORD _imm_props ;
2006-02-21 00:01:58 +01:00
2021-06-17 10:34:43 +02:00
static Palette _local_palette ; ///< Current palette to use for drawing.
2011-12-10 17:54:46 +01:00
2021-01-16 16:42:59 +01:00
bool VideoDriver_Win32Base : : ClaimMousePointer ( )
2011-11-17 22:09:08 +01:00
{
MyShowCursor ( false , true ) ;
return true ;
}
2021-02-14 14:06:19 +01:00
struct Win32VkMapping {
2005-07-23 17:16:57 +02:00
byte vk_from ;
byte vk_count ;
byte map_to ;
2007-03-07 13:11:48 +01:00
} ;
2005-07-23 17:16:57 +02:00
# define AS(x, z) {x, 0, z}
# define AM(x, y, z, w) {x, y - x, z}
2021-02-14 14:06:19 +01:00
static const Win32VkMapping _vk_mapping [ ] = {
2007-07-23 18:48:19 +02:00
/* Pageup stuff + up/down */
2009-01-09 23:56:28 +01:00
AM ( VK_PRIOR , VK_DOWN , WKC_PAGEUP , WKC_DOWN ) ,
2007-07-23 18:48:19 +02:00
/* Map letters & digits */
2009-01-09 23:56:28 +01:00
AM ( ' A ' , ' Z ' , ' A ' , ' Z ' ) ,
AM ( ' 0 ' , ' 9 ' , ' 0 ' , ' 9 ' ) ,
2005-07-23 17:16:57 +02:00
2006-08-28 20:53:03 +02:00
AS ( VK_ESCAPE , WKC_ESC ) ,
AS ( VK_PAUSE , WKC_PAUSE ) ,
AS ( VK_BACK , WKC_BACKSPACE ) ,
AM ( VK_INSERT , VK_DELETE , WKC_INSERT , WKC_DELETE ) ,
2005-07-23 17:16:57 +02:00
2006-08-28 20:53:03 +02:00
AS ( VK_SPACE , WKC_SPACE ) ,
AS ( VK_RETURN , WKC_RETURN ) ,
AS ( VK_TAB , WKC_TAB ) ,
2005-07-23 17:16:57 +02:00
2007-07-23 18:48:19 +02:00
/* Function keys */
2006-08-28 20:53:03 +02:00
AM ( VK_F1 , VK_F12 , WKC_F1 , WKC_F12 ) ,
2005-07-23 17:16:57 +02:00
2007-07-23 18:48:19 +02:00
/* Numeric part */
2008-09-07 13:55:28 +02:00
AM ( VK_NUMPAD0 , VK_NUMPAD9 , ' 0 ' , ' 9 ' ) ,
2006-08-28 20:53:03 +02:00
AS ( VK_DIVIDE , WKC_NUM_DIV ) ,
AS ( VK_MULTIPLY , WKC_NUM_MUL ) ,
AS ( VK_SUBTRACT , WKC_NUM_MINUS ) ,
AS ( VK_ADD , WKC_NUM_PLUS ) ,
2007-07-23 18:39:27 +02:00
AS ( VK_DECIMAL , WKC_NUM_DECIMAL ) ,
2007-07-23 18:48:19 +02:00
/* Other non-letter keys */
2007-07-23 18:39:27 +02:00
AS ( 0xBF , WKC_SLASH ) ,
AS ( 0xBA , WKC_SEMICOLON ) ,
AS ( 0xBB , WKC_EQUALS ) ,
AS ( 0xDB , WKC_L_BRACKET ) ,
AS ( 0xDC , WKC_BACKSLASH ) ,
AS ( 0xDD , WKC_R_BRACKET ) ,
AS ( 0xDE , WKC_SINGLEQUOTE ) ,
AS ( 0xBC , WKC_COMMA ) ,
AS ( 0xBD , WKC_MINUS ) ,
AS ( 0xBE , WKC_PERIOD )
2005-07-23 17:16:57 +02:00
} ;
static uint MapWindowsKey ( uint sym )
{
2021-02-14 14:06:19 +01:00
const Win32VkMapping * map ;
2005-07-23 17:16:57 +02:00
uint key = 0 ;
for ( map = _vk_mapping ; map ! = endof ( _vk_mapping ) ; + + map ) {
if ( ( uint ) ( sym - map - > vk_from ) < = map - > vk_count ) {
key = sym - map - > vk_from + map - > map_to ;
break ;
}
}
if ( GetAsyncKeyState ( VK_SHIFT ) < 0 ) key | = WKC_SHIFT ;
if ( GetAsyncKeyState ( VK_CONTROL ) < 0 ) key | = WKC_CTRL ;
if ( GetAsyncKeyState ( VK_MENU ) < 0 ) key | = WKC_ALT ;
return key ;
}
2021-01-16 16:43:03 +01:00
/** Colour depth to use for fullscreen display modes. */
2023-05-08 19:01:06 +02:00
uint8_t VideoDriver_Win32Base : : GetFullscreenBpp ( )
2021-01-16 16:43:03 +01:00
{
/* Check modes for the relevant fullscreen bpp */
return _support8bpp ! = S8BPP_HARDWARE ? 32 : BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ;
}
2011-05-02 18:14:23 +02:00
/**
* Instantiate a new window .
* @ param full_screen Whether to make a full screen window or not .
2021-03-08 13:48:32 +01:00
* @ param resize Whether to change window size .
2011-05-02 18:14:23 +02:00
* @ return True if the window could be created .
*/
2021-03-08 13:48:32 +01:00
bool VideoDriver_Win32Base : : MakeWindow ( bool full_screen , bool resize )
2007-06-21 17:48:00 +02:00
{
2020-12-21 22:28:56 +01:00
/* full_screen is whether the new window should be fullscreen,
* _wnd . fullscreen is whether the current window is . */
2007-06-21 17:48:00 +02:00
_fullscreen = full_screen ;
2009-03-15 01:32:18 +01:00
/* recreate window? */
2021-03-01 23:19:54 +01:00
if ( ( full_screen ! = this - > fullscreen ) & & this - > main_wnd ) {
2021-01-16 16:43:02 +01:00
DestroyWindow ( this - > main_wnd ) ;
this - > main_wnd = 0 ;
2007-06-21 17:48:00 +02:00
}
if ( full_screen ) {
DEVMODE settings ;
memset ( & settings , 0 , sizeof ( settings ) ) ;
settings . dmSize = sizeof ( settings ) ;
settings . dmFields =
2014-04-27 14:15:14 +02:00
DM_BITSPERPEL |
2007-06-21 17:48:00 +02:00
DM_PELSWIDTH |
2021-02-20 11:30:09 +01:00
DM_PELSHEIGHT ;
2021-01-16 16:43:03 +01:00
settings . dmBitsPerPel = this - > GetFullscreenBpp ( ) ;
2021-01-16 16:43:12 +01:00
settings . dmPelsWidth = this - > width_org ;
settings . dmPelsHeight = this - > height_org ;
2007-06-21 17:48:00 +02:00
2012-11-25 13:14:13 +01:00
/* Check for 8 bpp support. */
2021-01-16 16:43:03 +01:00
if ( settings . dmBitsPerPel = = 8 & & ChangeDisplaySettings ( & settings , CDS_FULLSCREEN | CDS_TEST ) ! = DISP_CHANGE_SUCCESSFUL ) {
2012-11-25 13:14:13 +01:00
settings . dmBitsPerPel = 32 ;
}
2011-02-14 21:16:36 +01:00
/* Test fullscreen with current resolution, if it fails use desktop resolution. */
if ( ChangeDisplaySettings ( & settings , CDS_FULLSCREEN | CDS_TEST ) ! = DISP_CHANGE_SUCCESSFUL ) {
RECT r ;
GetWindowRect ( GetDesktopWindow ( ) , & r ) ;
2012-11-25 13:14:13 +01:00
/* Guard against recursion. If we already failed here once, just fall through to
* the next ChangeDisplaySettings call which will fail and error out appropriately . */
2012-12-03 23:08:00 +01:00
if ( ( int ) settings . dmPelsWidth ! = r . right - r . left | | ( int ) settings . dmPelsHeight ! = r . bottom - r . top ) {
2012-11-25 13:14:13 +01:00
return this - > ChangeResolution ( r . right - r . left , r . bottom - r . top ) ;
}
2011-02-14 21:16:36 +01:00
}
2007-06-21 17:48:00 +02:00
if ( ChangeDisplaySettings ( & settings , CDS_FULLSCREEN ) ! = DISP_CHANGE_SUCCESSFUL ) {
2021-03-08 13:48:32 +01:00
this - > MakeWindow ( false , resize ) ; // don't care about the result
2008-01-01 15:20:48 +01:00
return false ; // the request failed
2007-06-21 17:48:00 +02:00
}
2021-01-16 16:43:03 +01:00
} else if ( this - > fullscreen ) {
2009-03-15 01:32:18 +01:00
/* restore display? */
2019-04-10 23:07:06 +02:00
ChangeDisplaySettings ( nullptr , 0 ) ;
2012-04-30 18:49:26 +02:00
/* restore the resolution */
2021-01-16 16:43:12 +01:00
this - > width = _bck_resolution . width ;
this - > height = _bck_resolution . height ;
2007-06-21 17:48:00 +02:00
}
{
RECT r ;
DWORD style , showstyle ;
2012-07-10 19:47:03 +02:00
int w , h ;
2007-06-21 17:48:00 +02:00
showstyle = SW_SHOWNORMAL ;
2021-01-16 16:43:03 +01:00
this - > fullscreen = full_screen ;
if ( this - > fullscreen ) {
2007-06-21 17:48:00 +02:00
style = WS_POPUP ;
2021-01-16 16:43:12 +01:00
SetRect ( & r , 0 , 0 , this - > width_org , this - > height_org ) ;
2007-06-21 17:48:00 +02:00
} else {
style = WS_OVERLAPPEDWINDOW ;
/* On window creation, check if we were in maximize mode before */
if ( _window_maximize ) showstyle = SW_SHOWMAXIMIZED ;
2021-01-16 16:43:12 +01:00
SetRect ( & r , 0 , 0 , this - > width , this - > height ) ;
2007-06-21 17:48:00 +02:00
}
AdjustWindowRect ( & r , style , FALSE ) ;
2012-07-10 19:47:03 +02:00
w = r . right - r . left ;
h = r . bottom - r . top ;
2012-04-30 18:48:47 +02:00
2021-01-16 16:43:02 +01:00
if ( this - > main_wnd ! = nullptr ) {
2021-03-08 13:48:32 +01:00
if ( ! _window_maximize & & resize ) SetWindowPos ( this - > main_wnd , 0 , 0 , 0 , w , h , SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOMOVE ) ;
2012-07-10 19:47:03 +02:00
} else {
2023-06-11 12:00:02 +02:00
int x = 0 ;
int y = 0 ;
2023-06-04 20:38:16 +02:00
2023-06-11 12:00:02 +02:00
/* For windowed mode, center on the workspace of the primary display. */
if ( ! this - > fullscreen ) {
MONITORINFO mi ;
mi . cbSize = sizeof ( mi ) ;
GetMonitorInfo ( MonitorFromWindow ( 0 , MONITOR_DEFAULTTOPRIMARY ) , & mi ) ;
x = ( mi . rcWork . right - mi . rcWork . left - w ) / 2 ;
y = ( mi . rcWork . bottom - mi . rcWork . top - h ) / 2 ;
}
2007-06-21 17:48:00 +02:00
2023-06-06 17:37:39 +02:00
std : : string caption = VideoDriver : : GetCaption ( ) ;
this - > main_wnd = CreateWindow ( L " OTTD " , OTTD2FS ( caption ) . c_str ( ) , style , x , y , w , h , 0 , 0 , GetModuleHandle ( nullptr ) , this ) ;
2023-04-19 22:47:36 +02:00
if ( this - > main_wnd = = nullptr ) UserError ( " CreateWindow failed " ) ;
2021-01-16 16:43:02 +01:00
ShowWindow ( this - > main_wnd , showstyle ) ;
2007-06-21 17:48:00 +02:00
}
}
2010-01-04 03:32:36 +01:00
2014-01-02 23:41:58 +01:00
BlitterFactory : : GetCurrentBlitter ( ) - > PostResize ( ) ;
2010-01-04 03:32:36 +01:00
2021-01-16 16:43:02 +01:00
GameSizeChanged ( ) ;
return true ;
2007-06-21 17:48:00 +02:00
}
2013-08-05 22:36:20 +02:00
/** Forward key presses to the window system. */
2023-05-08 19:01:06 +02:00
static LRESULT HandleCharMsg ( uint keycode , char32_t charcode )
2013-08-05 22:36:20 +02:00
{
2023-05-08 19:01:06 +02:00
static char32_t prev_char = 0 ;
2013-08-05 22:36:40 +02:00
/* Did we get a lead surrogate? If yes, store and exit. */
if ( Utf16IsLeadSurrogate ( charcode ) ) {
2021-06-12 09:10:17 +02:00
if ( prev_char ! = 0 ) Debug ( driver , 1 , " Got two UTF-16 lead surrogates, dropping the first one " ) ;
2013-08-05 22:36:40 +02:00
prev_char = charcode ;
return 0 ;
}
/* Stored lead surrogate and incoming trail surrogate? Combine and forward to input handling. */
if ( prev_char ! = 0 ) {
if ( Utf16IsTrailSurrogate ( charcode ) ) {
charcode = Utf16DecodeSurrogate ( prev_char , charcode ) ;
} else {
2021-06-12 09:10:17 +02:00
Debug ( driver , 1 , " Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate " ) ;
2013-08-05 22:36:40 +02:00
}
}
prev_char = 0 ;
2013-08-05 22:36:20 +02:00
2013-08-05 22:36:36 +02:00
HandleKeypress ( keycode , charcode ) ;
2013-08-05 22:36:20 +02:00
return 0 ;
}
2013-08-05 22:37:25 +02:00
/** Should we draw the composition string ourself, i.e is this a normal IME? */
static bool DrawIMECompositionString ( )
{
return ( _imm_props & IME_PROP_AT_CARET ) & & ! ( _imm_props & IME_PROP_SPECIAL_UI ) ;
}
2013-08-05 22:37:14 +02:00
/** Set position of the composition window to the caret position. */
static void SetCompositionPos ( HWND hwnd )
{
HIMC hIMC = ImmGetContext ( hwnd ) ;
2023-12-26 00:24:59 +01:00
if ( hIMC ! = nullptr ) {
2013-08-05 22:37:14 +02:00
COMPOSITIONFORM cf ;
cf . dwStyle = CFS_POINT ;
if ( EditBoxInGlobalFocus ( ) ) {
/* Get caret position. */
Point pt = _focused_window - > GetCaretPosition ( ) ;
cf . ptCurrentPos . x = _focused_window - > left + pt . x ;
cf . ptCurrentPos . y = _focused_window - > top + pt . y ;
} else {
cf . ptCurrentPos . x = 0 ;
cf . ptCurrentPos . y = 0 ;
}
ImmSetCompositionWindow ( hIMC , & cf ) ;
}
ImmReleaseContext ( hwnd , hIMC ) ;
}
2013-08-05 22:37:18 +02:00
/** Set the position of the candidate window. */
static void SetCandidatePos ( HWND hwnd )
{
HIMC hIMC = ImmGetContext ( hwnd ) ;
2023-12-26 00:24:59 +01:00
if ( hIMC ! = nullptr ) {
2013-08-05 22:37:18 +02:00
CANDIDATEFORM cf ;
cf . dwIndex = 0 ;
cf . dwStyle = CFS_EXCLUDE ;
if ( EditBoxInGlobalFocus ( ) ) {
Point pt = _focused_window - > GetCaretPosition ( ) ;
cf . ptCurrentPos . x = _focused_window - > left + pt . x ;
cf . ptCurrentPos . y = _focused_window - > top + pt . y ;
if ( _focused_window - > window_class = = WC_CONSOLE ) {
cf . rcArea . left = _focused_window - > left ;
cf . rcArea . top = _focused_window - > top ;
cf . rcArea . right = _focused_window - > left + _focused_window - > width ;
cf . rcArea . bottom = _focused_window - > top + _focused_window - > height ;
} else {
cf . rcArea . left = _focused_window - > left + _focused_window - > nested_focus - > pos_x ;
cf . rcArea . top = _focused_window - > top + _focused_window - > nested_focus - > pos_y ;
cf . rcArea . right = cf . rcArea . left + _focused_window - > nested_focus - > current_x ;
cf . rcArea . bottom = cf . rcArea . top + _focused_window - > nested_focus - > current_y ;
}
} else {
cf . ptCurrentPos . x = 0 ;
cf . ptCurrentPos . y = 0 ;
SetRectEmpty ( & cf . rcArea ) ;
}
ImmSetCandidateWindow ( hIMC , & cf ) ;
}
ImmReleaseContext ( hwnd , hIMC ) ;
}
2013-08-05 22:37:06 +02:00
/** Handle WM_IME_COMPOSITION messages. */
static LRESULT HandleIMEComposition ( HWND hwnd , WPARAM wParam , LPARAM lParam )
{
HIMC hIMC = ImmGetContext ( hwnd ) ;
2023-12-26 00:24:59 +01:00
if ( hIMC ! = nullptr ) {
2013-08-05 22:37:06 +02:00
if ( lParam & GCS_RESULTSTR ) {
/* Read result string from the IME. */
2019-04-10 23:07:06 +02:00
LONG len = ImmGetCompositionString ( hIMC , GCS_RESULTSTR , nullptr , 0 ) ; // Length is always in bytes, even in UNICODE build.
2021-05-01 22:06:17 +02:00
std : : wstring str ( len + 1 , L ' \0 ' ) ;
len = ImmGetCompositionString ( hIMC , GCS_RESULTSTR , str . data ( ) , len ) ;
str [ len / sizeof ( wchar_t ) ] = L ' \0 ' ;
2013-08-05 22:37:06 +02:00
/* Transmit text to windowing system. */
2013-08-05 22:37:25 +02:00
if ( len > 0 ) {
2019-04-10 23:07:06 +02:00
HandleTextInput ( nullptr , true ) ; // Clear marked string.
2021-02-21 17:03:19 +01:00
HandleTextInput ( FS2OTTD ( str ) . c_str ( ) ) ;
2013-08-05 22:37:25 +02:00
}
2013-08-05 22:37:14 +02:00
SetCompositionPos ( hwnd ) ;
2013-08-05 22:37:06 +02:00
/* Don't pass the result string on to the default window proc. */
lParam & = ~ ( GCS_RESULTSTR | GCS_RESULTCLAUSE | GCS_RESULTREADCLAUSE | GCS_RESULTREADSTR ) ;
}
2013-08-05 22:37:25 +02:00
if ( ( lParam & GCS_COMPSTR ) & & DrawIMECompositionString ( ) ) {
/* Read composition string from the IME. */
2019-04-10 23:07:06 +02:00
LONG len = ImmGetCompositionString ( hIMC , GCS_COMPSTR , nullptr , 0 ) ; // Length is always in bytes, even in UNICODE build.
2021-05-01 22:06:17 +02:00
std : : wstring str ( len + 1 , L ' \0 ' ) ;
len = ImmGetCompositionString ( hIMC , GCS_COMPSTR , str . data ( ) , len ) ;
str [ len / sizeof ( wchar_t ) ] = L ' \0 ' ;
2013-08-05 22:37:25 +02:00
if ( len > 0 ) {
static char utf8_buf [ 1024 ] ;
2021-05-01 22:06:17 +02:00
convert_from_fs ( str . c_str ( ) , utf8_buf , lengthof ( utf8_buf ) ) ;
2013-08-05 22:37:25 +02:00
/* Convert caret position from bytes in the input string to a position in the UTF-8 encoded string. */
2019-04-10 23:07:06 +02:00
LONG caret_bytes = ImmGetCompositionString ( hIMC , GCS_CURSORPOS , nullptr , 0 ) ;
2013-08-05 22:37:25 +02:00
const char * caret = utf8_buf ;
2021-05-01 22:06:17 +02:00
for ( const wchar_t * c = str . c_str ( ) ; * c ! = ' \0 ' & & * caret ! = ' \0 ' & & caret_bytes > 0 ; c + + , caret_bytes - - ) {
2013-08-05 22:37:25 +02:00
/* Skip DBCS lead bytes or leading surrogates. */
if ( Utf16IsLeadSurrogate ( * c ) ) {
c + + ;
caret_bytes - - ;
}
Utf8Consume ( & caret ) ;
}
HandleTextInput ( utf8_buf , true , caret ) ;
} else {
2019-04-10 23:07:06 +02:00
HandleTextInput ( nullptr , true ) ;
2013-08-05 22:37:25 +02:00
}
lParam & = ~ ( GCS_COMPSTR | GCS_COMPATTR | GCS_COMPCLAUSE | GCS_CURSORPOS | GCS_DELTASTART ) ;
}
2013-08-05 22:37:06 +02:00
}
ImmReleaseContext ( hwnd , hIMC ) ;
return lParam ! = 0 ? DefWindowProc ( hwnd , WM_IME_COMPOSITION , wParam , lParam ) : 0 ;
}
2013-08-05 22:37:11 +02:00
/** Clear the current composition string. */
static void CancelIMEComposition ( HWND hwnd )
{
HIMC hIMC = ImmGetContext ( hwnd ) ;
2023-12-26 00:24:59 +01:00
if ( hIMC ! = nullptr ) ImmNotifyIME ( hIMC , NI_COMPOSITIONSTR , CPS_CANCEL , 0 ) ;
2013-08-05 22:37:11 +02:00
ImmReleaseContext ( hwnd , hIMC ) ;
2013-08-05 22:37:25 +02:00
/* Clear any marked string from the current edit box. */
2019-04-10 23:07:06 +02:00
HandleTextInput ( nullptr , true ) ;
2013-08-05 22:37:11 +02:00
}
2021-01-16 16:42:59 +01:00
LRESULT CALLBACK WndProcGdi ( HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam )
2005-07-23 17:16:57 +02:00
{
2023-05-08 19:01:06 +02:00
static uint32_t keycode = 0 ;
2007-04-27 23:27:02 +02:00
static bool console = false ;
2007-03-10 01:30:18 +01:00
2021-01-16 16:42:59 +01:00
VideoDriver_Win32Base * video_driver = ( VideoDriver_Win32Base * ) GetWindowLongPtr ( hwnd , GWLP_USERDATA ) ;
2005-07-23 17:16:57 +02:00
switch ( msg ) {
2006-08-31 16:50:12 +02:00
case WM_CREATE :
2021-01-16 16:42:59 +01:00
SetWindowLongPtr ( hwnd , GWLP_USERDATA , ( LONG_PTR ) ( ( LPCREATESTRUCT ) lParam ) - > lpCreateParams ) ;
2021-01-16 16:31:08 +01:00
_cursor . in_window = false ; // Win32 has mouse tracking.
2013-08-05 22:37:14 +02:00
SetCompositionPos ( hwnd ) ;
2013-08-05 22:37:25 +02:00
_imm_props = ImmGetProperty ( GetKeyboardLayout ( 0 ) , IGP_PROPERTY ) ;
2006-08-31 16:50:12 +02:00
break ;
2021-02-20 10:27:52 +01:00
case WM_PAINT : {
RECT r ;
GetUpdateRect ( hwnd , & r , FALSE ) ;
2021-01-16 16:42:59 +01:00
video_driver - > MakeDirty ( r . left , r . top , r . right - r . left , r . bottom - r . top ) ;
2007-06-19 17:04:08 +02:00
2021-02-20 10:27:52 +01:00
ValidateRect ( hwnd , nullptr ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2021-02-20 10:27:52 +01:00
}
2005-07-23 17:16:57 +02:00
2006-08-31 16:50:12 +02:00
case WM_PALETTECHANGED :
if ( ( HWND ) wParam = = hwnd ) return 0 ;
2017-08-13 20:38:42 +02:00
FALLTHROUGH ;
2006-08-31 16:50:12 +02:00
2021-01-16 16:58:48 +01:00
case WM_QUERYNEWPALETTE :
video_driver - > PaletteChanged ( hwnd ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2005-07-23 17:16:57 +02:00
2006-08-31 16:50:12 +02:00
case WM_CLOSE :
2006-09-04 19:30:30 +02:00
HandleExitGameRequest ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2006-11-05 02:13:08 +01:00
case WM_DESTROY :
2008-06-16 21:38:41 +02:00
if ( _window_maximize ) _cur_resolution = _bck_resolution ;
2006-11-05 02:13:08 +01:00
return 0 ;
2006-08-31 16:50:12 +02:00
case WM_LBUTTONDOWN :
SetCapture ( hwnd ) ;
_left_button_down = true ;
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2005-07-23 17:16:57 +02:00
return 0 ;
2006-08-31 16:50:12 +02:00
case WM_LBUTTONUP :
ReleaseCapture ( ) ;
_left_button_down = false ;
_left_button_clicked = false ;
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
case WM_RBUTTONDOWN :
SetCapture ( hwnd ) ;
_right_button_down = true ;
_right_button_clicked = true ;
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
case WM_RBUTTONUP :
ReleaseCapture ( ) ;
_right_button_down = false ;
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
case WM_MOUSELEAVE :
UndrawMouseCursor ( ) ;
_cursor . in_window = false ;
2006-11-05 02:53:12 +01:00
if ( ! _left_button_down & & ! _right_button_down ) MyShowCursor ( true ) ;
return 0 ;
2006-03-24 01:42:35 +01:00
2006-08-31 16:50:12 +02:00
case WM_MOUSEMOVE : {
2023-05-08 19:01:06 +02:00
int x = ( int16_t ) LOWORD ( lParam ) ;
int y = ( int16_t ) HIWORD ( lParam ) ;
2005-07-23 17:16:57 +02:00
2006-08-31 16:50:12 +02:00
/* If the mouse was not in the window and it has moved it means it has
2006-11-05 02:53:12 +01:00
* come into the window , so start drawing the mouse . Also start
2006-08-31 16:50:12 +02:00
* tracking the mouse for exiting the window */
if ( ! _cursor . in_window ) {
_cursor . in_window = true ;
2021-01-16 16:31:08 +01:00
TRACKMOUSEEVENT tme ;
tme . cbSize = sizeof ( tme ) ;
tme . dwFlags = TME_LEAVE ;
tme . hwndTrack = hwnd ;
TrackMouseEvent ( & tme ) ;
2006-08-31 16:50:12 +02:00
}
2005-07-23 17:16:57 +02:00
2017-12-09 20:21:45 +01:00
if ( _cursor . fix_at ) {
/* Get all queued mouse events now in case we have to warp the cursor. In the
* end , we only care about the current mouse position and not bygone events . */
MSG m ;
while ( PeekMessage ( & m , hwnd , WM_MOUSEMOVE , WM_MOUSEMOVE , PM_REMOVE | PM_NOYIELD | PM_QS_INPUT ) ) {
2023-05-08 19:01:06 +02:00
x = ( int16_t ) LOWORD ( m . lParam ) ;
y = ( int16_t ) HIWORD ( m . lParam ) ;
2017-12-09 20:21:45 +01:00
}
}
2023-06-03 23:10:16 +02:00
if ( _cursor . UpdateCursorPosition ( x , y ) ) {
2017-12-09 20:21:45 +01:00
POINT pt ;
2015-02-23 00:06:45 +01:00
pt . x = _cursor . pos . x ;
pt . y = _cursor . pos . y ;
ClientToScreen ( hwnd , & pt ) ;
SetCursorPos ( pt . x , pt . y ) ;
2005-07-23 17:16:57 +02:00
}
2006-08-31 16:50:12 +02:00
MyShowCursor ( false ) ;
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2005-07-23 17:16:57 +02:00
}
2013-08-05 22:37:25 +02:00
case WM_INPUTLANGCHANGE :
_imm_props = ImmGetProperty ( GetKeyboardLayout ( 0 ) , IGP_PROPERTY ) ;
break ;
case WM_IME_SETCONTEXT :
/* Don't show the composition window if we draw the string ourself. */
if ( DrawIMECompositionString ( ) ) lParam & = ~ ISC_SHOWUICOMPOSITIONWINDOW ;
break ;
2013-08-05 22:37:14 +02:00
case WM_IME_STARTCOMPOSITION :
SetCompositionPos ( hwnd ) ;
2013-08-05 22:37:25 +02:00
if ( DrawIMECompositionString ( ) ) return 0 ;
2013-08-05 22:37:14 +02:00
break ;
2013-08-05 22:37:06 +02:00
case WM_IME_COMPOSITION :
return HandleIMEComposition ( hwnd , wParam , lParam ) ;
2013-08-05 22:37:25 +02:00
case WM_IME_ENDCOMPOSITION :
/* Clear any pending composition string. */
2019-04-10 23:07:06 +02:00
HandleTextInput ( nullptr , true ) ;
2013-08-05 22:37:25 +02:00
if ( DrawIMECompositionString ( ) ) return 0 ;
break ;
2013-08-05 22:37:18 +02:00
case WM_IME_NOTIFY :
if ( wParam = = IMN_OPENCANDIDATE ) SetCandidatePos ( hwnd ) ;
break ;
2007-04-27 23:27:02 +02:00
case WM_DEADCHAR :
console = GB ( lParam , 16 , 8 ) = = 41 ;
return 0 ;
2007-03-10 01:30:18 +01:00
case WM_CHAR : {
uint scancode = GB ( lParam , 16 , 8 ) ;
uint charcode = wParam ;
2005-07-23 17:16:57 +02:00
2007-04-27 23:27:02 +02:00
/* If the console key is a dead-key, we need to press it twice to get a WM_CHAR message.
* But we then get two WM_CHAR messages , so ignore the first one */
if ( console & & scancode = = 41 ) {
console = false ;
return 0 ;
}
2013-08-05 22:36:20 +02:00
/* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
* clear the keycode so a previous WM_KEYDOWN doesn ' t become ' stuck ' . */
uint cur_keycode = keycode ;
keycode = 0 ;
2007-03-10 01:30:18 +01:00
2013-08-05 22:36:20 +02:00
return HandleCharMsg ( cur_keycode , charcode ) ;
2007-03-10 01:30:18 +01:00
}
2006-08-31 16:50:12 +02:00
2007-03-10 01:30:18 +01:00
case WM_KEYDOWN : {
2013-08-05 22:36:20 +02:00
/* No matter the keyboard layout, we will map the '~' to the console. */
uint scancode = GB ( lParam , 16 , 8 ) ;
2013-11-16 11:05:57 +01:00
keycode = scancode = = 41 ? ( uint ) WKC_BACKQUOTE : MapWindowsKey ( wParam ) ;
2006-08-31 16:50:12 +02:00
2013-08-05 22:36:20 +02:00
uint charcode = MapVirtualKey ( wParam , MAPVK_VK_TO_CHAR ) ;
/* No character translation? */
if ( charcode = = 0 ) {
2013-08-05 22:36:36 +02:00
HandleKeypress ( keycode , 0 ) ;
2013-08-05 22:36:20 +02:00
return 0 ;
}
2021-04-08 23:16:43 +02:00
/* If an edit box is in focus, wait for the corresponding WM_CHAR message. */
if ( ! EditBoxInGlobalFocus ( ) ) {
/* Is the console key a dead key? If yes, ignore the first key down event. */
if ( HasBit ( charcode , 31 ) & & ! console ) {
if ( scancode = = 41 ) {
console = true ;
return 0 ;
}
2013-08-05 22:36:20 +02:00
}
2021-04-08 23:16:43 +02:00
console = false ;
2013-08-05 22:36:20 +02:00
2021-04-08 23:16:43 +02:00
/* IMEs and other input methods sometimes send a WM_CHAR without a WM_KEYDOWN,
* clear the keycode so a previous WM_KEYDOWN doesn ' t become ' stuck ' . */
uint cur_keycode = keycode ;
keycode = 0 ;
2013-08-05 22:36:20 +02:00
2021-04-08 23:16:43 +02:00
return HandleCharMsg ( cur_keycode , LOWORD ( charcode ) ) ;
}
return 0 ;
2005-07-23 17:16:57 +02:00
}
2009-03-15 01:32:18 +01:00
case WM_SYSKEYDOWN : // user presses F10 or Alt, both activating the title-menu
2006-08-31 16:50:12 +02:00
switch ( wParam ) {
case VK_RETURN :
2009-03-15 01:32:18 +01:00
case ' F ' : // Full Screen on ALT + ENTER/F
2021-01-16 16:43:03 +01:00
ToggleFullScreen ( ! video_driver - > fullscreen ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2005-07-23 17:16:57 +02:00
2009-03-15 01:32:18 +01:00
case VK_MENU : // Just ALT
2006-08-31 16:50:12 +02:00
return 0 ; // do nothing
2009-03-15 01:32:18 +01:00
case VK_F10 : // F10, ignore activation of menu
2013-08-05 22:36:36 +02:00
HandleKeypress ( MapWindowsKey ( wParam ) , 0 ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2009-03-15 01:32:18 +01:00
default : // ALT in combination with something else
2013-08-05 22:36:36 +02:00
HandleKeypress ( MapWindowsKey ( wParam ) , 0 ) ;
2006-08-31 16:50:12 +02:00
break ;
}
2005-07-23 17:16:57 +02:00
break ;
2006-08-31 16:50:12 +02:00
case WM_SIZE :
2007-06-30 17:02:21 +02:00
if ( wParam ! = SIZE_MINIMIZED ) {
2006-11-05 02:09:57 +01:00
/* Set maximized flag when we maximize (obviously), but also when we
* switched to fullscreen from a maximized state */
_window_maximize = ( wParam = = SIZE_MAXIMIZED | | ( _window_maximize & & _fullscreen ) ) ;
2012-04-30 18:49:26 +02:00
if ( _window_maximize | | _fullscreen ) _bck_resolution = _cur_resolution ;
2021-01-16 16:58:48 +01:00
video_driver - > ClientSizeChanged ( LOWORD ( lParam ) , HIWORD ( lParam ) ) ;
2006-08-31 16:50:12 +02:00
}
return 0 ;
case WM_SIZING : {
2009-01-10 01:31:47 +01:00
RECT * r = ( RECT * ) lParam ;
2006-08-31 16:50:12 +02:00
RECT r2 ;
int w , h ;
SetRect ( & r2 , 0 , 0 , 0 , 0 ) ;
AdjustWindowRect ( & r2 , GetWindowLong ( hwnd , GWL_STYLE ) , FALSE ) ;
w = r - > right - r - > left - ( r2 . right - r2 . left ) ;
h = r - > bottom - r - > top - ( r2 . bottom - r2 . top ) ;
2021-01-08 11:16:18 +01:00
w = std : : max ( w , 64 ) ;
h = std : : max ( h , 64 ) ;
2006-08-31 16:50:12 +02:00
SetRect ( & r2 , 0 , 0 , w , h ) ;
AdjustWindowRect ( & r2 , GetWindowLong ( hwnd , GWL_STYLE ) , FALSE ) ;
w = r2 . right - r2 . left ;
h = r2 . bottom - r2 . top ;
switch ( wParam ) {
case WMSZ_BOTTOM :
r - > bottom = r - > top + h ;
break ;
case WMSZ_BOTTOMLEFT :
r - > bottom = r - > top + h ;
r - > left = r - > right - w ;
break ;
case WMSZ_BOTTOMRIGHT :
r - > bottom = r - > top + h ;
r - > right = r - > left + w ;
break ;
case WMSZ_LEFT :
r - > left = r - > right - w ;
break ;
case WMSZ_RIGHT :
r - > right = r - > left + w ;
break ;
case WMSZ_TOP :
r - > top = r - > bottom - h ;
break ;
case WMSZ_TOPLEFT :
r - > top = r - > bottom - h ;
r - > left = r - > right - w ;
break ;
case WMSZ_TOPRIGHT :
r - > top = r - > bottom - h ;
r - > right = r - > left + w ;
break ;
}
return TRUE ;
2005-07-23 17:16:57 +02:00
}
2022-09-07 22:17:10 +02:00
case WM_DPICHANGED : {
2021-04-20 12:49:20 +02:00
auto did_adjust = AdjustGUIZoom ( true ) ;
2022-09-07 22:17:10 +02:00
/* Resize the window to match the new DPI setting. */
RECT * prcNewWindow = ( RECT * ) lParam ;
SetWindowPos ( hwnd ,
2023-12-26 00:24:59 +01:00
nullptr ,
2022-09-07 22:17:10 +02:00
prcNewWindow - > left ,
prcNewWindow - > top ,
prcNewWindow - > right - prcNewWindow - > left ,
prcNewWindow - > bottom - prcNewWindow - > top ,
SWP_NOZORDER | SWP_NOACTIVATE ) ;
if ( did_adjust ) ReInitAllWindows ( true ) ;
return 0 ;
}
2009-03-15 01:32:18 +01:00
/* needed for wheel */
2005-07-23 17:16:57 +02:00
# if !defined(WM_MOUSEWHEEL)
2006-08-31 16:50:12 +02:00
# define WM_MOUSEWHEEL 0x020A
2009-03-15 01:32:18 +01:00
# endif /* WM_MOUSEWHEEL */
2005-07-23 17:16:57 +02:00
# if !defined(GET_WHEEL_DELTA_WPARAM)
# define GET_WHEEL_DELTA_WPARAM(wparam) ((short)HIWORD(wparam))
2009-03-15 01:32:18 +01:00
# endif /* GET_WHEEL_DELTA_WPARAM */
2005-07-23 17:16:57 +02:00
2006-08-31 16:50:12 +02:00
case WM_MOUSEWHEEL : {
int delta = GET_WHEEL_DELTA_WPARAM ( wParam ) ;
2005-07-23 17:16:57 +02:00
2006-08-31 16:50:12 +02:00
if ( delta < 0 ) {
_cursor . wheel + + ;
} else if ( delta > 0 ) {
_cursor . wheel - - ;
}
2006-11-15 22:01:19 +01:00
HandleMouseEvents ( ) ;
2006-08-31 16:50:12 +02:00
return 0 ;
2005-07-23 17:16:57 +02:00
}
2007-06-30 17:02:21 +02:00
case WM_SETFOCUS :
2021-01-16 16:43:12 +01:00
video_driver - > has_focus = true ;
2013-08-05 22:37:14 +02:00
SetCompositionPos ( hwnd ) ;
2007-06-30 17:02:21 +02:00
break ;
case WM_KILLFOCUS :
2021-01-16 16:43:12 +01:00
video_driver - > has_focus = false ;
2007-06-30 17:02:21 +02:00
break ;
case WM_ACTIVATE : {
2007-08-09 22:59:36 +02:00
/* Don't do anything if we are closing openttd */
if ( _exit_game ) break ;
2007-06-30 17:02:21 +02:00
bool active = ( LOWORD ( wParam ) ! = WA_INACTIVE ) ;
bool minimized = ( HIWORD ( wParam ) ! = 0 ) ;
2021-01-16 16:43:03 +01:00
if ( video_driver - > fullscreen ) {
2007-06-30 17:02:21 +02:00
if ( active & & minimized ) {
2007-06-17 21:00:45 +02:00
/* Restore the game window */
2021-03-01 23:18:53 +01:00
Dimension d = _bck_resolution ; // Save current non-fullscreen window size as it will be overwritten by ShowWindow.
2007-06-17 21:00:45 +02:00
ShowWindow ( hwnd , SW_RESTORE ) ;
2021-03-01 23:18:53 +01:00
_bck_resolution = d ;
2021-01-16 16:42:59 +01:00
video_driver - > MakeWindow ( true ) ;
2007-06-30 17:02:21 +02:00
} else if ( ! active & & ! minimized ) {
2007-06-17 21:00:45 +02:00
/* Minimise the window and restore desktop */
ShowWindow ( hwnd , SW_MINIMIZE ) ;
2019-04-10 23:07:06 +02:00
ChangeDisplaySettings ( nullptr , 0 ) ;
2007-06-17 21:00:45 +02:00
}
}
2010-08-01 20:53:30 +02:00
break ;
}
2007-08-04 15:51:41 +02:00
}
2007-06-30 17:02:21 +02:00
2005-07-23 17:16:57 +02:00
return DefWindowProc ( hwnd , msg , wParam , lParam ) ;
}
2007-03-07 12:47:46 +01:00
static void RegisterWndClass ( )
2005-07-23 17:16:57 +02:00
{
2006-08-31 16:50:12 +02:00
static bool registered = false ;
2021-01-16 15:27:13 +01:00
if ( registered ) return ;
HINSTANCE hinst = GetModuleHandle ( nullptr ) ;
WNDCLASS wnd = {
CS_OWNDC ,
WndProcGdi ,
0 ,
0 ,
hinst ,
LoadIcon ( hinst , MAKEINTRESOURCE ( 100 ) ) ,
LoadCursor ( nullptr , IDC_ARROW ) ,
0 ,
0 ,
2021-02-21 20:48:21 +01:00
L " OTTD "
2021-01-16 15:27:13 +01:00
} ;
registered = true ;
2023-04-19 22:47:36 +02:00
if ( ! RegisterClass ( & wnd ) ) UserError ( " RegisterClass failed " ) ;
2005-07-23 17:16:57 +02:00
}
2008-06-16 21:38:41 +02:00
static const Dimension default_resolutions [ ] = {
2006-08-31 16:50:12 +02:00
{ 640 , 480 } ,
{ 800 , 600 } ,
{ 1024 , 768 } ,
{ 1152 , 864 } ,
{ 1280 , 800 } ,
{ 1280 , 960 } ,
{ 1280 , 1024 } ,
{ 1400 , 1050 } ,
{ 1600 , 1200 } ,
{ 1680 , 1050 } ,
{ 1920 , 1200 }
2005-07-23 17:16:57 +02:00
} ;
2023-05-08 19:01:06 +02:00
static void FindResolutions ( uint8_t bpp )
2005-07-23 17:16:57 +02:00
{
2019-04-12 18:46:49 +02:00
_resolutions . clear ( ) ;
2021-01-16 16:43:03 +01:00
DEVMODE dm ;
for ( uint i = 0 ; EnumDisplaySettings ( nullptr , i , & dm ) ! = 0 ; i + + ) {
2019-04-12 18:46:49 +02:00
if ( dm . dmBitsPerPel ! = bpp | | dm . dmPelsWidth < 640 | | dm . dmPelsHeight < 480 ) continue ;
if ( std : : find ( _resolutions . begin ( ) , _resolutions . end ( ) , Dimension ( dm . dmPelsWidth , dm . dmPelsHeight ) ) ! = _resolutions . end ( ) ) continue ;
_resolutions . emplace_back ( dm . dmPelsWidth , dm . dmPelsHeight ) ;
2005-07-23 17:16:57 +02:00
}
/* We have found no resolutions, show the default list */
2019-04-12 18:46:49 +02:00
if ( _resolutions . empty ( ) ) {
_resolutions . assign ( std : : begin ( default_resolutions ) , std : : end ( default_resolutions ) ) ;
2005-07-23 17:16:57 +02:00
}
2019-04-12 18:46:49 +02:00
SortResolutions ( ) ;
2005-07-23 17:16:57 +02:00
}
2021-01-16 16:43:03 +01:00
void VideoDriver_Win32Base : : Initialize ( )
{
this - > UpdateAutoResolution ( ) ;
RegisterWndClass ( ) ;
FindResolutions ( this - > GetFullscreenBpp ( ) ) ;
/* fullscreen uses those */
2021-01-16 16:43:12 +01:00
this - > width = this - > width_org = _cur_resolution . width ;
this - > height = this - > height_org = _cur_resolution . height ;
2021-01-16 16:43:03 +01:00
2021-06-12 09:10:17 +02:00
Debug ( driver , 2 , " Resolution for display: {}x{} " , _cur_resolution . width , _cur_resolution . height ) ;
2021-01-16 16:43:03 +01:00
}
void VideoDriver_Win32Base : : Stop ( )
{
DestroyWindow ( this - > main_wnd ) ;
if ( this - > fullscreen ) ChangeDisplaySettings ( nullptr , 0 ) ;
MyShowCursor ( true ) ;
}
2021-01-16 16:42:59 +01:00
void VideoDriver_Win32Base : : MakeDirty ( int left , int top , int width , int height )
2005-07-23 17:16:57 +02:00
{
2021-02-20 10:27:52 +01:00
Rect r = { left , top , left + width , top + height } ;
2021-02-21 00:35:35 +01:00
this - > dirty_rect = BoundingRect ( this - > dirty_rect , r ) ;
2005-07-23 17:16:57 +02:00
}
2021-01-16 16:42:59 +01:00
void VideoDriver_Win32Base : : CheckPaletteAnim ( )
2005-07-23 17:16:57 +02:00
{
2021-06-17 10:34:43 +02:00
if ( ! CopyPalette ( _local_palette ) ) return ;
2021-02-20 10:27:52 +01:00
this - > MakeDirty ( 0 , 0 , _screen . width , _screen . height ) ;
2005-07-23 17:16:57 +02:00
}
2021-01-16 16:42:59 +01:00
void VideoDriver_Win32Base : : InputLoop ( )
2021-02-19 11:01:49 +01:00
{
bool old_ctrl_pressed = _ctrl_pressed ;
2021-01-16 16:43:12 +01:00
_ctrl_pressed = this - > has_focus & & GetAsyncKeyState ( VK_CONTROL ) < 0 ;
_shift_pressed = this - > has_focus & & GetAsyncKeyState ( VK_SHIFT ) < 0 ;
2021-02-19 11:01:49 +01:00
/* Speedup when pressing tab, except when using ALT+TAB
* to switch to another application . */
2021-02-28 15:41:03 +01:00
this - > fast_forward_key_pressed = this - > has_focus & & GetAsyncKeyState ( VK_TAB ) < 0 & & GetAsyncKeyState ( VK_MENU ) > = 0 ;
2021-02-19 11:01:49 +01:00
/* Determine which directional keys are down. */
2021-01-16 16:43:12 +01:00
if ( this - > has_focus ) {
2021-02-19 11:01:49 +01:00
_dirkeys =
( GetAsyncKeyState ( VK_LEFT ) < 0 ? 1 : 0 ) +
( GetAsyncKeyState ( VK_UP ) < 0 ? 2 : 0 ) +
( GetAsyncKeyState ( VK_RIGHT ) < 0 ? 4 : 0 ) +
( GetAsyncKeyState ( VK_DOWN ) < 0 ? 8 : 0 ) ;
} else {
_dirkeys = 0 ;
}
if ( old_ctrl_pressed ! = _ctrl_pressed ) HandleCtrlChanged ( ) ;
}
2021-02-24 14:45:10 +01:00
bool VideoDriver_Win32Base : : PollEvent ( )
2005-07-23 17:16:57 +02:00
{
MSG mesg ;
2021-02-24 14:45:10 +01:00
if ( ! PeekMessage ( & mesg , nullptr , 0 , 0 , PM_REMOVE ) ) return false ;
/* Convert key messages to char messages if we want text input. */
if ( EditBoxInGlobalFocus ( ) ) TranslateMessage ( & mesg ) ;
DispatchMessage ( & mesg ) ;
return true ;
}
void VideoDriver_Win32Base : : MainLoop ( )
{
2021-02-24 15:22:23 +01:00
this - > StartGameThread ( ) ;
2006-02-01 08:36:15 +01:00
for ( ; ; ) {
2019-03-11 00:45:39 +01:00
if ( _exit_game ) break ;
2005-07-23 17:16:57 +02:00
2021-02-24 15:04:41 +01:00
this - > Tick ( ) ;
2021-02-20 11:54:33 +01:00
this - > SleepTillNextTick ( ) ;
2005-07-23 17:16:57 +02:00
}
2021-02-24 15:22:23 +01:00
this - > StopGameThread ( ) ;
2005-07-23 17:16:57 +02:00
}
2021-01-16 16:43:16 +01:00
void VideoDriver_Win32Base : : ClientSizeChanged ( int w , int h , bool force )
2021-01-16 16:58:48 +01:00
{
/* Allocate backing store of the new size. */
2021-01-16 16:43:16 +01:00
if ( this - > AllocateBackingStore ( w , h , force ) ) {
2021-06-17 10:34:43 +02:00
CopyPalette ( _local_palette , true ) ;
2021-01-16 16:58:48 +01:00
BlitterFactory : : GetCurrentBlitter ( ) - > PostResize ( ) ;
GameSizeChanged ( ) ;
}
}
2021-01-16 16:42:59 +01:00
bool VideoDriver_Win32Base : : ChangeResolution ( int w , int h )
2005-07-23 17:16:57 +02:00
{
2021-01-16 16:43:02 +01:00
if ( _window_maximize ) ShowWindow ( this - > main_wnd , SW_SHOWNORMAL ) ;
2012-07-10 19:47:03 +02:00
2021-01-16 16:43:12 +01:00
this - > width = this - > width_org = w ;
this - > height = this - > height_org = h ;
2005-07-23 17:16:57 +02:00
2019-03-11 00:45:39 +01:00
return this - > MakeWindow ( _fullscreen ) ; // _wnd.fullscreen screws up ingame resolution switching
2005-07-23 17:16:57 +02:00
}
2021-01-16 16:42:59 +01:00
bool VideoDriver_Win32Base : : ToggleFullscreen ( bool full_screen )
2006-08-31 16:50:12 +02:00
{
2021-03-08 16:57:59 +01:00
bool res = this - > MakeWindow ( full_screen ) ;
InvalidateWindowClassesData ( WC_GAME_OPTIONS , 3 ) ;
return res ;
2006-08-31 16:50:12 +02:00
}
2011-10-04 23:35:40 +02:00
2021-01-16 16:42:59 +01:00
void VideoDriver_Win32Base : : EditBoxLostFocus ( )
2013-08-05 22:37:11 +02:00
{
2021-01-16 16:43:02 +01:00
CancelIMEComposition ( this - > main_wnd ) ;
SetCompositionPos ( this - > main_wnd ) ;
SetCandidatePos ( this - > main_wnd ) ;
2013-08-05 22:37:11 +02:00
}
2021-01-14 21:53:06 +01:00
2023-09-16 22:20:53 +02:00
static BOOL CALLBACK MonitorEnumProc ( HMONITOR hMonitor , HDC , LPRECT , LPARAM data )
2021-03-09 10:22:52 +01:00
{
2021-05-13 23:47:08 +02:00
auto & list = * reinterpret_cast < std : : vector < int > * > ( data ) ;
2021-03-09 10:22:52 +01:00
2021-05-13 23:47:08 +02:00
MONITORINFOEX monitorInfo = { } ;
monitorInfo . cbSize = sizeof ( MONITORINFOEX ) ;
GetMonitorInfo ( hMonitor , & monitorInfo ) ;
2021-03-09 10:22:52 +01:00
2021-05-13 23:47:08 +02:00
DEVMODE devMode = { } ;
devMode . dmSize = sizeof ( DEVMODE ) ;
devMode . dmDriverExtra = 0 ;
EnumDisplaySettings ( monitorInfo . szDevice , ENUM_CURRENT_SETTINGS , & devMode ) ;
if ( devMode . dmDisplayFrequency ! = 0 ) list . push_back ( devMode . dmDisplayFrequency ) ;
return true ;
}
2021-03-09 10:22:52 +01:00
2021-05-13 23:47:08 +02:00
std : : vector < int > VideoDriver_Win32Base : : GetListOfMonitorRefreshRates ( )
{
std : : vector < int > rates = { } ;
EnumDisplayMonitors ( nullptr , nullptr , MonitorEnumProc , reinterpret_cast < LPARAM > ( & rates ) ) ;
2021-03-09 10:22:52 +01:00
return rates ;
}
2021-01-16 16:42:59 +01:00
Dimension VideoDriver_Win32Base : : GetScreenSize ( ) const
2021-01-14 21:53:06 +01:00
{
return { static_cast < uint > ( GetSystemMetrics ( SM_CXSCREEN ) ) , static_cast < uint > ( GetSystemMetrics ( SM_CYSCREEN ) ) } ;
}
2021-01-08 22:17:04 +01:00
2021-01-16 16:42:59 +01:00
float VideoDriver_Win32Base : : GetDPIScale ( )
2021-01-08 22:17:04 +01:00
{
typedef UINT ( WINAPI * PFNGETDPIFORWINDOW ) ( HWND hwnd ) ;
typedef UINT ( WINAPI * PFNGETDPIFORSYSTEM ) ( VOID ) ;
typedef HRESULT ( WINAPI * PFNGETDPIFORMONITOR ) ( HMONITOR hMonitor , int dpiType , UINT * dpiX , UINT * dpiY ) ;
static PFNGETDPIFORWINDOW _GetDpiForWindow = nullptr ;
static PFNGETDPIFORSYSTEM _GetDpiForSystem = nullptr ;
static PFNGETDPIFORMONITOR _GetDpiForMonitor = nullptr ;
static bool init_done = false ;
if ( ! init_done ) {
init_done = true ;
2024-01-10 22:38:58 +01:00
static LibraryLoader _user32 ( " user32.dll " ) ;
static LibraryLoader _shcore ( " shcore.dll " ) ;
_GetDpiForWindow = _user32 . GetFunction ( " GetDpiForWindow " ) ;
_GetDpiForSystem = _user32 . GetFunction ( " GetDpiForSystem " ) ;
_GetDpiForMonitor = _shcore . GetFunction ( " GetDpiForMonitor " ) ;
2021-01-08 22:17:04 +01:00
}
UINT cur_dpi = 0 ;
2021-01-16 16:43:02 +01:00
if ( cur_dpi = = 0 & & _GetDpiForWindow ! = nullptr & & this - > main_wnd ! = nullptr ) {
2021-01-08 22:17:04 +01:00
/* Per window DPI is supported since Windows 10 Ver 1607. */
2021-01-16 16:43:02 +01:00
cur_dpi = _GetDpiForWindow ( this - > main_wnd ) ;
2021-01-08 22:17:04 +01:00
}
2021-01-16 16:43:02 +01:00
if ( cur_dpi = = 0 & & _GetDpiForMonitor ! = nullptr & & this - > main_wnd ! = nullptr ) {
2021-01-08 22:17:04 +01:00
/* Per monitor is supported since Windows 8.1. */
UINT dpiX , dpiY ;
2021-01-16 16:43:02 +01:00
if ( SUCCEEDED ( _GetDpiForMonitor ( MonitorFromWindow ( this - > main_wnd , MONITOR_DEFAULTTOPRIMARY ) , 0 /* MDT_EFFECTIVE_DPI */ , & dpiX , & dpiY ) ) ) {
2021-01-08 22:17:04 +01:00
cur_dpi = dpiX ; // X and Y are always identical.
}
}
if ( cur_dpi = = 0 & & _GetDpiForSystem ! = nullptr ) {
/* Fall back to system DPI. */
cur_dpi = _GetDpiForSystem ( ) ;
}
return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f ; // Default Windows DPI value is 96.
}
2021-02-20 10:49:27 +01:00
2021-01-16 16:42:59 +01:00
bool VideoDriver_Win32Base : : LockVideoBuffer ( )
2021-02-20 10:49:27 +01:00
{
2021-01-16 16:43:11 +01:00
if ( this - > buffer_locked ) return false ;
this - > buffer_locked = true ;
2021-01-16 16:43:09 +01:00
_screen . dst_ptr = this - > GetVideoPointer ( ) ;
2021-01-16 16:43:16 +01:00
assert ( _screen . dst_ptr ! = nullptr ) ;
2021-01-16 16:43:09 +01:00
2021-02-20 10:49:27 +01:00
return true ;
}
2021-01-16 16:42:59 +01:00
void VideoDriver_Win32Base : : UnlockVideoBuffer ( )
2021-02-20 10:49:27 +01:00
{
2021-01-16 16:43:16 +01:00
assert ( _screen . dst_ptr ! = nullptr ) ;
if ( _screen . dst_ptr ! = nullptr ) {
/* Hand video buffer back to the drawing backend. */
this - > ReleaseVideoPointer ( ) ;
_screen . dst_ptr = nullptr ;
}
2021-01-16 16:43:11 +01:00
this - > buffer_locked = false ;
2021-02-20 10:49:27 +01:00
}
2021-01-16 16:42:59 +01:00
static FVideoDriver_Win32GDI iFVideoDriver_Win32GDI ;
const char * VideoDriver_Win32GDI : : Start ( const StringList & param )
{
if ( BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) = = 0 ) return " Only real blitters supported " ;
2021-01-16 16:43:03 +01:00
this - > Initialize ( ) ;
2021-01-16 16:42:59 +01:00
2021-01-16 16:43:02 +01:00
this - > MakePalette ( ) ;
2021-01-16 16:58:48 +01:00
this - > AllocateBackingStore ( _cur_resolution . width , _cur_resolution . height ) ;
2021-01-16 16:42:59 +01:00
this - > MakeWindow ( _fullscreen ) ;
MarkWholeScreenDirty ( ) ;
2021-02-24 15:22:23 +01:00
this - > is_game_threaded = ! GetDriverParamBool ( param , " no_threads " ) & & ! GetDriverParamBool ( param , " no_thread " ) ;
2021-01-16 16:42:59 +01:00
return nullptr ;
}
void VideoDriver_Win32GDI : : Stop ( )
{
2021-01-16 16:43:02 +01:00
DeleteObject ( this - > gdi_palette ) ;
DeleteObject ( this - > dib_sect ) ;
2021-01-16 16:42:59 +01:00
2021-01-16 16:43:03 +01:00
this - > VideoDriver_Win32Base : : Stop ( ) ;
2021-01-16 16:42:59 +01:00
}
2021-01-16 16:58:48 +01:00
bool VideoDriver_Win32GDI : : AllocateBackingStore ( int w , int h , bool force )
{
uint bpp = BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ;
w = std : : max ( w , 64 ) ;
h = std : : max ( h , 64 ) ;
if ( ! force & & w = = _screen . width & & h = = _screen . height ) return false ;
2021-05-01 22:06:17 +02:00
BITMAPINFO * bi = ( BITMAPINFO * ) new char [ sizeof ( BITMAPINFOHEADER ) + sizeof ( RGBQUAD ) * 256 ] ( ) ;
2021-01-16 16:58:48 +01:00
bi - > bmiHeader . biSize = sizeof ( BITMAPINFOHEADER ) ;
2021-01-16 16:43:12 +01:00
bi - > bmiHeader . biWidth = this - > width = w ;
bi - > bmiHeader . biHeight = - ( this - > height = h ) ;
2021-01-16 16:58:48 +01:00
bi - > bmiHeader . biPlanes = 1 ;
bi - > bmiHeader . biBitCount = bpp ;
bi - > bmiHeader . biCompression = BI_RGB ;
2021-01-16 16:43:02 +01:00
if ( this - > dib_sect ) DeleteObject ( this - > dib_sect ) ;
2021-01-16 16:58:48 +01:00
HDC dc = GetDC ( 0 ) ;
2021-01-16 16:43:09 +01:00
this - > dib_sect = CreateDIBSection ( dc , bi , DIB_RGB_COLORS , ( VOID * * ) & this - > buffer_bits , nullptr , 0 ) ;
2021-05-01 22:06:17 +02:00
if ( this - > dib_sect = = nullptr ) {
delete [ ] bi ;
2023-04-19 22:47:36 +02:00
UserError ( " CreateDIBSection failed " ) ;
2021-05-01 22:06:17 +02:00
}
2021-01-16 16:58:48 +01:00
ReleaseDC ( 0 , dc ) ;
_screen . width = w ;
_screen . pitch = ( bpp = = 8 ) ? Align ( w , 4 ) : w ;
_screen . height = h ;
2021-01-16 16:43:09 +01:00
_screen . dst_ptr = this - > GetVideoPointer ( ) ;
2021-01-16 16:58:48 +01:00
2021-05-01 22:06:17 +02:00
delete [ ] bi ;
2021-01-16 16:58:48 +01:00
return true ;
}
2021-01-16 16:42:59 +01:00
bool VideoDriver_Win32GDI : : AfterBlitterChange ( )
{
assert ( BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ! = 0 ) ;
2021-03-08 13:48:32 +01:00
return this - > AllocateBackingStore ( _screen . width , _screen . height , true ) & & this - > MakeWindow ( _fullscreen , false ) ;
2021-01-16 16:58:48 +01:00
}
2021-01-16 16:43:02 +01:00
void VideoDriver_Win32GDI : : MakePalette ( )
{
2021-06-17 10:34:43 +02:00
CopyPalette ( _local_palette , true ) ;
2021-01-16 16:43:02 +01:00
2021-05-01 22:06:17 +02:00
LOGPALETTE * pal = ( LOGPALETTE * ) new char [ sizeof ( LOGPALETTE ) + ( 256 - 1 ) * sizeof ( PALETTEENTRY ) ] ( ) ;
2021-01-16 16:43:02 +01:00
pal - > palVersion = 0x300 ;
pal - > palNumEntries = 256 ;
for ( uint i = 0 ; i ! = 256 ; i + + ) {
pal - > palPalEntry [ i ] . peRed = _local_palette . palette [ i ] . r ;
pal - > palPalEntry [ i ] . peGreen = _local_palette . palette [ i ] . g ;
pal - > palPalEntry [ i ] . peBlue = _local_palette . palette [ i ] . b ;
pal - > palPalEntry [ i ] . peFlags = 0 ;
}
this - > gdi_palette = CreatePalette ( pal ) ;
2021-05-01 22:06:17 +02:00
delete [ ] pal ;
2023-04-19 22:47:36 +02:00
if ( this - > gdi_palette = = nullptr ) UserError ( " CreatePalette failed! \n " ) ;
2021-01-16 16:43:02 +01:00
}
void VideoDriver_Win32GDI : : UpdatePalette ( HDC dc , uint start , uint count )
{
RGBQUAD rgb [ 256 ] ;
for ( uint i = 0 ; i ! = count ; i + + ) {
rgb [ i ] . rgbRed = _local_palette . palette [ start + i ] . r ;
rgb [ i ] . rgbGreen = _local_palette . palette [ start + i ] . g ;
rgb [ i ] . rgbBlue = _local_palette . palette [ start + i ] . b ;
rgb [ i ] . rgbReserved = 0 ;
}
SetDIBColorTable ( dc , start , count , rgb ) ;
}
2021-01-16 16:58:48 +01:00
void VideoDriver_Win32GDI : : PaletteChanged ( HWND hWnd )
{
HDC hDC = GetWindowDC ( hWnd ) ;
2021-01-16 16:43:02 +01:00
HPALETTE hOldPalette = SelectPalette ( hDC , this - > gdi_palette , FALSE ) ;
2021-01-16 16:58:48 +01:00
UINT nChanged = RealizePalette ( hDC ) ;
SelectPalette ( hDC , hOldPalette , TRUE ) ;
ReleaseDC ( hWnd , hDC ) ;
if ( nChanged ! = 0 ) this - > MakeDirty ( 0 , 0 , _screen . width , _screen . height ) ;
}
void VideoDriver_Win32GDI : : Paint ( )
{
PerformanceMeasurer framerate ( PFE_VIDEO ) ;
2021-02-21 00:35:35 +01:00
if ( IsEmptyRect ( this - > dirty_rect ) ) return ;
2021-01-16 16:58:48 +01:00
2021-01-16 16:43:02 +01:00
HDC dc = GetDC ( this - > main_wnd ) ;
2021-01-16 16:58:48 +01:00
HDC dc2 = CreateCompatibleDC ( dc ) ;
2021-01-16 16:43:02 +01:00
HBITMAP old_bmp = ( HBITMAP ) SelectObject ( dc2 , this - > dib_sect ) ;
HPALETTE old_palette = SelectPalette ( dc , this - > gdi_palette , FALSE ) ;
2021-01-16 16:58:48 +01:00
2021-06-17 10:34:43 +02:00
if ( _local_palette . count_dirty ! = 0 ) {
2021-01-16 16:58:48 +01:00
Blitter * blitter = BlitterFactory : : GetCurrentBlitter ( ) ;
switch ( blitter - > UsePaletteAnimation ( ) ) {
case Blitter : : PALETTE_ANIMATION_VIDEO_BACKEND :
2021-01-16 16:43:02 +01:00
this - > UpdatePalette ( dc2 , _local_palette . first_dirty , _local_palette . count_dirty ) ;
2021-01-16 16:58:48 +01:00
break ;
2021-01-16 16:43:16 +01:00
case Blitter : : PALETTE_ANIMATION_BLITTER : {
2021-01-16 16:58:48 +01:00
blitter - > PaletteAnimate ( _local_palette ) ;
break ;
2021-01-16 16:43:16 +01:00
}
2021-01-16 16:58:48 +01:00
case Blitter : : PALETTE_ANIMATION_NONE :
break ;
default :
NOT_REACHED ( ) ;
}
2021-06-17 10:34:43 +02:00
_local_palette . count_dirty = 0 ;
2021-01-16 16:58:48 +01:00
}
2021-01-16 16:43:12 +01:00
BitBlt ( dc , 0 , 0 , this - > width , this - > height , dc2 , 0 , 0 , SRCCOPY ) ;
2021-01-16 16:58:48 +01:00
SelectPalette ( dc , old_palette , TRUE ) ;
SelectObject ( dc2 , old_bmp ) ;
DeleteDC ( dc2 ) ;
2021-01-16 16:43:02 +01:00
ReleaseDC ( this - > main_wnd , dc ) ;
2021-01-16 16:58:48 +01:00
2021-02-21 00:35:35 +01:00
this - > dirty_rect = { } ;
2021-01-16 16:58:48 +01:00
}
2021-01-16 16:43:02 +01:00
# ifdef _DEBUG
/* Keep this function here..
* It allows you to redraw the screen from within the MSVC debugger */
/* static */ int VideoDriver_Win32GDI : : RedrawScreenDebug ( )
{
static int _fooctr ;
VideoDriver_Win32GDI * drv = static_cast < VideoDriver_Win32GDI * > ( VideoDriver : : GetInstance ( ) ) ;
2021-01-16 16:43:09 +01:00
_screen . dst_ptr = drv - > GetVideoPointer ( ) ;
UpdateWindows ( ) ;
2021-01-16 16:43:02 +01:00
drv - > Paint ( ) ;
GdiFlush ( ) ;
return _fooctr + + ;
}
# endif
2021-01-16 16:43:04 +01:00
# ifdef WITH_OPENGL
# include <GL/gl.h>
# include "../3rdparty/opengl/glext.h"
2021-01-16 16:43:17 +01:00
# include "../3rdparty/opengl/wglext.h"
2021-01-16 16:43:04 +01:00
# include "opengl.h"
# ifndef PFD_SUPPORT_COMPOSITION
# define PFD_SUPPORT_COMPOSITION 0x00008000
# endif
2021-01-16 16:43:17 +01:00
static PFNWGLCREATECONTEXTATTRIBSARBPROC _wglCreateContextAttribsARB = nullptr ;
2021-01-16 16:43:18 +01:00
static PFNWGLSWAPINTERVALEXTPROC _wglSwapIntervalEXT = nullptr ;
2021-01-16 16:43:17 +01:00
static bool _hasWGLARBCreateContextProfile = false ; ///< Is WGL_ARB_create_context_profile supported?
2021-02-24 23:06:56 +01:00
/** Platform-specific callback to get an OpenGL function pointer. */
2021-01-16 16:43:05 +01:00
static OGLProc GetOGLProcAddressCallback ( const char * proc )
{
2021-02-24 23:06:56 +01:00
OGLProc ret = reinterpret_cast < OGLProc > ( wglGetProcAddress ( proc ) ) ;
if ( ret = = nullptr ) {
/* Non-extension GL function? Try normal loading. */
ret = reinterpret_cast < OGLProc > ( GetProcAddress ( GetModuleHandle ( L " opengl32 " ) , proc ) ) ;
}
return ret ;
2021-01-16 16:43:05 +01:00
}
2021-01-16 16:43:17 +01:00
/**
* Set the pixel format of a window -
* @ param dc Device context to set the pixel format of .
* @ return nullptr on success , error message otherwise .
*/
2023-09-16 23:27:16 +02:00
static const char * SelectPixelFormat ( HDC dc )
2021-01-16 16:43:17 +01:00
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof ( PIXELFORMATDESCRIPTOR ) , // Size of this struct.
1 , // Version of this struct.
PFD_DRAW_TO_WINDOW | // Require window support.
PFD_SUPPORT_OPENGL | // Require OpenGL support.
PFD_DOUBLEBUFFER | // Use double buffering.
PFD_DEPTH_DONTCARE ,
PFD_TYPE_RGBA , // Request RGBA format.
24 , // 24 bpp (excluding alpha).
0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // Colour bits and shift ignored.
0 , 0 , 0 , 0 , 0 , // No accumulation buffer.
0 , 0 , // No depth/stencil buffer.
0 , // No aux buffers.
PFD_MAIN_PLANE , // Main layer.
0 , 0 , 0 , 0 // Ignored/reserved.
} ;
2023-12-02 23:12:50 +01:00
pfd . dwFlags | = PFD_SUPPORT_COMPOSITION ; // Make OpenTTD compatible with Aero.
2021-01-16 16:43:17 +01:00
/* Choose a suitable pixel format. */
int format = ChoosePixelFormat ( dc , & pfd ) ;
if ( format = = 0 ) return " No suitable pixel format found " ;
if ( ! SetPixelFormat ( dc , format , & pfd ) ) return " Can't set pixel format " ;
return nullptr ;
}
/** Bind all WGL extension functions we need. */
static void LoadWGLExtensions ( )
{
/* Querying the supported WGL extensions and loading the matching
* functions requires a valid context , even for the extensions
* regarding context creation . To get around this , we create
* a dummy window with a dummy context . The extension functions
* remain valid even after this context is destroyed . */
HWND wnd = CreateWindow ( _T ( " STATIC " ) , _T ( " dummy " ) , WS_OVERLAPPEDWINDOW , 0 , 0 , 0 , 0 , nullptr , nullptr , GetModuleHandle ( nullptr ) , nullptr ) ;
HDC dc = GetDC ( wnd ) ;
/* Set pixel format of the window. */
2023-09-16 23:27:16 +02:00
if ( SelectPixelFormat ( dc ) = = nullptr ) {
2021-01-16 16:43:17 +01:00
/* Create rendering context. */
HGLRC rc = wglCreateContext ( dc ) ;
if ( rc ! = nullptr ) {
wglMakeCurrent ( dc , rc ) ;
2021-05-14 15:41:00 +02:00
# ifdef __MINGW32__
/* GCC doesn't understand the expected usage of wglGetProcAddress(). */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
# endif /* __MINGW32__ */
2021-01-16 16:43:17 +01:00
/* Get list of WGL extensions. */
PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = ( PFNWGLGETEXTENSIONSSTRINGARBPROC ) wglGetProcAddress ( " wglGetExtensionsStringARB " ) ;
if ( wglGetExtensionsStringARB ! = nullptr ) {
const char * wgl_exts = wglGetExtensionsStringARB ( dc ) ;
/* Bind supported functions. */
if ( FindStringInExtensionList ( wgl_exts , " WGL_ARB_create_context " ) ! = nullptr ) {
_wglCreateContextAttribsARB = ( PFNWGLCREATECONTEXTATTRIBSARBPROC ) wglGetProcAddress ( " wglCreateContextAttribsARB " ) ;
}
_hasWGLARBCreateContextProfile = FindStringInExtensionList ( wgl_exts , " WGL_ARB_create_context_profile " ) ! = nullptr ;
2021-01-16 16:43:18 +01:00
if ( FindStringInExtensionList ( wgl_exts , " WGL_EXT_swap_control " ) ! = nullptr ) {
_wglSwapIntervalEXT = ( PFNWGLSWAPINTERVALEXTPROC ) wglGetProcAddress ( " wglSwapIntervalEXT " ) ;
}
2021-01-16 16:43:17 +01:00
}
2021-05-14 15:41:00 +02:00
# ifdef __MINGW32__
# pragma GCC diagnostic pop
# endif
2021-01-16 16:43:17 +01:00
wglMakeCurrent ( nullptr , nullptr ) ;
wglDeleteContext ( rc ) ;
}
}
ReleaseDC ( wnd , dc ) ;
DestroyWindow ( wnd ) ;
}
2021-01-16 16:43:04 +01:00
static FVideoDriver_Win32OpenGL iFVideoDriver_Win32OpenGL ;
const char * VideoDriver_Win32OpenGL : : Start ( const StringList & param )
{
2021-01-16 16:43:25 +01:00
if ( BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) = = 0 ) return " Only real blitters supported " ;
2021-01-16 16:43:04 +01:00
Dimension old_res = _cur_resolution ; // Save current screen resolution in case of errors, as MakeWindow invalidates it.
2021-01-16 16:43:17 +01:00
LoadWGLExtensions ( ) ;
2021-01-16 16:43:04 +01:00
this - > Initialize ( ) ;
this - > MakeWindow ( _fullscreen ) ;
/* Create and initialize OpenGL context. */
const char * err = this - > AllocateContext ( ) ;
if ( err ! = nullptr ) {
this - > Stop ( ) ;
_cur_resolution = old_res ;
return err ;
}
2022-04-30 14:52:39 +02:00
this - > driver_info = GetName ( ) ;
this - > driver_info + = " ( " ;
this - > driver_info + = OpenGLBackend : : Get ( ) - > GetDriverName ( ) ;
this - > driver_info + = " ) " ;
2021-01-16 16:43:16 +01:00
this - > ClientSizeChanged ( this - > width , this - > height , true ) ;
2021-04-11 14:28:29 +02:00
/* We should have a valid screen buffer now. If not, something went wrong and we should abort. */
if ( _screen . dst_ptr = = nullptr ) {
this - > Stop ( ) ;
_cur_resolution = old_res ;
return " Can't get pointer to screen buffer " ;
}
2021-04-25 00:43:38 +02:00
/* Main loop expects to start with the buffer unmapped. */
this - > ReleaseVideoPointer ( ) ;
2021-01-16 16:43:04 +01:00
MarkWholeScreenDirty ( ) ;
2021-02-24 15:22:23 +01:00
this - > is_game_threaded = ! GetDriverParamBool ( param , " no_threads " ) & & ! GetDriverParamBool ( param , " no_thread " ) ;
2021-01-16 16:43:04 +01:00
return nullptr ;
}
void VideoDriver_Win32OpenGL : : Stop ( )
{
this - > DestroyContext ( ) ;
this - > VideoDriver_Win32Base : : Stop ( ) ;
}
void VideoDriver_Win32OpenGL : : DestroyContext ( )
{
OpenGLBackend : : Destroy ( ) ;
wglMakeCurrent ( nullptr , nullptr ) ;
if ( this - > gl_rc ! = nullptr ) {
wglDeleteContext ( this - > gl_rc ) ;
this - > gl_rc = nullptr ;
}
if ( this - > dc ! = nullptr ) {
ReleaseDC ( this - > main_wnd , this - > dc ) ;
this - > dc = nullptr ;
}
}
2021-04-10 14:53:26 +02:00
void VideoDriver_Win32OpenGL : : ToggleVsync ( bool vsync )
{
if ( _wglSwapIntervalEXT ! = nullptr ) {
_wglSwapIntervalEXT ( vsync ) ;
} else if ( vsync ) {
2021-06-12 09:10:17 +02:00
Debug ( driver , 0 , " OpenGL: Vsync requested, but not supported by driver " ) ;
2021-04-10 14:53:26 +02:00
}
}
2021-01-16 16:43:04 +01:00
const char * VideoDriver_Win32OpenGL : : AllocateContext ( )
{
this - > dc = GetDC ( this - > main_wnd ) ;
2023-09-16 23:27:16 +02:00
const char * err = SelectPixelFormat ( this - > dc ) ;
2021-01-16 16:43:17 +01:00
if ( err ! = nullptr ) return err ;
HGLRC rc = nullptr ;
/* Create OpenGL device context. Try to get an 3.2+ context if possible. */
if ( _wglCreateContextAttribsARB ! = nullptr ) {
2021-04-21 22:57:09 +02:00
/* Try for OpenGL 4.5 first. */
2021-01-16 16:43:17 +01:00
int attribs [ ] = {
2021-04-21 22:57:09 +02:00
WGL_CONTEXT_MAJOR_VERSION_ARB , 4 ,
WGL_CONTEXT_MINOR_VERSION_ARB , 5 ,
2021-01-16 16:43:17 +01:00
WGL_CONTEXT_FLAGS_ARB , _debug_driver_level > = 8 ? WGL_CONTEXT_DEBUG_BIT_ARB : 0 ,
2021-01-16 16:43:26 +01:00
_hasWGLARBCreateContextProfile ? WGL_CONTEXT_PROFILE_MASK_ARB : 0 , WGL_CONTEXT_CORE_PROFILE_BIT_ARB , // Terminate list if WGL_ARB_create_context_profile isn't supported.
2021-01-16 16:43:17 +01:00
0
} ;
rc = _wglCreateContextAttribsARB ( this - > dc , nullptr , attribs ) ;
2021-04-21 22:57:09 +02:00
if ( rc = = nullptr ) {
/* Try again for a 3.2 context. */
attribs [ 1 ] = 3 ;
attribs [ 3 ] = 2 ;
rc = _wglCreateContextAttribsARB ( this - > dc , nullptr , attribs ) ;
}
2021-01-16 16:43:17 +01:00
}
2021-01-16 16:43:04 +01:00
2021-01-16 16:43:17 +01:00
if ( rc = = nullptr ) {
/* Old OpenGL or old driver, let's hope for the best. */
rc = wglCreateContext ( this - > dc ) ;
if ( rc = = nullptr ) return " Can't create OpenGL context " ;
}
if ( ! wglMakeCurrent ( this - > dc , rc ) ) return " Can't active GL context " ;
2021-01-16 16:43:04 +01:00
2021-04-10 14:53:26 +02:00
this - > ToggleVsync ( _video_vsync ) ;
2021-01-16 16:43:18 +01:00
2021-01-16 16:43:17 +01:00
this - > gl_rc = rc ;
2021-04-21 22:06:04 +02:00
return OpenGLBackend : : Create ( & GetOGLProcAddressCallback , this - > GetScreenSize ( ) ) ;
2021-01-16 16:43:04 +01:00
}
bool VideoDriver_Win32OpenGL : : ToggleFullscreen ( bool full_screen )
{
2021-01-16 16:43:16 +01:00
if ( _screen . dst_ptr ! = nullptr ) this - > ReleaseVideoPointer ( ) ;
2021-01-16 16:43:04 +01:00
this - > DestroyContext ( ) ;
bool res = this - > VideoDriver_Win32Base : : ToggleFullscreen ( full_screen ) ;
res & = this - > AllocateContext ( ) = = nullptr ;
2021-01-16 16:43:16 +01:00
this - > ClientSizeChanged ( this - > width , this - > height , true ) ;
2021-01-16 16:43:04 +01:00
return res ;
}
bool VideoDriver_Win32OpenGL : : AfterBlitterChange ( )
{
assert ( BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) ! = 0 ) ;
2021-01-16 16:43:16 +01:00
this - > ClientSizeChanged ( this - > width , this - > height , true ) ;
2021-01-16 16:43:04 +01:00
return true ;
}
2021-02-24 15:22:23 +01:00
void VideoDriver_Win32OpenGL : : PopulateSystemSprites ( )
{
OpenGLBackend : : Get ( ) - > PopulateCursorCache ( ) ;
}
2021-01-16 16:43:38 +01:00
void VideoDriver_Win32OpenGL : : ClearSystemSprites ( )
{
OpenGLBackend : : Get ( ) - > ClearCursorCache ( ) ;
}
2021-01-16 16:43:04 +01:00
bool VideoDriver_Win32OpenGL : : AllocateBackingStore ( int w , int h , bool force )
{
if ( ! force & & w = = _screen . width & & h = = _screen . height ) return false ;
2021-01-16 16:43:12 +01:00
this - > width = w = std : : max ( w , 64 ) ;
this - > height = h = std : : max ( h , 64 ) ;
2021-01-16 16:43:04 +01:00
if ( this - > gl_rc = = nullptr ) return false ;
2021-01-16 16:43:16 +01:00
if ( _screen . dst_ptr ! = nullptr ) this - > ReleaseVideoPointer ( ) ;
2021-02-21 00:35:35 +01:00
this - > dirty_rect = { } ;
2021-01-16 16:43:16 +01:00
bool res = OpenGLBackend : : Get ( ) - > Resize ( w , h , force ) ;
2021-03-20 19:43:54 +01:00
SwapBuffers ( this - > dc ) ;
2021-01-16 16:43:16 +01:00
_screen . dst_ptr = this - > GetVideoPointer ( ) ;
return res ;
2021-01-16 16:43:09 +01:00
}
2021-02-21 00:35:35 +01:00
2021-01-16 16:43:09 +01:00
void * VideoDriver_Win32OpenGL : : GetVideoPointer ( )
{
2021-01-16 16:43:42 +01:00
if ( BlitterFactory : : GetCurrentBlitter ( ) - > NeedsAnimationBuffer ( ) ) {
this - > anim_buffer = OpenGLBackend : : Get ( ) - > GetAnimBuffer ( ) ;
}
2021-01-16 16:43:09 +01:00
return OpenGLBackend : : Get ( ) - > GetVideoBuffer ( ) ;
2021-01-16 16:43:04 +01:00
}
2021-01-16 16:43:16 +01:00
void VideoDriver_Win32OpenGL : : ReleaseVideoPointer ( )
{
2021-01-16 16:43:42 +01:00
if ( this - > anim_buffer ! = nullptr ) OpenGLBackend : : Get ( ) - > ReleaseAnimBuffer ( this - > dirty_rect ) ;
2021-01-16 16:43:16 +01:00
OpenGLBackend : : Get ( ) - > ReleaseVideoBuffer ( this - > dirty_rect ) ;
this - > dirty_rect = { } ;
_screen . dst_ptr = nullptr ;
2021-01-16 16:43:42 +01:00
this - > anim_buffer = nullptr ;
2021-01-16 16:43:16 +01:00
}
2021-01-16 16:43:04 +01:00
void VideoDriver_Win32OpenGL : : Paint ( )
{
PerformanceMeasurer framerate ( PFE_VIDEO ) ;
2021-06-17 10:34:43 +02:00
if ( _local_palette . count_dirty ! = 0 ) {
2021-01-16 16:43:04 +01:00
Blitter * blitter = BlitterFactory : : GetCurrentBlitter ( ) ;
2021-01-16 16:43:38 +01:00
/* Always push a changed palette to OpenGL. */
OpenGLBackend : : Get ( ) - > UpdatePalette ( _local_palette . palette , _local_palette . first_dirty , _local_palette . count_dirty ) ;
if ( blitter - > UsePaletteAnimation ( ) = = Blitter : : PALETTE_ANIMATION_BLITTER ) {
blitter - > PaletteAnimate ( _local_palette ) ;
2021-01-16 16:43:04 +01:00
}
2021-01-16 16:43:38 +01:00
2021-06-17 10:34:43 +02:00
_local_palette . count_dirty = 0 ;
2021-01-16 16:43:04 +01:00
}
2021-01-16 16:43:16 +01:00
OpenGLBackend : : Get ( ) - > Paint ( ) ;
2021-04-06 00:22:55 +02:00
OpenGLBackend : : Get ( ) - > DrawMouseCursor ( ) ;
2021-01-16 16:43:38 +01:00
2021-01-16 16:43:04 +01:00
SwapBuffers ( this - > dc ) ;
}
# endif /* WITH_OPENGL */