2017-07-13 10:26:03 +02:00
/*
* CGuiHandler . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
2011-12-17 21:59:59 +03:00
# include "StdInc.h"
# include "CGuiHandler.h"
2015-06-22 20:53:47 +02:00
# include "../lib/CondSh.h"
2011-12-17 21:59:59 +03:00
# include "CIntObject.h"
2023-01-05 19:34:37 +02:00
# include "CursorHandler.h"
2023-04-27 19:21:06 +02:00
# include "ShortcutHandler.h"
2023-05-12 23:15:48 +02:00
# include "FramerateManager.h"
2014-07-13 20:53:37 +03:00
# include "../CGameInfo.h"
2023-02-02 21:15:13 +02:00
# include "../render/Colors.h"
2023-02-01 20:42:06 +02:00
# include "../renderSDL/SDL_Extensions.h"
2023-05-08 12:22:01 +02:00
# include "../renderSDL/ScreenHandler.h"
2013-04-04 17:58:54 +03:00
# include "../CMT.h"
2014-05-23 22:39:10 +03:00
# include "../CPlayerInterface.h"
2022-12-09 13:38:46 +02:00
# include "../battle/BattleInterface.h"
2011-12-17 21:59:59 +03:00
2023-02-01 20:42:06 +02:00
# include "../../lib/CThreadHelper.h"
# include "../../lib/CConfigHandler.h"
2023-01-30 00:12:43 +02:00
# include <SDL_render.h>
# include <SDL_timer.h>
2023-01-30 19:55:32 +02:00
# include <SDL_events.h>
2023-04-27 19:21:06 +02:00
# include <SDL_keycode.h>
2023-01-30 00:12:43 +02:00
2023-02-02 19:10:29 +02:00
# ifdef VCMI_APPLE
# include <dispatch/dispatch.h>
# endif
# ifdef VCMI_IOS
# include "ios/utils.h"
# endif
2023-04-30 00:38:28 +02:00
CGuiHandler GH ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
extern std : : queue < SDL_Event > SDLEventsQueue ;
2011-12-17 21:59:59 +03:00
extern boost : : mutex eventsM ;
2012-04-08 04:15:18 +03:00
boost : : thread_specific_ptr < bool > inGuiThread ;
2016-10-22 16:22:00 +02:00
SObjectConstruction : : SObjectConstruction ( CIntObject * obj )
2011-12-17 21:59:59 +03:00
: myObj ( obj )
{
GH . createdObj . push_front ( obj ) ;
GH . captureChildren = true ;
}
SObjectConstruction : : ~ SObjectConstruction ( )
{
assert ( GH . createdObj . size ( ) ) ;
assert ( GH . createdObj . front ( ) = = myObj ) ;
GH . createdObj . pop_front ( ) ;
GH . captureChildren = GH . createdObj . size ( ) ;
}
SSetCaptureState : : SSetCaptureState ( bool allow , ui8 actions )
{
previousCapture = GH . captureChildren ;
GH . captureChildren = false ;
prevActions = GH . defActionsDef ;
GH . defActionsDef = actions ;
}
SSetCaptureState : : ~ SSetCaptureState ( )
{
GH . captureChildren = previousCapture ;
GH . defActionsDef = prevActions ;
}
2014-03-07 16:21:09 +03:00
static inline void
2012-10-06 23:35:04 +03:00
processList ( const ui16 mask , const ui16 flag , std : : list < CIntObject * > * lst , std : : function < void ( std : : list < CIntObject * > * ) > cb )
{
if ( mask & flag )
2014-03-07 16:21:09 +03:00
cb ( lst ) ;
2012-10-06 23:35:04 +03:00
}
2014-03-07 16:21:09 +03:00
void CGuiHandler : : processLists ( const ui16 activityFlag , std : : function < void ( std : : list < CIntObject * > * ) > cb )
2012-10-06 23:35:04 +03:00
{
processList ( CIntObject : : LCLICK , activityFlag , & lclickable , cb ) ;
processList ( CIntObject : : RCLICK , activityFlag , & rclickable , cb ) ;
2017-06-07 20:16:18 +02:00
processList ( CIntObject : : MCLICK , activityFlag , & mclickable , cb ) ;
2012-10-06 23:35:04 +03:00
processList ( CIntObject : : HOVER , activityFlag , & hoverable , cb ) ;
processList ( CIntObject : : MOVE , activityFlag , & motioninterested , cb ) ;
processList ( CIntObject : : KEYBOARD , activityFlag , & keyinterested , cb ) ;
processList ( CIntObject : : TIME , activityFlag , & timeinterested , cb ) ;
2014-03-07 16:21:09 +03:00
processList ( CIntObject : : WHEEL , activityFlag , & wheelInterested , cb ) ;
processList ( CIntObject : : DOUBLECLICK , activityFlag , & doubleClickInterested , cb ) ;
2014-05-23 19:46:54 +03:00
processList ( CIntObject : : TEXTINPUT , activityFlag , & textInterested , cb ) ;
2012-10-06 23:35:04 +03:00
}
2023-01-16 12:26:43 +02:00
void CGuiHandler : : init ( )
{
2023-05-08 12:22:01 +02:00
screenHandlerInstance = std : : make_unique < ScreenHandler > ( ) ;
2023-04-28 13:22:03 +02:00
shortcutsHandlerInstance = std : : make_unique < ShortcutHandler > ( ) ;
2023-05-12 23:15:48 +02:00
framerateManagerInstance = std : : make_unique < FramerateManager > ( settings [ " video " ] [ " targetfps " ] . Integer ( ) ) ;
2023-04-30 00:03:50 +02:00
2023-01-16 12:26:43 +02:00
isPointerRelativeMode = settings [ " general " ] [ " userRelativePointer " ] . Bool ( ) ;
pointerSpeedMultiplier = settings [ " general " ] [ " relativePointerSpeedMultiplier " ] . Float ( ) ;
}
2012-10-06 23:35:04 +03:00
void CGuiHandler : : handleElementActivate ( CIntObject * elem , ui16 activityFlag )
{
2012-10-07 13:38:35 +03:00
processLists ( activityFlag , [ & ] ( std : : list < CIntObject * > * lst ) {
2014-03-07 16:21:09 +03:00
lst - > push_front ( elem ) ;
2012-10-06 23:35:04 +03:00
} ) ;
elem - > active_m | = activityFlag ;
}
void CGuiHandler : : handleElementDeActivate ( CIntObject * elem , ui16 activityFlag )
{
2012-10-07 13:38:35 +03:00
processLists ( activityFlag , [ & ] ( std : : list < CIntObject * > * lst ) {
2013-06-29 16:05:48 +03:00
auto hlp = std : : find ( lst - > begin ( ) , lst - > end ( ) , elem ) ;
2012-10-06 23:35:04 +03:00
assert ( hlp ! = lst - > end ( ) ) ;
2014-03-07 16:21:09 +03:00
lst - > erase ( hlp ) ;
2012-10-06 23:35:04 +03:00
} ) ;
elem - > active_m & = ~ activityFlag ;
}
2018-07-25 00:36:48 +02:00
void CGuiHandler : : popInt ( std : : shared_ptr < IShowActivatable > top )
2011-12-17 21:59:59 +03:00
{
assert ( listInt . front ( ) = = top ) ;
top - > deactivate ( ) ;
2018-07-25 00:36:48 +02:00
disposed . push_back ( top ) ;
2011-12-17 21:59:59 +03:00
listInt . pop_front ( ) ;
objsToBlit - = top ;
2013-11-03 15:51:25 +03:00
if ( ! listInt . empty ( ) )
2011-12-17 21:59:59 +03:00
listInt . front ( ) - > activate ( ) ;
totalRedraw ( ) ;
}
2018-07-25 00:36:48 +02:00
void CGuiHandler : : pushInt ( std : : shared_ptr < IShowActivatable > newInt )
2011-12-17 21:59:59 +03:00
{
2013-07-22 19:23:23 +03:00
assert ( newInt ) ;
2018-07-25 00:36:48 +02:00
assert ( ! vstd : : contains ( listInt , newInt ) ) ; // do not add same object twice
2013-07-22 19:23:23 +03:00
2011-12-17 21:59:59 +03:00
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
2014-03-07 16:21:09 +03:00
screenBuf = screen2 ;
2011-12-17 21:59:59 +03:00
2013-11-03 15:51:25 +03:00
if ( ! listInt . empty ( ) )
2011-12-17 21:59:59 +03:00
listInt . front ( ) - > deactivate ( ) ;
listInt . push_front ( newInt ) ;
2022-12-18 22:32:07 +02:00
CCS - > curh - > set ( Cursor : : Map : : POINTER ) ;
2011-12-17 21:59:59 +03:00
newInt - > activate ( ) ;
objsToBlit . push_back ( newInt ) ;
totalRedraw ( ) ;
}
2016-10-22 16:22:00 +02:00
void CGuiHandler : : popInts ( int howMany )
2011-12-17 21:59:59 +03:00
{
if ( ! howMany ) return ; //senseless but who knows...
assert ( listInt . size ( ) > = howMany ) ;
listInt . front ( ) - > deactivate ( ) ;
for ( int i = 0 ; i < howMany ; i + + )
{
objsToBlit - = listInt . front ( ) ;
2018-07-25 00:36:48 +02:00
disposed . push_back ( listInt . front ( ) ) ;
2011-12-17 21:59:59 +03:00
listInt . pop_front ( ) ;
}
2013-11-03 15:51:25 +03:00
if ( ! listInt . empty ( ) )
2011-12-17 21:59:59 +03:00
{
listInt . front ( ) - > activate ( ) ;
totalRedraw ( ) ;
}
fakeMouseMove ( ) ;
}
2018-07-25 00:36:48 +02:00
std : : shared_ptr < IShowActivatable > CGuiHandler : : topInt ( )
2011-12-17 21:59:59 +03:00
{
2013-11-03 15:51:25 +03:00
if ( listInt . empty ( ) )
2018-07-25 00:36:48 +02:00
return std : : shared_ptr < IShowActivatable > ( ) ;
2014-03-07 16:21:09 +03:00
else
2011-12-17 21:59:59 +03:00
return listInt . front ( ) ;
}
void CGuiHandler : : totalRedraw ( )
{
2023-05-01 16:27:49 +02:00
CSDL_Ext : : fillSurface ( screen2 , Colors : : BLACK ) ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : objsToBlit )
elem - > showAll ( screen2 ) ;
2023-01-17 22:01:35 +02:00
CSDL_Ext : : blitAt ( screen2 , 0 , 0 , screen ) ;
2011-12-17 21:59:59 +03:00
}
void CGuiHandler : : updateTime ( )
{
2023-05-13 22:38:57 +02:00
int ms = framerateManager ( ) . getElapsedMilliseconds ( ) ;
2011-12-17 21:59:59 +03:00
std : : list < CIntObject * > hlp = timeinterested ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : hlp )
2011-12-17 21:59:59 +03:00
{
2013-06-29 16:05:48 +03:00
if ( ! vstd : : contains ( timeinterested , elem ) ) continue ;
2023-03-22 23:09:43 +02:00
( elem ) - > tick ( ms ) ;
2011-12-17 21:59:59 +03:00
}
}
void CGuiHandler : : handleEvents ( )
{
2016-11-27 16:48:18 +02:00
//player interface may want special event handling
2014-06-18 13:31:11 +03:00
if ( nullptr ! = LOCPLINT & & LOCPLINT - > capturedAllEvents ( ) )
return ;
2016-11-27 16:48:18 +02:00
boost : : unique_lock < boost : : mutex > lock ( eventsM ) ;
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
while ( ! SDLEventsQueue . empty ( ) )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
continueEventHandling = true ;
2023-01-27 00:27:06 +02:00
SDL_Event currentEvent = SDLEventsQueue . front ( ) ;
2023-02-02 15:49:23 +02:00
if ( currentEvent . type = = SDL_MOUSEMOTION )
{
cursorPosition = Point ( currentEvent . motion . x , currentEvent . motion . y ) ;
mouseButtonsMask = currentEvent . motion . state ;
}
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
SDLEventsQueue . pop ( ) ;
2020-05-03 00:26:30 +02:00
// In a sequence of mouse motion events, skip all but the last one.
// This prevents freezes when every motion event takes longer to handle than interval at which
// the events arrive (like dragging on the minimap in world view, with redraw at every event)
// so that the events would start piling up faster than they can be processed.
2023-01-27 00:27:06 +02:00
if ( ( currentEvent . type = = SDL_MOUSEMOTION ) & & ! SDLEventsQueue . empty ( ) & & ( SDLEventsQueue . front ( ) . type = = SDL_MOUSEMOTION ) )
2020-05-03 00:26:30 +02:00
continue ;
2023-01-27 00:27:06 +02:00
handleCurrentEvent ( currentEvent ) ;
2011-12-17 21:59:59 +03:00
}
}
2023-01-16 12:26:43 +02:00
void CGuiHandler : : convertTouchToMouse ( SDL_Event * current )
2023-01-07 10:15:32 +02:00
{
int rLogicalWidth , rLogicalHeight ;
SDL_RenderGetLogicalSize ( mainRenderer , & rLogicalWidth , & rLogicalHeight ) ;
int adjustedMouseY = ( int ) ( current - > tfinger . y * rLogicalHeight ) ;
int adjustedMouseX = ( int ) ( current - > tfinger . x * rLogicalWidth ) ;
current - > button . x = adjustedMouseX ;
current - > motion . x = adjustedMouseX ;
current - > button . y = adjustedMouseY ;
current - > motion . y = adjustedMouseY ;
}
2023-01-16 12:26:43 +02:00
void CGuiHandler : : fakeMoveCursor ( float dx , float dy )
{
int x , y , w , h ;
SDL_Event event ;
SDL_MouseMotionEvent sme = { SDL_MOUSEMOTION , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
sme . state = SDL_GetMouseState ( & x , & y ) ;
SDL_GetWindowSize ( mainWindow , & w , & h ) ;
2023-01-29 18:21:55 +02:00
sme . x = CCS - > curh - > position ( ) . x + ( int ) ( GH . pointerSpeedMultiplier * w * dx ) ;
sme . y = CCS - > curh - > position ( ) . y + ( int ) ( GH . pointerSpeedMultiplier * h * dy ) ;
2023-01-16 12:26:43 +02:00
vstd : : abetween ( sme . x , 0 , w ) ;
vstd : : abetween ( sme . y , 0 , h ) ;
event . motion = sme ;
SDL_PushEvent ( & event ) ;
}
void CGuiHandler : : fakeMouseMove ( )
{
fakeMoveCursor ( 0 , 0 ) ;
}
2023-02-02 16:15:39 +02:00
void CGuiHandler : : startTextInput ( const Rect & whereInput )
{
# ifdef VCMI_APPLE
dispatch_async ( dispatch_get_main_queue ( ) , ^ {
# endif
// TODO ios: looks like SDL bug actually, try fixing there
auto renderer = SDL_GetRenderer ( mainWindow ) ;
float scaleX , scaleY ;
SDL_Rect viewport ;
SDL_RenderGetScale ( renderer , & scaleX , & scaleY ) ;
SDL_RenderGetViewport ( renderer , & viewport ) ;
# ifdef VCMI_IOS
const auto nativeScale = iOS_utils : : screenScale ( ) ;
scaleX / = nativeScale ;
scaleY / = nativeScale ;
# endif
SDL_Rect rectInScreenCoordinates ;
rectInScreenCoordinates . x = ( viewport . x + whereInput . x ) * scaleX ;
rectInScreenCoordinates . y = ( viewport . y + whereInput . y ) * scaleY ;
rectInScreenCoordinates . w = whereInput . w * scaleX ;
rectInScreenCoordinates . h = whereInput . h * scaleY ;
SDL_SetTextInputRect ( & rectInScreenCoordinates ) ;
if ( SDL_IsTextInputActive ( ) = = SDL_FALSE )
{
SDL_StartTextInput ( ) ;
}
# ifdef VCMI_APPLE
} ) ;
# endif
}
void CGuiHandler : : stopTextInput ( )
{
# ifdef VCMI_APPLE
dispatch_async ( dispatch_get_main_queue ( ) , ^ {
# endif
if ( SDL_IsTextInputActive ( ) = = SDL_TRUE )
{
SDL_StopTextInput ( ) ;
}
# ifdef VCMI_APPLE
} ) ;
# endif
}
2023-01-16 12:26:43 +02:00
void CGuiHandler : : fakeMouseButtonEventRelativeMode ( bool down , bool right )
{
SDL_Event event ;
SDL_MouseButtonEvent sme = { SDL_MOUSEBUTTONDOWN , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
if ( ! down )
{
sme . type = SDL_MOUSEBUTTONUP ;
}
sme . button = right ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT ;
2023-01-29 18:21:55 +02:00
sme . x = CCS - > curh - > position ( ) . x ;
sme . y = CCS - > curh - > position ( ) . y ;
2023-01-16 12:26:43 +02:00
2023-01-18 18:15:04 +02:00
float xScale , yScale ;
int w , h , rLogicalWidth , rLogicalHeight ;
2023-01-16 12:26:43 +02:00
2023-01-18 18:15:04 +02:00
SDL_GetWindowSize ( mainWindow , & w , & h ) ;
SDL_RenderGetLogicalSize ( mainRenderer , & rLogicalWidth , & rLogicalHeight ) ;
SDL_RenderGetScale ( mainRenderer , & xScale , & yScale ) ;
2023-01-16 12:26:43 +02:00
SDL_EventState ( SDL_MOUSEMOTION , SDL_IGNORE ) ;
2023-02-02 16:15:39 +02:00
moveCursorToPosition ( Point (
2023-01-18 18:15:04 +02:00
( int ) ( sme . x * xScale ) + ( w - rLogicalWidth * xScale ) / 2 ,
2023-02-02 16:15:39 +02:00
( int ) ( sme . y * yScale + ( h - rLogicalHeight * yScale ) / 2 ) ) ) ;
2023-01-16 12:26:43 +02:00
SDL_EventState ( SDL_MOUSEMOTION , SDL_ENABLE ) ;
event . button = sme ;
SDL_PushEvent ( & event ) ;
}
2023-01-29 18:21:55 +02:00
void CGuiHandler : : handleCurrentEvent ( SDL_Event & current )
2011-12-17 21:59:59 +03:00
{
2023-01-27 00:27:06 +02:00
if ( current . type = = SDL_KEYDOWN | | current . type = = SDL_KEYUP )
2011-12-17 21:59:59 +03:00
{
2023-01-27 00:27:06 +02:00
SDL_KeyboardEvent key = current . key ;
2023-04-27 19:21:06 +02:00
if ( key . repeat ! = 0 )
return ; // ignore periodic event resends
2023-01-27 00:27:06 +02:00
if ( current . type = = SDL_KEYDOWN & & key . keysym . sym > = SDLK_F1 & & key . keysym . sym < = SDLK_F15 & & settings [ " session " ] [ " spectate " ] . Bool ( ) )
2017-06-05 17:43:02 +02:00
{
//TODO: we need some central place for all interface-independent hotkeys
Settings s = settings . write [ " session " ] ;
switch ( key . keysym . sym )
{
case SDLK_F5 :
if ( settings [ " session " ] [ " spectate-locked-pim " ] . Bool ( ) )
LOCPLINT - > pim - > unlock ( ) ;
else
LOCPLINT - > pim - > lock ( ) ;
s [ " spectate-locked-pim " ] . Bool ( ) = ! settings [ " session " ] [ " spectate-locked-pim " ] . Bool ( ) ;
break ;
case SDLK_F6 :
s [ " spectate-ignore-hero " ] . Bool ( ) = ! settings [ " session " ] [ " spectate-ignore-hero " ] . Bool ( ) ;
break ;
case SDLK_F7 :
s [ " spectate-skip-battle " ] . Bool ( ) = ! settings [ " session " ] [ " spectate-skip-battle " ] . Bool ( ) ;
break ;
case SDLK_F8 :
s [ " spectate-skip-battle-result " ] . Bool ( ) = ! settings [ " session " ] [ " spectate-skip-battle-result " ] . Bool ( ) ;
break ;
case SDLK_F9 :
2022-12-09 13:10:35 +02:00
//not working yet since CClient::run remain locked after BattleInterface removal
2018-07-25 00:36:48 +02:00
// if(LOCPLINT->battleInt)
// {
// GH.popInts(1);
// vstd::clear_pointer(LOCPLINT->battleInt);
// }
2017-06-05 17:43:02 +02:00
break ;
default :
break ;
}
return ;
}
2011-12-17 21:59:59 +03:00
2023-04-28 13:22:03 +02:00
auto shortcutsVector = shortcutsHandler ( ) . translateKeycode ( key . keysym . sym ) ;
2011-12-17 21:59:59 +03:00
bool keysCaptured = false ;
2017-09-10 23:00:46 +02:00
for ( auto i = keyinterested . begin ( ) ; i ! = keyinterested . end ( ) & & continueEventHandling ; i + + )
2011-12-17 21:59:59 +03:00
{
2023-04-27 19:21:06 +02:00
for ( EShortcut shortcut : shortcutsVector )
2011-12-17 21:59:59 +03:00
{
2023-04-27 19:21:06 +02:00
if ( ( * i ) - > captureThisKey ( shortcut ) )
{
keysCaptured = true ;
break ;
}
2011-12-17 21:59:59 +03:00
}
}
std : : list < CIntObject * > miCopy = keyinterested ;
2017-09-10 23:00:46 +02:00
for ( auto i = miCopy . begin ( ) ; i ! = miCopy . end ( ) & & continueEventHandling ; i + + )
2023-04-27 19:21:06 +02:00
{
for ( EShortcut shortcut : shortcutsVector )
2023-02-02 18:02:25 +02:00
{
2023-04-27 19:21:06 +02:00
if ( vstd : : contains ( keyinterested , * i ) & & ( ! keysCaptured | | ( * i ) - > captureThisKey ( shortcut ) ) )
{
if ( key . state = = SDL_PRESSED )
( * * i ) . keyPressed ( shortcut ) ;
if ( key . state = = SDL_RELEASED )
( * * i ) . keyReleased ( shortcut ) ;
}
2023-02-02 18:02:25 +02:00
}
2023-04-27 19:21:06 +02:00
}
2011-12-17 21:59:59 +03:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_MOUSEMOTION )
2011-12-17 21:59:59 +03:00
{
2023-01-27 00:27:06 +02:00
handleMouseMotion ( current ) ;
2011-12-17 21:59:59 +03:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_MOUSEBUTTONDOWN )
2011-12-17 21:59:59 +03:00
{
2023-01-27 00:27:06 +02:00
switch ( current . button . button )
2011-12-17 21:59:59 +03:00
{
2017-06-07 20:16:18 +02:00
case SDL_BUTTON_LEFT :
2023-01-23 14:43:55 +02:00
{
auto doubleClicked = false ;
2023-01-27 00:27:06 +02:00
if ( lastClick = = getCursorPosition ( ) & & ( SDL_GetTicks ( ) - lastClickTime ) < 300 )
2011-12-17 21:59:59 +03:00
{
std : : list < CIntObject * > hlp = doubleClickInterested ;
2017-09-10 23:00:46 +02:00
for ( auto i = hlp . begin ( ) ; i ! = hlp . end ( ) & & continueEventHandling ; i + + )
2011-12-17 21:59:59 +03:00
{
2017-06-07 20:16:18 +02:00
if ( ! vstd : : contains ( doubleClickInterested , * i ) ) continue ;
2023-01-27 00:27:06 +02:00
if ( ( * i ) - > pos . isInside ( current . motion . x , current . motion . y ) )
2011-12-17 21:59:59 +03:00
{
( * i ) - > onDoubleClick ( ) ;
2023-01-23 14:43:55 +02:00
doubleClicked = true ;
2011-12-17 21:59:59 +03:00
}
}
}
2023-01-27 00:27:06 +02:00
lastClick = current . motion ;
2011-12-17 21:59:59 +03:00
lastClickTime = SDL_GetTicks ( ) ;
2023-01-23 14:43:55 +02:00
if ( ! doubleClicked )
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( lclickable , MouseButton : : LEFT , true ) ;
2017-06-07 20:16:18 +02:00
break ;
2023-01-23 14:43:55 +02:00
}
2017-06-07 20:16:18 +02:00
case SDL_BUTTON_RIGHT :
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( rclickable , MouseButton : : RIGHT , true ) ;
2017-06-07 20:16:18 +02:00
break ;
case SDL_BUTTON_MIDDLE :
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( mclickable , MouseButton : : MIDDLE , true ) ;
2017-06-07 20:16:18 +02:00
break ;
default :
break ;
2011-12-17 21:59:59 +03:00
}
2014-05-21 19:04:34 +03:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_MOUSEWHEEL )
2014-05-21 19:04:34 +03:00
{
std : : list < CIntObject * > hlp = wheelInterested ;
2017-09-10 23:00:46 +02:00
for ( auto i = hlp . begin ( ) ; i ! = hlp . end ( ) & & continueEventHandling ; i + + )
2014-05-21 19:04:34 +03:00
{
if ( ! vstd : : contains ( wheelInterested , * i ) ) continue ;
2015-08-22 04:47:54 +02:00
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
int x = 0 , y = 0 ;
SDL_GetMouseState ( & x , & y ) ;
2023-01-27 00:27:06 +02:00
( * i ) - > wheelScrolled ( current . wheel . y < 0 , ( * i ) - > pos . isInside ( x , y ) ) ;
2015-08-22 04:47:54 +02:00
}
2011-12-17 21:59:59 +03:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_TEXTINPUT )
2014-05-23 19:46:54 +03:00
{
for ( auto it : textInterested )
{
2023-02-02 20:16:41 +02:00
it - > textInputed ( current . text . text ) ;
2014-05-23 19:46:54 +03:00
}
2016-11-27 16:48:18 +02:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_TEXTEDITING )
2014-05-23 19:46:54 +03:00
{
for ( auto it : textInterested )
{
2023-02-02 20:16:41 +02:00
it - > textEdited ( current . edit . text ) ;
2014-05-23 19:46:54 +03:00
}
2016-11-27 16:48:18 +02:00
}
2023-01-27 00:27:06 +02:00
else if ( current . type = = SDL_MOUSEBUTTONUP )
2011-12-17 21:59:59 +03:00
{
2023-01-17 12:04:57 +02:00
if ( ! multifinger )
2011-12-17 21:59:59 +03:00
{
2023-01-29 18:21:55 +02:00
switch ( current . button . button )
2023-01-07 10:15:32 +02:00
{
case SDL_BUTTON_LEFT :
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( lclickable , MouseButton : : LEFT , false ) ;
2023-01-07 10:15:32 +02:00
break ;
case SDL_BUTTON_RIGHT :
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( rclickable , MouseButton : : RIGHT , false ) ;
2023-01-07 10:15:32 +02:00
break ;
case SDL_BUTTON_MIDDLE :
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( mclickable , MouseButton : : MIDDLE , false ) ;
2023-01-07 10:15:32 +02:00
break ;
}
}
}
2023-01-29 18:21:55 +02:00
else if ( current . type = = SDL_FINGERMOTION )
2023-01-16 12:26:43 +02:00
{
if ( isPointerRelativeMode )
{
2023-01-29 18:21:55 +02:00
fakeMoveCursor ( current . tfinger . dx , current . tfinger . dy ) ;
2023-01-16 12:26:43 +02:00
}
}
2023-01-29 18:21:55 +02:00
else if ( current . type = = SDL_FINGERDOWN )
2023-01-07 10:15:32 +02:00
{
2023-01-29 18:21:55 +02:00
auto fingerCount = SDL_GetNumTouchFingers ( current . tfinger . touchId ) ;
2023-01-07 10:15:32 +02:00
2023-01-17 12:04:57 +02:00
multifinger = fingerCount > 1 ;
2023-01-07 10:15:32 +02:00
2023-01-16 12:26:43 +02:00
if ( isPointerRelativeMode )
2023-01-07 10:15:32 +02:00
{
2023-01-29 18:21:55 +02:00
if ( current . tfinger . x > 0.5 )
2023-01-16 12:26:43 +02:00
{
2023-01-29 18:21:55 +02:00
bool isRightClick = current . tfinger . y < 0.5 ;
2023-01-16 12:26:43 +02:00
fakeMouseButtonEventRelativeMode ( true , isRightClick ) ;
}
}
2023-01-18 18:15:04 +02:00
# ifndef VCMI_IOS
2023-01-16 12:26:43 +02:00
else if ( fingerCount = = 2 )
{
2023-01-29 18:21:55 +02:00
convertTouchToMouse ( & current ) ;
handleMouseMotion ( current ) ;
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( rclickable , MouseButton : : RIGHT , true ) ;
2023-01-07 10:15:32 +02:00
}
2023-01-18 18:15:04 +02:00
# endif //VCMI_IOS
2023-01-07 10:15:32 +02:00
}
2023-01-29 18:21:55 +02:00
else if ( current . type = = SDL_FINGERUP )
2023-01-07 10:15:32 +02:00
{
2023-01-29 20:30:37 +02:00
# ifndef VCMI_IOS
2023-01-29 18:21:55 +02:00
auto fingerCount = SDL_GetNumTouchFingers ( current . tfinger . touchId ) ;
2023-01-29 20:30:37 +02:00
# endif //VCMI_IOS
2023-01-07 10:15:32 +02:00
2023-01-16 12:26:43 +02:00
if ( isPointerRelativeMode )
2023-01-07 10:15:32 +02:00
{
2023-01-29 18:21:55 +02:00
if ( current . tfinger . x > 0.5 )
2023-01-16 12:26:43 +02:00
{
2023-01-29 18:21:55 +02:00
bool isRightClick = current . tfinger . y < 0.5 ;
2023-01-16 12:26:43 +02:00
fakeMouseButtonEventRelativeMode ( false , isRightClick ) ;
}
}
2023-01-18 18:15:04 +02:00
# ifndef VCMI_IOS
2023-01-16 12:26:43 +02:00
else if ( multifinger )
{
2023-01-29 18:21:55 +02:00
convertTouchToMouse ( & current ) ;
handleMouseMotion ( current ) ;
2023-02-02 16:22:19 +02:00
handleMouseButtonClick ( rclickable , MouseButton : : RIGHT , false ) ;
2023-01-17 12:04:57 +02:00
multifinger = fingerCount ! = 0 ;
2011-12-17 21:59:59 +03:00
}
2023-01-17 12:04:57 +02:00
# endif //VCMI_IOS
2011-12-17 21:59:59 +03:00
}
2017-06-07 20:16:18 +02:00
} //event end
2023-02-02 16:22:19 +02:00
void CGuiHandler : : handleMouseButtonClick ( CIntObjectList & interestedObjs , MouseButton btn , bool isPressed )
2017-06-07 20:16:18 +02:00
{
auto hlp = interestedObjs ;
2017-09-10 23:00:46 +02:00
for ( auto i = hlp . begin ( ) ; i ! = hlp . end ( ) & & continueEventHandling ; i + + )
2011-12-17 21:59:59 +03:00
{
2017-06-07 20:16:18 +02:00
if ( ! vstd : : contains ( interestedObjs , * i ) ) continue ;
auto prev = ( * i ) - > mouseState ( btn ) ;
if ( ! isPressed )
( * i ) - > updateMouseState ( btn , isPressed ) ;
2023-01-27 00:27:06 +02:00
if ( ( * i ) - > pos . isInside ( getCursorPosition ( ) ) )
2011-12-17 21:59:59 +03:00
{
2017-06-07 20:16:18 +02:00
if ( isPressed )
( * i ) - > updateMouseState ( btn , isPressed ) ;
( * i ) - > click ( btn , isPressed , prev ) ;
2011-12-17 21:59:59 +03:00
}
2017-06-07 20:16:18 +02:00
else if ( ! isPressed )
( * i ) - > click ( btn , boost : : logic : : indeterminate , prev ) ;
2011-12-17 21:59:59 +03:00
}
2017-06-07 20:16:18 +02:00
}
2011-12-17 21:59:59 +03:00
2023-01-27 00:27:06 +02:00
void CGuiHandler : : handleMouseMotion ( const SDL_Event & current )
2011-12-17 21:59:59 +03:00
{
//sending active, hovered hoverable objects hover() call
std : : vector < CIntObject * > hlp ;
2023-03-03 15:23:37 +02:00
auto hoverableCopy = hoverable ;
for ( auto & elem : hoverableCopy )
2011-12-17 21:59:59 +03:00
{
2023-01-27 00:27:06 +02:00
if ( elem - > pos . isInside ( getCursorPosition ( ) ) )
2011-12-17 21:59:59 +03:00
{
2013-06-29 16:05:48 +03:00
if ( ! ( elem ) - > hovered )
hlp . push_back ( ( elem ) ) ;
2011-12-17 21:59:59 +03:00
}
2013-06-29 16:05:48 +03:00
else if ( ( elem ) - > hovered )
2011-12-17 21:59:59 +03:00
{
2013-06-29 16:05:48 +03:00
( elem ) - > hover ( false ) ;
( elem ) - > hovered = false ;
2011-12-17 21:59:59 +03:00
}
}
2023-03-03 15:23:37 +02:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : hlp )
2011-12-17 21:59:59 +03:00
{
2013-06-29 16:05:48 +03:00
elem - > hover ( true ) ;
elem - > hovered = true ;
2011-12-17 21:59:59 +03:00
}
2023-02-02 15:49:23 +02:00
// do not send motion events for events outside our window
//if (current.motion.windowID == 0)
handleMoveInterested ( current . motion ) ;
2011-12-17 21:59:59 +03:00
}
void CGuiHandler : : simpleRedraw ( )
{
//update only top interface and draw background
if ( objsToBlit . size ( ) > 1 )
2023-01-17 22:01:35 +02:00
CSDL_Ext : : blitAt ( screen2 , 0 , 0 , screen ) ; //blit background
2017-06-03 07:25:10 +02:00
if ( ! objsToBlit . empty ( ) )
objsToBlit . back ( ) - > show ( screen ) ; //blit active interface/window
2011-12-17 21:59:59 +03:00
}
2016-10-22 16:22:00 +02:00
void CGuiHandler : : handleMoveInterested ( const SDL_MouseMotionEvent & motion )
2014-03-07 16:21:09 +03:00
{
2011-12-17 21:59:59 +03:00
//sending active, MotionInterested objects mouseMoved() call
std : : list < CIntObject * > miCopy = motioninterested ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : miCopy )
2011-12-17 21:59:59 +03:00
{
2023-01-17 22:01:35 +02:00
if ( elem - > strongInterest | | Rect : : createAround ( elem - > pos , 1 ) . isInside ( motion . x , motion . y ) ) //checking bounds including border fixes bug #2476
2011-12-17 21:59:59 +03:00
{
2023-02-02 15:49:23 +02:00
( elem ) - > mouseMoved ( Point ( motion . x , motion . y ) ) ;
2011-12-17 21:59:59 +03:00
}
}
}
2014-06-01 18:31:37 +03:00
void CGuiHandler : : renderFrame ( )
2011-12-17 21:59:59 +03:00
{
2015-08-22 15:47:40 +02:00
2015-06-22 20:53:47 +02:00
// Updating GUI requires locking pim mutex (that protects screen and GUI state).
// During game:
// When ending the game, the pim mutex might be hold by other thread,
2016-11-27 16:48:18 +02:00
// that will notify us about the ending game by setting terminate_cond flag.
//in PreGame terminate_cond stay false
2015-08-22 15:47:40 +02:00
bool acquiredTheLockOnPim = false ; //for tracking whether pim mutex locking succeeded
2017-08-13 16:44:41 +02:00
while ( ! terminate_cond - > get ( ) & & ! ( acquiredTheLockOnPim = CPlayerInterface : : pim - > try_lock ( ) ) ) //try acquiring long until it succeeds or we are told to terminate
2022-12-30 11:56:26 +02:00
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 1 ) ) ;
2015-06-22 20:53:47 +02:00
2015-08-22 15:47:40 +02:00
if ( acquiredTheLockOnPim )
{
2015-06-22 20:53:47 +02:00
// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock.
boost : : unique_lock < boost : : recursive_mutex > un ( * CPlayerInterface : : pim , boost : : adopt_lock ) ;
if ( nullptr ! = curInt )
curInt - > update ( ) ;
2016-11-27 16:48:18 +02:00
2023-03-05 21:06:52 +02:00
if ( settings [ " video " ] [ " showfps " ] . Bool ( ) )
2016-11-27 16:48:18 +02:00
drawFPSCounter ( ) ;
2018-07-25 00:36:48 +02:00
SDL_UpdateTexture ( screenTexture , nullptr , screen - > pixels , screen - > pitch ) ;
2014-07-03 21:05:59 +03:00
2022-06-13 15:11:53 +02:00
SDL_RenderClear ( mainRenderer ) ;
2016-10-28 12:39:16 +02:00
SDL_RenderCopy ( mainRenderer , screenTexture , nullptr , nullptr ) ;
2014-07-03 21:05:59 +03:00
2018-07-25 00:36:48 +02:00
CCS - > curh - > render ( ) ;
2016-11-27 16:48:18 +02:00
SDL_RenderPresent ( mainRenderer ) ;
2018-07-25 00:36:48 +02:00
disposed . clear ( ) ;
2016-11-27 16:48:18 +02:00
}
2022-06-05 15:20:01 +02:00
2023-05-13 22:38:57 +02:00
framerateManager ( ) . framerateDelay ( ) ; // holds a constant FPS
2011-12-17 21:59:59 +03:00
}
CGuiHandler : : CGuiHandler ( )
2023-02-02 15:49:23 +02:00
: lastClick ( - 500 , - 500 )
, lastClickTime ( 0 )
, defActionsDef ( 0 )
, captureChildren ( false )
, multifinger ( false )
, mouseButtonsMask ( 0 )
, continueEventHandling ( true )
, curInt ( nullptr )
, statusbar ( nullptr )
2011-12-17 21:59:59 +03:00
{
2017-08-13 16:44:41 +02:00
terminate_cond = new CondSh < bool > ( false ) ;
2011-12-17 21:59:59 +03:00
}
CGuiHandler : : ~ CGuiHandler ( )
{
2017-08-13 21:15:25 +02:00
delete terminate_cond ;
2011-12-17 21:59:59 +03:00
}
2023-04-28 13:22:03 +02:00
ShortcutHandler & CGuiHandler : : shortcutsHandler ( )
2023-04-27 19:21:06 +02:00
{
2023-05-13 00:12:11 +02:00
assert ( shortcutsHandlerInstance ) ;
2023-04-28 13:22:03 +02:00
return * shortcutsHandlerInstance ;
2023-04-27 19:21:06 +02:00
}
2023-02-02 16:15:39 +02:00
2023-05-12 23:15:48 +02:00
FramerateManager & CGuiHandler : : framerateManager ( )
{
2023-05-13 00:12:11 +02:00
assert ( framerateManagerInstance ) ;
2023-05-12 23:15:48 +02:00
return * framerateManagerInstance ;
}
2023-02-02 16:15:39 +02:00
void CGuiHandler : : moveCursorToPosition ( const Point & position )
{
SDL_WarpMouseInWindow ( mainWindow , position . x , position . y ) ;
}
bool CGuiHandler : : isKeyboardCtrlDown ( ) const
{
2023-04-10 13:34:17 +02:00
# ifdef VCMI_MAC
return SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_LGUI ] | | SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_RGUI ] ;
# else
2023-02-02 16:15:39 +02:00
return SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_LCTRL ] | | SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_RCTRL ] ;
2023-04-10 13:34:17 +02:00
# endif
2023-02-02 16:15:39 +02:00
}
bool CGuiHandler : : isKeyboardAltDown ( ) const
{
return SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_LALT ] | | SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_RALT ] ;
}
bool CGuiHandler : : isKeyboardShiftDown ( ) const
{
return SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_LSHIFT ] | | SDL_GetKeyboardState ( nullptr ) [ SDL_SCANCODE_RSHIFT ] ;
}
2011-12-17 21:59:59 +03:00
void CGuiHandler : : breakEventHandling ( )
{
2017-09-10 23:00:46 +02:00
continueEventHandling = false ;
2011-12-17 21:59:59 +03:00
}
2023-01-27 00:27:06 +02:00
const Point & CGuiHandler : : getCursorPosition ( ) const
{
return cursorPosition ;
}
2023-02-03 18:23:53 +02:00
Point CGuiHandler : : screenDimensions ( ) const
{
2023-02-03 18:55:25 +02:00
return Point ( screen - > w , screen - > h ) ;
2023-02-03 18:23:53 +02:00
}
2023-02-02 15:49:23 +02:00
bool CGuiHandler : : isMouseButtonPressed ( ) const
{
return mouseButtonsMask > 0 ;
}
bool CGuiHandler : : isMouseButtonPressed ( MouseButton button ) const
{
static_assert ( static_cast < uint32_t > ( MouseButton : : LEFT ) = = SDL_BUTTON_LEFT , " mismatch between VCMI and SDL enum! " ) ;
static_assert ( static_cast < uint32_t > ( MouseButton : : MIDDLE ) = = SDL_BUTTON_MIDDLE , " mismatch between VCMI and SDL enum! " ) ;
static_assert ( static_cast < uint32_t > ( MouseButton : : RIGHT ) = = SDL_BUTTON_RIGHT , " mismatch between VCMI and SDL enum! " ) ;
static_assert ( static_cast < uint32_t > ( MouseButton : : EXTRA1 ) = = SDL_BUTTON_X1 , " mismatch between VCMI and SDL enum! " ) ;
static_assert ( static_cast < uint32_t > ( MouseButton : : EXTRA2 ) = = SDL_BUTTON_X2 , " mismatch between VCMI and SDL enum! " ) ;
uint32_t index = static_cast < uint32_t > ( button ) ;
return mouseButtonsMask & SDL_BUTTON ( index ) ;
}
2011-12-17 21:59:59 +03:00
void CGuiHandler : : drawFPSCounter ( )
{
static SDL_Rect overlay = { 0 , 0 , 64 , 32 } ;
2023-01-30 19:55:32 +02:00
uint32_t black = SDL_MapRGB ( screen - > format , 10 , 10 , 10 ) ;
2011-12-17 21:59:59 +03:00
SDL_FillRect ( screen , & overlay , black ) ;
2023-05-13 22:38:57 +02:00
std : : string fps = std : : to_string ( framerateManager ( ) . getFramerate ( ) ) ;
2023-01-30 00:12:43 +02:00
graphics - > fonts [ FONT_BIG ] - > renderTextLeft ( screen , fps , Colors : : YELLOW , Point ( 10 , 10 ) ) ;
2011-12-17 21:59:59 +03:00
}
2012-04-08 04:15:18 +03:00
bool CGuiHandler : : amIGuiThread ( )
{
return inGuiThread . get ( ) & & * inGuiThread ;
}
2011-12-22 16:05:19 +03:00
2023-02-02 18:15:05 +02:00
void CGuiHandler : : pushUserEvent ( EUserEvent usercode )
2023-02-02 18:35:01 +02:00
{
pushUserEvent ( usercode , nullptr ) ;
}
void CGuiHandler : : pushUserEvent ( EUserEvent usercode , void * userdata )
2012-09-11 17:25:19 +03:00
{
SDL_Event event ;
2023-02-02 18:15:05 +02:00
event . type = SDL_USEREVENT ;
event . user . code = static_cast < int32_t > ( usercode ) ;
2023-02-02 18:35:01 +02:00
event . user . data1 = userdata ;
2012-09-11 17:25:19 +03:00
SDL_PushEvent ( & event ) ;
}
2023-05-08 12:22:01 +02:00
IScreenHandler & CGuiHandler : : screenHandler ( )
2023-04-30 00:03:50 +02:00
{
2023-05-08 12:22:01 +02:00
return * screenHandlerInstance ;
2023-04-30 00:03:50 +02:00
}
2023-05-04 21:33:25 +02:00
void CGuiHandler : : onScreenResize ( )
{
for ( auto const & entry : listInt )
{
auto intObject = std : : dynamic_pointer_cast < CIntObject > ( entry ) ;
if ( intObject )
intObject - > onScreenResize ( ) ;
}
totalRedraw ( ) ;
}