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
2014-07-15 10:14:49 +03:00
# include <SDL.h>
2011-12-17 21:59:59 +03:00
# include "CIntObject.h"
2011-12-22 16:05:19 +03:00
# include "CCursorHandler.h"
2014-07-13 20:53:37 +03:00
# include "../CGameInfo.h"
2011-12-17 21:59:59 +03:00
# include "../../lib/CThreadHelper.h"
2012-09-29 13:59:43 +03:00
# include "../../lib/CConfigHandler.h"
2013-04-04 17:58:54 +03:00
# include "../CMT.h"
2014-05-23 22:39:10 +03:00
# include "../CPlayerInterface.h"
2017-06-05 17:43:02 +02:00
# include "../battle/CBattleInterface.h"
2011-12-17 21:59:59 +03:00
2012-09-11 17:25:19 +03:00
extern std : : queue < SDL_Event > events ;
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
}
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 ( ) ;
2017-09-13 02:35:58 +02:00
2018-01-05 19:21:07 +02:00
pushSDLEvent ( SDL_USEREVENT , EUserEvent : : INTERFACE_CHANGED ) ;
2011-12-17 21:59:59 +03:00
}
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 ) ;
2018-07-25 00:36:48 +02:00
CCS - > curh - > changeGraphic ( ECursor : : ADVENTURE , 0 ) ;
2011-12-17 21:59:59 +03:00
newInt - > activate ( ) ;
objsToBlit . push_back ( newInt ) ;
totalRedraw ( ) ;
2017-09-13 02:35:58 +02:00
2018-01-05 19:21:07 +02:00
pushSDLEvent ( SDL_USEREVENT , EUserEvent : : INTERFACE_CHANGED ) ;
2011-12-17 21:59:59 +03:00
}
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 ( ) ;
2017-09-13 02:35:58 +02:00
2018-01-05 19:21:07 +02:00
pushSDLEvent ( SDL_USEREVENT , EUserEvent : : INTERFACE_CHANGED ) ;
2011-12-17 21:59:59 +03:00
}
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 ( )
{
2020-01-06 20:46:56 +02:00
# ifdef VCMI_ANDROID
SDL_FillRect ( screen2 , NULL , SDL_MapRGB ( screen2 - > format , 0 , 0 , 0 ) ) ;
# endif
2013-06-29 16:05:48 +03:00
for ( auto & elem : objsToBlit )
elem - > showAll ( screen2 ) ;
2011-12-17 21:59:59 +03:00
blitAt ( screen2 , 0 , 0 , screen ) ;
}
void CGuiHandler : : updateTime ( )
{
2012-06-13 16:04:06 +03:00
int ms = mainFPSmng - > 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 ;
( elem ) - > onTimer ( 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 ) ;
2014-06-18 07:30:23 +03:00
while ( ! events . empty ( ) )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
continueEventHandling = true ;
2018-01-18 05:34:57 +02:00
SDL_Event ev = events . front ( ) ;
current = & ev ;
2016-11-27 16:48:18 +02:00
events . 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.
if ( ( ev . type = = SDL_MOUSEMOTION ) & & ! events . empty ( ) & & ( events . front ( ) . type = = SDL_MOUSEMOTION ) )
continue ;
2017-09-10 23:00:46 +02:00
handleCurrentEvent ( ) ;
2011-12-17 21:59:59 +03:00
}
}
2017-09-10 23:00:46 +02:00
void CGuiHandler : : handleCurrentEvent ( )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
if ( current - > type = = SDL_KEYDOWN | | current - > type = = SDL_KEYUP )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
SDL_KeyboardEvent key = current - > key ;
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 :
//not working yet since CClient::run remain locked after CBattleInterface 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
//translate numpad keys
if ( key . keysym . sym = = SDLK_KP_ENTER )
{
2015-06-21 00:38:05 +02:00
key . keysym . sym = SDLK_RETURN ;
2014-05-23 20:29:43 +03:00
key . keysym . scancode = SDL_SCANCODE_RETURN ;
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
{
2012-03-11 19:29:01 +03:00
if ( ( * i ) - > captureThisEvent ( key ) )
2011-12-17 21:59:59 +03:00
{
keysCaptured = true ;
break ;
}
}
std : : list < CIntObject * > miCopy = keyinterested ;
2017-09-10 23:00:46 +02:00
for ( auto i = miCopy . begin ( ) ; i ! = miCopy . end ( ) & & continueEventHandling ; i + + )
2012-03-11 19:29:01 +03:00
if ( vstd : : contains ( keyinterested , * i ) & & ( ! keysCaptured | | ( * i ) - > captureThisEvent ( key ) ) )
2011-12-17 21:59:59 +03:00
( * * i ) . keyPressed ( key ) ;
}
2017-09-10 23:00:46 +02:00
else if ( current - > type = = SDL_MOUSEMOTION )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
handleMouseMotion ( ) ;
2011-12-17 21:59:59 +03:00
}
2017-09-10 23:00:46 +02:00
else if ( current - > type = = SDL_MOUSEBUTTONDOWN )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +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 :
2017-09-10 23:00:46 +02:00
if ( lastClick = = current - > motion & & ( 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 ;
2017-09-10 23:00:46 +02:00
if ( isItIn ( & ( * i ) - > pos , current - > motion . x , current - > motion . y ) )
2011-12-17 21:59:59 +03:00
{
( * i ) - > onDoubleClick ( ) ;
}
}
}
2017-09-10 23:00:46 +02:00
lastClick = current - > motion ;
2011-12-17 21:59:59 +03:00
lastClickTime = SDL_GetTicks ( ) ;
2017-06-07 20:16:18 +02:00
handleMouseButtonClick ( lclickable , EIntObjMouseBtnType : : LEFT , true ) ;
break ;
case SDL_BUTTON_RIGHT :
handleMouseButtonClick ( rclickable , EIntObjMouseBtnType : : RIGHT , true ) ;
break ;
case SDL_BUTTON_MIDDLE :
handleMouseButtonClick ( mclickable , EIntObjMouseBtnType : : MIDDLE , true ) ;
break ;
default :
break ;
2011-12-17 21:59:59 +03:00
}
2014-05-21 19:04:34 +03:00
}
2017-09-10 23:00:46 +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 ) ;
2017-09-10 23:00:46 +02:00
( * i ) - > wheelScrolled ( current - > wheel . y < 0 , isItIn ( & ( * i ) - > pos , x , y ) ) ;
2015-08-22 04:47:54 +02:00
}
2011-12-17 21:59:59 +03:00
}
2017-09-10 23:00:46 +02:00
else if ( current - > type = = SDL_TEXTINPUT )
2014-05-23 19:46:54 +03:00
{
for ( auto it : textInterested )
{
2017-09-10 23:00:46 +02:00
it - > textInputed ( current - > text ) ;
2014-05-23 19:46:54 +03:00
}
2016-11-27 16:48:18 +02:00
}
2017-09-10 23:00:46 +02:00
else if ( current - > type = = SDL_TEXTEDITING )
2014-05-23 19:46:54 +03:00
{
for ( auto it : textInterested )
{
2017-09-10 23:00:46 +02:00
it - > textEdited ( current - > edit ) ;
2014-05-23 19:46:54 +03:00
}
2016-11-27 16:48:18 +02:00
}
2014-07-03 11:26:15 +03:00
//todo: muiltitouch
2017-09-10 23:00:46 +02:00
else if ( current - > type = = SDL_MOUSEBUTTONUP )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +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 :
handleMouseButtonClick ( lclickable , EIntObjMouseBtnType : : LEFT , false ) ;
break ;
case SDL_BUTTON_RIGHT :
handleMouseButtonClick ( rclickable , EIntObjMouseBtnType : : RIGHT , false ) ;
break ;
case SDL_BUTTON_MIDDLE :
handleMouseButtonClick ( mclickable , EIntObjMouseBtnType : : MIDDLE , false ) ;
break ;
2011-12-17 21:59:59 +03:00
}
}
2017-06-07 20:16:18 +02:00
current = nullptr ;
} //event end
void CGuiHandler : : handleMouseButtonClick ( CIntObjectList & interestedObjs , EIntObjMouseBtnType btn , bool isPressed )
{
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 ) ;
if ( isItIn ( & ( * i ) - > pos , current - > motion . x , current - > motion . y ) )
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
2017-09-10 23:00:46 +02:00
void CGuiHandler : : handleMouseMotion ( )
2011-12-17 21:59:59 +03:00
{
//sending active, hovered hoverable objects hover() call
std : : vector < CIntObject * > hlp ;
2013-06-29 16:05:48 +03:00
for ( auto & elem : hoverable )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
if ( isItIn ( & ( elem ) - > pos , current - > motion . x , current - > motion . y ) )
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
}
}
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
}
2017-09-10 23:00:46 +02:00
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 )
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
{
2017-07-08 23:35:43 +02:00
if ( elem - > strongInterest | | isItInOrLowerBounds ( & elem - > pos , motion . x , motion . y ) ) //checking lower bounds fixes bug #2476
2011-12-17 21:59:59 +03:00
{
2013-06-29 16:05:48 +03:00
( elem ) - > mouseMoved ( motion ) ;
2011-12-17 21:59:59 +03:00
}
}
}
void CGuiHandler : : fakeMouseMove ( )
{
2017-09-10 22:13:19 +02:00
SDL_Event event ;
2014-05-23 20:29:43 +03:00
SDL_MouseMotionEvent sme = { SDL_MOUSEMOTION , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ;
2011-12-17 21:59:59 +03:00
int x , y ;
2015-06-21 00:13:45 +02:00
2011-12-17 21:59:59 +03:00
sme . state = SDL_GetMouseState ( & x , & y ) ;
sme . x = x ;
sme . y = y ;
2017-09-10 22:13:19 +02:00
event . motion = sme ;
SDL_PushEvent ( & event ) ;
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
2015-08-22 15:47:40 +02:00
boost : : this_thread : : sleep ( boost : : posix_time : : milliseconds ( 15 ) ) ;
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
2018-07-25 00:36:48 +02:00
if ( settings [ " general " ] [ " 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
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
}
2015-06-22 20:53:47 +02:00
2016-11-27 16:48:18 +02:00
mainFPSmng - > framerateDelay ( ) ; // holds a constant FPS
2011-12-17 21:59:59 +03:00
}
2014-06-01 18:31:37 +03:00
2011-12-17 21:59:59 +03:00
CGuiHandler : : CGuiHandler ( )
2016-11-27 16:48:18 +02:00
: lastClick ( - 500 , - 500 ) , lastClickTime ( 0 ) , defActionsDef ( 0 ) , captureChildren ( false )
2011-12-17 21:59:59 +03:00
{
2017-09-10 23:00:46 +02:00
continueEventHandling = true ;
2013-06-26 14:18:27 +03:00
curInt = nullptr ;
current = nullptr ;
statusbar = nullptr ;
2011-12-17 21:59:59 +03:00
// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
2011-12-22 16:05:19 +03:00
mainFPSmng = new CFramerateManager ( 48 ) ;
2014-03-07 16:21:09 +03:00
//do not init CFramerateManager here --AVS
2016-11-27 16:48:18 +02: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 ( )
{
delete mainFPSmng ;
2017-08-13 21:15:25 +02:00
delete terminate_cond ;
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
}
void CGuiHandler : : drawFPSCounter ( )
{
const static SDL_Color yellow = { 255 , 255 , 0 , 0 } ;
static SDL_Rect overlay = { 0 , 0 , 64 , 32 } ;
Uint32 black = SDL_MapRGB ( screen - > format , 10 , 10 , 10 ) ;
SDL_FillRect ( screen , & overlay , black ) ;
std : : string fps = boost : : lexical_cast < std : : string > ( mainFPSmng - > fps ) ;
2012-12-19 20:24:53 +03:00
graphics - > fonts [ FONT_BIG ] - > renderTextLeft ( screen , fps , yellow , Point ( 10 , 10 ) ) ;
2011-12-17 21:59:59 +03:00
}
2016-10-22 16:22:00 +02:00
SDL_Keycode CGuiHandler : : arrowToNum ( SDL_Keycode key )
2011-12-17 21:59:59 +03:00
{
2014-05-21 19:04:34 +03:00
switch ( key )
{
case SDLK_DOWN :
return SDLK_KP_2 ;
case SDLK_UP :
return SDLK_KP_8 ;
case SDLK_LEFT :
return SDLK_KP_4 ;
case SDLK_RIGHT :
return SDLK_KP_6 ;
default :
2014-07-03 11:26:15 +03:00
throw std : : runtime_error ( " Wrong key! " ) ;
2016-11-27 16:48:18 +02:00
}
2011-12-17 21:59:59 +03:00
}
2015-06-21 00:38:05 +02:00
SDL_Keycode CGuiHandler : : numToDigit ( SDL_Keycode key )
2011-12-17 21:59:59 +03:00
{
# define REMOVE_KP(keyName) case SDLK_KP_ ## keyName : return SDLK_ ## keyName;
switch ( key )
{
2014-05-23 20:29:43 +03:00
REMOVE_KP ( 0 )
REMOVE_KP ( 1 )
REMOVE_KP ( 2 )
REMOVE_KP ( 3 )
REMOVE_KP ( 4 )
REMOVE_KP ( 5 )
REMOVE_KP ( 6 )
REMOVE_KP ( 7 )
REMOVE_KP ( 8 )
2016-11-27 16:48:18 +02:00
REMOVE_KP ( 9 )
2011-12-17 21:59:59 +03:00
REMOVE_KP ( PERIOD )
2014-05-23 20:29:43 +03:00
REMOVE_KP ( MINUS )
REMOVE_KP ( PLUS )
REMOVE_KP ( EQUALS )
2011-12-17 21:59:59 +03:00
case SDLK_KP_MULTIPLY :
return SDLK_ASTERISK ;
case SDLK_KP_DIVIDE :
return SDLK_SLASH ;
case SDLK_KP_ENTER :
return SDLK_RETURN ;
default :
return SDLK_UNKNOWN ;
}
# undef REMOVE_KP
}
2015-06-21 00:38:05 +02:00
bool CGuiHandler : : isNumKey ( SDL_Keycode key , bool number )
2011-12-17 21:59:59 +03:00
{
2014-05-21 19:04:34 +03:00
if ( number )
2014-05-23 20:29:43 +03:00
return key > = SDLK_KP_1 & & key < = SDLK_KP_0 ;
2014-05-21 19:04:34 +03:00
else
2014-05-23 20:29:43 +03:00
return ( key > = SDLK_KP_1 & & key < = SDLK_KP_0 ) | | key = = SDLK_KP_MINUS | | key = = SDLK_KP_PLUS | | key = = SDLK_KP_EQUALS ;
2011-12-17 21:59:59 +03:00
}
2015-06-21 00:38:05 +02:00
bool CGuiHandler : : isArrowKey ( SDL_Keycode key )
2011-12-17 21:59:59 +03:00
{
2014-05-23 20:29:43 +03:00
return key = = SDLK_UP | | key = = SDLK_DOWN | | key = = SDLK_LEFT | | key = = SDLK_RIGHT ;
2011-12-22 16:05:19 +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
2012-09-11 17:25:19 +03:00
void CGuiHandler : : pushSDLEvent ( int type , int usercode )
{
SDL_Event event ;
event . type = type ;
event . user . code = usercode ; // not necessarily used
SDL_PushEvent ( & event ) ;
}
2011-12-22 16:05:19 +03:00
CFramerateManager : : CFramerateManager ( int rate )
{
this - > rate = rate ;
this - > rateticks = ( 1000.0 / rate ) ;
this - > fps = 0 ;
2015-06-23 00:08:25 +02:00
this - > accumulatedFrames = 0 ;
this - > accumulatedTime = 0 ;
2016-11-27 16:48:18 +02:00
this - > lastticks = 0 ;
this - > timeElapsed = 0 ;
2011-12-22 16:05:19 +03:00
}
void CFramerateManager : : init ( )
{
this - > lastticks = SDL_GetTicks ( ) ;
}
void CFramerateManager : : framerateDelay ( )
{
ui32 currentTicks = SDL_GetTicks ( ) ;
2012-06-13 16:04:06 +03:00
timeElapsed = currentTicks - lastticks ;
2016-11-27 16:48:18 +02:00
2011-12-22 16:05:19 +03:00
// FPS is higher than it should be, then wait some time
2012-06-13 16:04:06 +03:00
if ( timeElapsed < rateticks )
2011-12-22 16:05:19 +03:00
{
2020-10-01 10:38:06 +02:00
SDL_Delay ( ( Uint32 ) ceil ( this - > rateticks ) - timeElapsed ) ;
2011-12-22 16:05:19 +03:00
}
2016-11-27 16:48:18 +02:00
2015-06-23 00:08:25 +02:00
accumulatedTime + = timeElapsed ;
accumulatedFrames + + ;
2011-12-22 16:05:19 +03:00
2015-06-23 00:08:25 +02:00
if ( accumulatedFrames > = 100 )
{
//about 2 second should be passed
2020-10-01 10:38:06 +02:00
fps = static_cast < int > ( ceil ( 1000.0 / ( accumulatedTime / accumulatedFrames ) ) ) ;
2015-06-23 00:08:25 +02:00
accumulatedTime = 0 ;
2016-11-27 16:48:18 +02:00
accumulatedFrames = 0 ;
2017-07-12 21:01:10 +02:00
}
2012-06-13 16:04:06 +03:00
2015-06-23 00:08:25 +02:00
currentTicks = SDL_GetTicks ( ) ;
2013-08-09 20:37:41 +03:00
// recalculate timeElapsed for external calls via getElapsed()
// limit it to 1000 ms to avoid breaking animation in case of huge lag (e.g. triggered breakpoint)
timeElapsed = std : : min < ui32 > ( currentTicks - lastticks , 1000 ) ;
2015-06-23 00:08:25 +02:00
2012-06-13 16:04:06 +03:00
lastticks = SDL_GetTicks ( ) ;
2012-11-20 20:53:45 +03:00
}