2012-11-01 15:19:01 +00:00
// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2015-12-26 19:31:37 +00:00
# include <algorithm>
2020-08-15 18:53:08 +00:00
2020-10-01 11:05:04 +00:00
# include "Common/Data/Color/RGBAUtil.h"
2021-02-22 00:38:02 +00:00
# include "Common/Data/Text/I18n.h"
2020-10-04 08:10:55 +00:00
# include "Common/System/Display.h"
2020-10-04 08:30:18 +00:00
# include "Common/System/System.h"
2020-10-04 21:24:14 +00:00
# include "Common/Render/TextureAtlas.h"
2020-10-03 22:25:21 +00:00
# include "Common/Math/math_util.h"
2020-10-04 18:48:47 +00:00
# include "Common/UI/Context.h"
2013-09-17 04:48:14 +00:00
2020-08-15 18:53:08 +00:00
# include "Common/Log.h"
# include "Common/TimeUtil.h"
# include "Core/Config.h"
# include "Core/Core.h"
# include "Core/System.h"
# include "Core/HLE/sceCtrl.h"
2021-03-04 09:37:35 +00:00
# include "Core/ControlMapper.h"
2020-08-15 18:53:08 +00:00
# include "UI/GamepadEmu.h"
2023-02-08 20:54:15 +00:00
const float TOUCH_SCALE_FACTOR = 1.5f ;
2021-08-17 07:17:37 +00:00
static uint32_t usedPointerMask = 0 ;
2021-09-13 11:22:27 +00:00
static uint32_t analogPointerMask = 0 ;
2021-08-17 07:17:37 +00:00
2023-12-29 16:07:49 +00:00
static float g_gamepadOpacity ;
static double g_lastTouch ;
2023-12-29 16:24:15 +00:00
void GamepadUpdateOpacity ( float force ) {
if ( force > = 0.0f ) {
g_gamepadOpacity = force ;
return ;
}
2023-12-31 00:04:14 +00:00
if ( coreState ! = CORE_RUNNING ) {
g_gamepadOpacity = 0.0f ;
return ;
}
2023-12-29 16:07:49 +00:00
float fadeAfterSeconds = g_Config . iTouchButtonHideSeconds ;
float fadeTransitionSeconds = std : : min ( fadeAfterSeconds , 0.5f ) ;
float opacity = g_Config . iTouchButtonOpacity / 100.0f ;
2013-12-10 22:19:37 +00:00
2023-12-29 16:07:49 +00:00
float multiplier = 1.0f ;
float secondsWithoutTouch = time_now_d ( ) - g_lastTouch ;
if ( secondsWithoutTouch > = fadeAfterSeconds & & fadeAfterSeconds > 0.0f ) {
if ( secondsWithoutTouch > = fadeAfterSeconds + fadeTransitionSeconds ) {
multiplier = 0.0f ;
} else {
float secondsIntoFade = secondsWithoutTouch - fadeAfterSeconds ;
multiplier = 1.0f - ( secondsIntoFade / fadeTransitionSeconds ) ;
}
}
g_gamepadOpacity = opacity * multiplier ;
2016-01-17 18:23:28 +00:00
}
2023-12-29 16:37:34 +00:00
void GamepadTouch ( bool reset ) {
g_lastTouch = reset ? 0.0f : time_now_d ( ) ;
2015-12-20 20:40:47 +00:00
}
2023-12-29 16:24:15 +00:00
float GamepadGetOpacity ( ) {
return g_gamepadOpacity ;
}
2023-12-29 16:07:49 +00:00
static u32 GetButtonColor ( ) {
return g_Config . iTouchButtonStyle ! = 0 ? 0xFFFFFF : 0xc0b080 ;
2015-12-20 20:40:47 +00:00
}
2023-12-29 16:07:49 +00:00
GamepadView : : GamepadView ( const char * key , UI : : LayoutParams * layoutParams ) : UI : : View ( layoutParams ) , key_ ( key ) { }
2021-02-22 00:38:02 +00:00
std : : string GamepadView : : DescribeText ( ) const {
2023-04-05 22:34:50 +00:00
auto co = GetI18NCategory ( I18NCat : : CONTROLS ) ;
2024-01-19 12:44:49 +00:00
return std : : string ( co - > T ( key_ ) ) ;
2021-02-22 00:38:02 +00:00
}
2013-07-20 10:06:06 +00:00
void MultiTouchButton : : GetContentDimensions ( const UIContext & dc , float & w , float & h ) const {
2020-02-29 20:51:14 +00:00
const AtlasImage * image = dc . Draw ( ) - > GetAtlas ( ) - > getImage ( bgImg_ ) ;
if ( image ) {
w = image - > w * scale_ ;
h = image - > h * scale_ ;
} else {
w = 0.0f ;
h = 0.0f ;
}
2013-07-20 10:06:06 +00:00
}
2022-11-27 15:15:16 +00:00
bool MultiTouchButton : : Touch ( const TouchInput & input ) {
bool retval = GamepadView : : Touch ( input ) ;
2013-07-20 10:06:06 +00:00
if ( ( input . flags & TOUCH_DOWN ) & & bounds_ . Contains ( input . x , input . y ) ) {
pointerDownMask_ | = 1 < < input . id ;
2021-08-17 07:17:37 +00:00
usedPointerMask | = 1 < < input . id ;
2013-07-20 10:06:06 +00:00
}
if ( input . flags & TOUCH_MOVE ) {
2021-09-21 08:35:39 +00:00
if ( bounds_ . Contains ( input . x , input . y ) & & ! ( analogPointerMask & ( 1 < < input . id ) ) )
2013-07-20 10:06:06 +00:00
pointerDownMask_ | = 1 < < input . id ;
else
pointerDownMask_ & = ~ ( 1 < < input . id ) ;
}
if ( input . flags & TOUCH_UP ) {
pointerDownMask_ & = ~ ( 1 < < input . id ) ;
2021-08-17 07:17:37 +00:00
usedPointerMask & = ~ ( 1 < < input . id ) ;
2013-07-20 10:06:06 +00:00
}
2015-05-21 08:49:47 +00:00
if ( input . flags & TOUCH_RELEASE_ALL ) {
pointerDownMask_ = 0 ;
2021-08-17 07:17:37 +00:00
usedPointerMask = 0 ;
2015-05-21 08:49:47 +00:00
}
2022-11-27 15:15:16 +00:00
return retval ;
2013-07-20 10:06:06 +00:00
}
void MultiTouchButton : : Draw ( UIContext & dc ) {
2023-12-29 16:24:15 +00:00
float opacity = g_gamepadOpacity ;
2017-01-26 10:10:33 +00:00
if ( opacity < = 0.0f )
return ;
2013-10-10 14:44:12 +00:00
2013-07-20 10:06:06 +00:00
float scale = scale_ ;
if ( IsDown ( ) ) {
2018-09-30 23:48:02 +00:00
if ( g_Config . iTouchButtonStyle = = 2 ) {
opacity * = 1.35f ;
} else {
2023-02-08 20:54:15 +00:00
scale * = TOUCH_SCALE_FACTOR ;
2018-09-30 23:48:02 +00:00
opacity * = 1.15f ;
}
2013-07-20 10:06:06 +00:00
}
2015-05-31 16:25:43 +00:00
2013-12-10 22:19:37 +00:00
uint32_t colorBg = colorAlpha ( GetButtonColor ( ) , opacity ) ;
2018-09-30 23:48:02 +00:00
uint32_t downBg = colorAlpha ( 0xFFFFFF , opacity * 0.5f ) ;
2013-07-20 10:06:06 +00:00
uint32_t color = colorAlpha ( 0xFFFFFF , opacity ) ;
2018-09-30 23:48:02 +00:00
if ( IsDown ( ) & & g_Config . iTouchButtonStyle = = 2 ) {
if ( bgImg_ ! = bgDownImg_ )
dc . Draw ( ) - > DrawImageRotated ( bgDownImg_ , bounds_ . centerX ( ) , bounds_ . centerY ( ) , scale , bgAngle_ * ( M_PI * 2 / 360.0f ) , downBg , flipImageH_ ) ;
}
2018-06-17 06:00:21 +00:00
dc . Draw ( ) - > DrawImageRotated ( bgImg_ , bounds_ . centerX ( ) , bounds_ . centerY ( ) , scale , bgAngle_ * ( M_PI * 2 / 360.0f ) , colorBg , flipImageH_ ) ;
2013-12-11 19:18:54 +00:00
int y = bounds_ . centerY ( ) ;
// Hack round the fact that the center of the rectangular picture the triangle is contained in
// is not at the "weight center" of the triangle.
2020-02-29 20:51:14 +00:00
if ( img_ = = ImageID ( " I_TRIANGLE " ) )
2013-12-11 19:18:54 +00:00
y - = 2.8f * scale ;
dc . Draw ( ) - > DrawImageRotated ( img_ , bounds_ . centerX ( ) , y , scale , angle_ * ( M_PI * 2 / 360.0f ) , color ) ;
2013-07-20 12:05:07 +00:00
}
2022-11-27 15:15:16 +00:00
bool BoolButton : : Touch ( const TouchInput & input ) {
2013-07-20 12:05:07 +00:00
bool lastDown = pointerDownMask_ ! = 0 ;
2022-11-27 15:15:16 +00:00
bool retval = MultiTouchButton : : Touch ( input ) ;
2013-07-20 12:05:07 +00:00
bool down = pointerDownMask_ ! = 0 ;
if ( down ! = lastDown ) {
* value_ = down ;
2018-06-23 18:06:56 +00:00
UI : : EventParams params { this } ;
params . a = down ;
OnChange . Trigger ( params ) ;
2013-07-20 12:05:07 +00:00
}
2022-11-27 15:15:16 +00:00
return retval ;
2013-07-20 10:06:06 +00:00
}
2022-11-27 15:15:16 +00:00
bool PSPButton : : Touch ( const TouchInput & input ) {
2013-07-20 10:06:06 +00:00
bool lastDown = pointerDownMask_ ! = 0 ;
2022-11-27 15:15:16 +00:00
bool retval = MultiTouchButton : : Touch ( input ) ;
2013-07-20 10:06:06 +00:00
bool down = pointerDownMask_ ! = 0 ;
if ( down & & ! lastDown ) {
2013-10-10 14:44:12 +00:00
if ( g_Config . bHapticFeedback ) {
2023-03-21 09:42:23 +00:00
System_Vibrate ( HAPTIC_VIRTUAL_KEY ) ;
2013-10-10 14:44:12 +00:00
}
2023-04-01 06:55:45 +00:00
__CtrlUpdateButtons ( pspButtonBit_ , 0 ) ;
2013-07-20 10:06:06 +00:00
} else if ( lastDown & & ! down ) {
2023-04-01 06:55:45 +00:00
__CtrlUpdateButtons ( 0 , pspButtonBit_ ) ;
2013-07-20 10:06:06 +00:00
}
2022-11-27 15:15:16 +00:00
return retval ;
2013-07-20 10:06:06 +00:00
}
2023-05-08 08:13:16 +00:00
bool CustomButton : : IsDown ( ) {
2021-03-04 09:37:35 +00:00
return ( toggle_ & & on_ ) | | ( ! toggle_ & & pointerDownMask_ ! = 0 ) ;
}
2023-05-08 08:13:16 +00:00
void CustomButton : : GetContentDimensions ( const UIContext & dc , float & w , float & h ) const {
2021-08-04 07:18:23 +00:00
MultiTouchButton : : GetContentDimensions ( dc , w , h ) ;
if ( invertedContextDimension_ ) {
float tmp = w ;
w = h ;
h = tmp ;
}
}
2023-05-08 08:13:16 +00:00
bool CustomButton : : Touch ( const TouchInput & input ) {
2023-03-26 08:48:59 +00:00
using namespace CustomKeyData ;
2015-06-28 05:34:05 +00:00
bool lastDown = pointerDownMask_ ! = 0 ;
2022-11-27 15:15:16 +00:00
bool retval = MultiTouchButton : : Touch ( input ) ;
2015-06-28 05:34:05 +00:00
bool down = pointerDownMask_ ! = 0 ;
2021-03-04 09:37:35 +00:00
if ( down & & ! lastDown ) {
if ( g_Config . bHapticFeedback )
2023-03-21 09:42:23 +00:00
System_Vibrate ( HAPTIC_VIRTUAL_KEY ) ;
2022-06-19 13:18:05 +00:00
if ( ! repeat_ ) {
2023-03-26 08:48:59 +00:00
for ( int i = 0 ; i < ARRAY_SIZE ( customKeyList ) ; i + + ) {
2022-06-19 13:18:05 +00:00
if ( pspButtonBit_ & ( 1ULL < < i ) ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , customKeyList [ i ] . c , ( on_ & & toggle_ ) ? KEY_UP : KEY_DOWN ) ;
2022-06-19 13:18:05 +00:00
}
2021-03-04 09:37:35 +00:00
}
}
2022-06-19 13:18:05 +00:00
on_ = toggle_ ? ! on_ : true ;
2021-03-04 09:37:35 +00:00
} else if ( ! toggle_ & & lastDown & & ! down ) {
2022-06-19 13:18:05 +00:00
if ( ! repeat_ ) {
2023-03-26 08:48:59 +00:00
for ( int i = 0 ; i < ARRAY_SIZE ( customKeyList ) ; i + + ) {
2022-06-19 13:18:05 +00:00
if ( pspButtonBit_ & ( 1ULL < < i ) ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , customKeyList [ i ] . c , KEY_UP ) ;
2022-06-19 13:18:05 +00:00
}
}
}
on_ = false ;
}
2022-11-27 15:15:16 +00:00
return retval ;
2022-06-19 13:18:05 +00:00
}
2023-05-08 08:13:16 +00:00
void CustomButton : : Update ( ) {
2022-06-19 13:18:05 +00:00
MultiTouchButton : : Update ( ) ;
2023-03-26 08:48:59 +00:00
using namespace CustomKeyData ;
2022-06-19 13:18:05 +00:00
if ( repeat_ ) {
// Give the game some time to process the input, frame based so it's faster when fast-forwarding.
static constexpr int DOWN_FRAME = 5 ;
if ( pressedFrames_ = = 2 * DOWN_FRAME ) {
pressedFrames_ = 0 ;
} else if ( pressedFrames_ = = DOWN_FRAME ) {
2023-03-26 08:48:59 +00:00
for ( int i = 0 ; i < ARRAY_SIZE ( customKeyList ) ; i + + ) {
2022-06-19 13:18:05 +00:00
if ( pspButtonBit_ & ( 1ULL < < i ) ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , customKeyList [ i ] . c , KEY_UP ) ;
2022-06-19 13:18:05 +00:00
}
2015-06-28 05:34:05 +00:00
}
2022-06-19 13:18:05 +00:00
} else if ( on_ & & pressedFrames_ = = 0 ) {
2023-03-26 08:48:59 +00:00
for ( int i = 0 ; i < ARRAY_SIZE ( customKeyList ) ; i + + ) {
2022-06-19 13:18:05 +00:00
if ( pspButtonBit_ & ( 1ULL < < i ) ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , customKeyList [ i ] . c , KEY_DOWN ) ;
2022-06-19 13:18:05 +00:00
}
}
pressedFrames_ = 1 ;
2015-06-28 05:34:05 +00:00
}
2022-06-19 13:18:05 +00:00
if ( pressedFrames_ > 0 )
pressedFrames_ + + ;
2015-06-28 05:34:05 +00:00
}
}
2013-07-20 10:06:06 +00:00
bool PSPButton : : IsDown ( ) {
2023-07-23 09:49:25 +00:00
return ( __CtrlPeekButtonsVisual ( ) & pspButtonBit_ ) ! = 0 ;
2013-07-20 10:54:33 +00:00
}
2021-02-22 00:38:02 +00:00
PSPDpad : : PSPDpad ( ImageID arrowIndex , const char * key , ImageID arrowDownIndex , ImageID overlayIndex , float scale , float spacing , UI : : LayoutParams * layoutParams )
: GamepadView ( key , layoutParams ) , arrowIndex_ ( arrowIndex ) , arrowDownIndex_ ( arrowDownIndex ) , overlayIndex_ ( overlayIndex ) ,
2013-12-02 14:15:19 +00:00
scale_ ( scale ) , spacing_ ( spacing ) , dragPointerId_ ( - 1 ) , down_ ( 0 ) {
2013-07-20 10:54:33 +00:00
}
2013-12-02 14:15:19 +00:00
void PSPDpad : : GetContentDimensions ( const UIContext & dc , float & w , float & h ) const {
2021-09-13 20:16:23 +00:00
const AtlasImage * image = dc . Draw ( ) - > GetAtlas ( ) - > getImage ( arrowIndex_ ) ;
if ( image ) {
w = 2.0f * D_pad_Radius * spacing_ + image - > w * scale_ ;
h = w ;
} else {
w = 0.0f ;
h = 0.0f ;
}
2013-07-20 10:54:33 +00:00
}
2022-11-27 15:15:16 +00:00
bool PSPDpad : : Touch ( const TouchInput & input ) {
bool retval = GamepadView : : Touch ( input ) ;
2013-07-20 10:54:33 +00:00
if ( input . flags & TOUCH_DOWN ) {
if ( dragPointerId_ = = - 1 & & bounds_ . Contains ( input . x , input . y ) ) {
dragPointerId_ = input . id ;
2021-08-17 07:17:37 +00:00
usedPointerMask | = 1 < < input . id ;
2013-07-20 10:54:33 +00:00
ProcessTouch ( input . x , input . y , true ) ;
}
}
if ( input . flags & TOUCH_MOVE ) {
2021-09-21 08:35:39 +00:00
if ( dragPointerId_ = = - 1 & & bounds_ . Contains ( input . x , input . y ) & & ! ( analogPointerMask & ( 1 < < input . id ) ) ) {
2021-09-13 11:22:27 +00:00
dragPointerId_ = input . id ;
}
2013-07-20 10:54:33 +00:00
if ( input . id = = dragPointerId_ ) {
ProcessTouch ( input . x , input . y , true ) ;
}
}
if ( input . flags & TOUCH_UP ) {
if ( input . id = = dragPointerId_ ) {
2013-07-20 12:05:07 +00:00
dragPointerId_ = - 1 ;
2021-08-17 07:17:37 +00:00
usedPointerMask & = ~ ( 1 < < input . id ) ;
2013-07-20 10:54:33 +00:00
ProcessTouch ( input . x , input . y , false ) ;
}
}
2022-11-27 15:15:16 +00:00
return retval ;
2013-07-20 10:54:33 +00:00
}
2013-12-02 14:15:19 +00:00
void PSPDpad : : ProcessTouch ( float x , float y , bool down ) {
2021-09-13 20:16:23 +00:00
float stick_size = bounds_ . w ;
2014-07-20 23:22:21 +00:00
float inv_stick_size = 1.0f / stick_size ;
2021-09-13 20:16:23 +00:00
const float deadzone = 0.05f ;
2013-07-20 10:54:33 +00:00
float dx = ( x - bounds_ . centerX ( ) ) * inv_stick_size ;
float dy = ( y - bounds_ . centerY ( ) ) * inv_stick_size ;
2023-08-31 09:03:14 +00:00
float rad = sqrtf ( dx * dx + dy * dy ) ;
2023-08-31 20:36:00 +00:00
if ( ! g_Config . bStickyTouchDPad & & ( rad < deadzone | | fabs ( dx ) > 0.5f | | fabs ( dy ) > 0.5 ) )
2013-07-20 10:54:33 +00:00
down = false ;
int ctrlMask = 0 ;
2021-09-13 20:16:23 +00:00
bool fourWay = g_Config . bDisableDpadDiagonals | | rad < 0.2f ;
2013-07-20 10:54:33 +00:00
if ( down ) {
2013-12-10 17:58:47 +00:00
if ( fourWay ) {
2013-12-02 14:50:09 +00:00
int direction = ( int ) ( floorf ( ( atan2f ( dy , dx ) / ( 2 * M_PI ) * 4 ) + 0.5f ) ) & 3 ;
switch ( direction ) {
2023-08-31 09:03:14 +00:00
case 0 : ctrlMask = CTRL_RIGHT ; break ;
case 1 : ctrlMask = CTRL_DOWN ; break ;
case 2 : ctrlMask = CTRL_LEFT ; break ;
case 3 : ctrlMask = CTRL_UP ; break ;
2013-12-02 14:50:09 +00:00
}
// 4 way pad
} else {
// 8 way pad
int direction = ( int ) ( floorf ( ( atan2f ( dy , dx ) / ( 2 * M_PI ) * 8 ) + 0.5f ) ) & 7 ;
switch ( direction ) {
2023-08-31 09:03:14 +00:00
case 0 : ctrlMask = CTRL_RIGHT ; break ;
case 1 : ctrlMask = CTRL_RIGHT | CTRL_DOWN ; break ;
case 2 : ctrlMask = CTRL_DOWN ; break ;
case 3 : ctrlMask = CTRL_DOWN | CTRL_LEFT ; break ;
case 4 : ctrlMask = CTRL_LEFT ; break ;
case 5 : ctrlMask = CTRL_UP | CTRL_LEFT ; break ;
case 6 : ctrlMask = CTRL_UP ; break ;
case 7 : ctrlMask = CTRL_UP | CTRL_RIGHT ; break ;
2013-12-02 14:50:09 +00:00
}
2013-07-20 10:54:33 +00:00
}
}
2023-08-31 09:03:14 +00:00
int lastDown = down_ ;
2023-09-11 09:10:00 +00:00
int pressed = ctrlMask & ~ lastDown ;
int released = ( ~ ctrlMask ) & lastDown ;
2013-07-20 10:54:33 +00:00
down_ = ctrlMask ;
2023-09-11 09:10:00 +00:00
bool vibrate = false ;
static const int dir [ 4 ] = { CTRL_RIGHT , CTRL_DOWN , CTRL_LEFT , CTRL_UP } ;
for ( int i = 0 ; i < 4 ; i + + ) {
if ( pressed & dir [ i ] ) {
vibrate = true ;
__CtrlUpdateButtons ( dir [ i ] , 0 ) ;
2013-10-10 14:44:12 +00:00
}
2023-09-11 09:10:00 +00:00
if ( released & dir [ i ] ) {
__CtrlUpdateButtons ( 0 , dir [ i ] ) ;
2013-10-10 14:44:12 +00:00
}
2013-07-20 10:54:33 +00:00
}
2023-09-11 09:10:00 +00:00
if ( vibrate & & g_Config . bHapticFeedback ) {
System_Vibrate ( HAPTIC_VIRTUAL_KEY ) ;
}
2013-07-20 10:54:33 +00:00
}
2013-12-02 14:15:19 +00:00
void PSPDpad : : Draw ( UIContext & dc ) {
2023-12-29 16:24:15 +00:00
float opacity = g_gamepadOpacity ;
2017-01-26 10:10:33 +00:00
if ( opacity < = 0.0f )
return ;
2013-07-20 10:54:33 +00:00
static const float xoff [ 4 ] = { 1 , 0 , - 1 , 0 } ;
static const float yoff [ 4 ] = { 0 , 1 , 0 , - 1 } ;
static const int dir [ 4 ] = { CTRL_RIGHT , CTRL_DOWN , CTRL_LEFT , CTRL_UP } ;
int buttons = __CtrlPeekButtons ( ) ;
2013-12-11 19:52:13 +00:00
float r = D_pad_Radius * spacing_ ;
2013-07-20 10:54:33 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
2018-09-30 23:48:02 +00:00
bool isDown = ( buttons & dir [ i ] ) ! = 0 ;
2013-12-11 19:52:13 +00:00
float x = bounds_ . centerX ( ) + xoff [ i ] * r ;
float y = bounds_ . centerY ( ) + yoff [ i ] * r ;
float x2 = bounds_ . centerX ( ) + xoff [ i ] * ( r + 10.f * scale_ ) ;
float y2 = bounds_ . centerY ( ) + yoff [ i ] * ( r + 10.f * scale_ ) ;
2023-02-08 20:54:15 +00:00
float angle = i * ( M_PI / 2.0f ) ;
float imgScale = isDown ? scale_ * TOUCH_SCALE_FACTOR : scale_ ;
2018-09-30 23:48:02 +00:00
float imgOpacity = opacity ;
if ( isDown & & g_Config . iTouchButtonStyle = = 2 ) {
imgScale = scale_ ;
imgOpacity * = 1.35f ;
uint32_t downBg = colorAlpha ( 0x00FFFFFF , imgOpacity * 0.5f ) ;
if ( arrowIndex_ ! = arrowDownIndex_ )
dc . Draw ( ) - > DrawImageRotated ( arrowDownIndex_ , x , y , imgScale , angle + PI , downBg , false ) ;
}
uint32_t colorBg = colorAlpha ( GetButtonColor ( ) , imgOpacity ) ;
uint32_t color = colorAlpha ( 0xFFFFFF , imgOpacity ) ;
2013-07-20 10:54:33 +00:00
dc . Draw ( ) - > DrawImageRotated ( arrowIndex_ , x , y , imgScale , angle + PI , colorBg , false ) ;
2020-02-29 20:51:14 +00:00
if ( overlayIndex_ . isValid ( ) )
2013-12-11 19:52:13 +00:00
dc . Draw ( ) - > DrawImageRotated ( overlayIndex_ , x2 , y2 , imgScale , angle + PI , color ) ;
2013-07-20 10:54:33 +00:00
}
2013-07-20 10:06:06 +00:00
}
2021-02-22 00:38:02 +00:00
PSPStick : : PSPStick ( ImageID bgImg , const char * key , ImageID stickImg , ImageID stickDownImg , int stick , float scale , UI : : LayoutParams * layoutParams )
: GamepadView ( key , layoutParams ) , dragPointerId_ ( - 1 ) , bgImg_ ( bgImg ) , stickImageIndex_ ( stickImg ) , stickDownImg_ ( stickDownImg ) , stick_ ( stick ) , scale_ ( scale ) , centerX_ ( - 1 ) , centerY_ ( - 1 ) {
2013-07-20 12:05:07 +00:00
stick_size_ = 50 ;
}
void PSPStick : : GetContentDimensions ( const UIContext & dc , float & w , float & h ) const {
2020-02-29 20:51:14 +00:00
dc . Draw ( ) - > GetAtlas ( ) - > measureImage ( bgImg_ , & w , & h ) ;
2021-07-11 15:51:10 +00:00
w * = scale_ ;
h * = scale_ ;
2013-07-20 12:05:07 +00:00
}
void PSPStick : : Draw ( UIContext & dc ) {
2023-12-29 16:24:15 +00:00
float opacity = g_gamepadOpacity ;
2017-01-26 10:10:33 +00:00
if ( opacity < = 0.0f )
return ;
2013-07-20 12:05:07 +00:00
2018-09-30 23:48:02 +00:00
if ( dragPointerId_ ! = - 1 & & g_Config . iTouchButtonStyle = = 2 ) {
opacity * = 1.35f ;
}
2013-12-10 22:19:37 +00:00
uint32_t colorBg = colorAlpha ( GetButtonColor ( ) , opacity ) ;
2018-09-30 23:48:02 +00:00
uint32_t downBg = colorAlpha ( 0x00FFFFFF , opacity * 0.5f ) ;
2013-07-20 12:05:07 +00:00
2015-03-05 09:58:25 +00:00
if ( centerX_ < 0.0f ) {
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
}
float stickX = centerX_ ;
float stickY = centerY_ ;
2013-07-20 12:05:07 +00:00
float dx , dy ;
__CtrlPeekAnalog ( stick_ , & dx , & dy ) ;
2021-07-11 15:51:10 +00:00
if ( ! g_Config . bHideStickBackground )
dc . Draw ( ) - > DrawImage ( bgImg_ , stickX , stickY , 1.0f * scale_ , colorBg , ALIGN_CENTER ) ;
float headScale = stick_ ? g_Config . fRightStickHeadScale : g_Config . fLeftStickHeadScale ;
2018-09-30 23:48:02 +00:00
if ( dragPointerId_ ! = - 1 & & g_Config . iTouchButtonStyle = = 2 & & stickDownImg_ ! = stickImageIndex_ )
2021-07-11 15:51:10 +00:00
dc . Draw ( ) - > DrawImage ( stickDownImg_ , stickX + dx * stick_size_ * scale_ , stickY - dy * stick_size_ * scale_ , 1.0f * scale_ * headScale , downBg , ALIGN_CENTER ) ;
dc . Draw ( ) - > DrawImage ( stickImageIndex_ , stickX + dx * stick_size_ * scale_ , stickY - dy * stick_size_ * scale_ , 1.0f * scale_ * headScale , colorBg , ALIGN_CENTER ) ;
2013-07-20 12:05:07 +00:00
}
2022-11-27 15:15:16 +00:00
bool PSPStick : : Touch ( const TouchInput & input ) {
bool retval = GamepadView : : Touch ( input ) ;
2015-05-21 08:49:47 +00:00
if ( input . flags & TOUCH_RELEASE_ALL ) {
dragPointerId_ = - 1 ;
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
2021-07-09 08:41:26 +00:00
__CtrlSetAnalogXY ( stick_ , 0.0f , 0.0f ) ;
2021-08-17 07:17:37 +00:00
usedPointerMask = 0 ;
2021-09-13 11:22:27 +00:00
analogPointerMask = 0 ;
2022-11-27 15:15:16 +00:00
return retval ;
2015-05-21 08:49:47 +00:00
}
2013-07-20 12:05:07 +00:00
if ( input . flags & TOUCH_DOWN ) {
2021-07-11 15:51:10 +00:00
float fac = 0.5f * ( stick_ ? g_Config . fRightStickHeadScale : g_Config . fLeftStickHeadScale ) - 0.5f ;
if ( dragPointerId_ = = - 1 & & bounds_ . Expand ( bounds_ . w * fac , bounds_ . h * fac ) . Contains ( input . x , input . y ) ) {
2015-03-05 09:58:25 +00:00
if ( g_Config . bAutoCenterTouchAnalog ) {
centerX_ = input . x ;
centerY_ = input . y ;
} else {
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
}
2013-07-20 12:05:07 +00:00
dragPointerId_ = input . id ;
2021-08-17 07:17:37 +00:00
usedPointerMask | = 1 < < input . id ;
2021-09-13 11:22:27 +00:00
analogPointerMask | = 1 < < input . id ;
2013-07-20 12:05:07 +00:00
ProcessTouch ( input . x , input . y , true ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2013-07-20 12:05:07 +00:00
}
}
if ( input . flags & TOUCH_MOVE ) {
if ( input . id = = dragPointerId_ ) {
ProcessTouch ( input . x , input . y , true ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2013-07-20 12:05:07 +00:00
}
}
if ( input . flags & TOUCH_UP ) {
if ( input . id = = dragPointerId_ ) {
dragPointerId_ = - 1 ;
2015-03-05 09:58:25 +00:00
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
2021-08-17 07:17:37 +00:00
usedPointerMask & = ~ ( 1 < < input . id ) ;
2021-09-13 11:22:27 +00:00
analogPointerMask & = ~ ( 1 < < input . id ) ;
2013-07-20 12:05:07 +00:00
ProcessTouch ( input . x , input . y , false ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2013-07-20 12:05:07 +00:00
}
}
2022-11-27 15:15:16 +00:00
return retval ;
2013-07-20 12:05:07 +00:00
}
void PSPStick : : ProcessTouch ( float x , float y , bool down ) {
2015-03-05 09:58:25 +00:00
if ( down & & centerX_ > = 0.0f ) {
2013-07-20 12:05:07 +00:00
float inv_stick_size = 1.0f / ( stick_size_ * scale_ ) ;
2015-03-05 09:58:25 +00:00
float dx = ( x - centerX_ ) * inv_stick_size ;
float dy = ( y - centerY_ ) * inv_stick_size ;
2013-07-20 12:05:07 +00:00
// Do not clamp to a circle! The PSP has nearly square range!
// Old code to clamp to a circle
// float len = sqrtf(dx * dx + dy * dy);
// if (len > 1.0f) {
// dx /= len;
// dy /= len;
//}
// Still need to clamp to a square
dx = std : : min ( 1.0f , std : : max ( - 1.0f , dx ) ) ;
dy = std : : min ( 1.0f , std : : max ( - 1.0f , dy ) ) ;
2021-07-09 08:41:26 +00:00
__CtrlSetAnalogXY ( stick_ , dx , - dy ) ;
2013-07-20 12:05:07 +00:00
} else {
2021-07-09 08:41:26 +00:00
__CtrlSetAnalogXY ( stick_ , 0.0f , 0.0f ) ;
2013-07-20 12:05:07 +00:00
}
}
2024-07-16 13:45:33 +00:00
PSPCustomStick : : PSPCustomStick ( ImageID bgImg , const char * key , ImageID stickImg , ImageID stickDownImg , int stick , float scale , UI : : LayoutParams * layoutParams )
: PSPStick ( bgImg , key , stickImg , stickDownImg , stick , scale , layoutParams ) {
2019-11-13 17:55:18 +00:00
}
void PSPCustomStick : : Draw ( UIContext & dc ) {
2023-12-29 16:24:15 +00:00
float opacity = g_gamepadOpacity ;
2019-11-13 17:55:18 +00:00
if ( opacity < = 0.0f )
return ;
if ( dragPointerId_ ! = - 1 & & g_Config . iTouchButtonStyle = = 2 ) {
opacity * = 1.35f ;
}
uint32_t colorBg = colorAlpha ( GetButtonColor ( ) , opacity ) ;
uint32_t downBg = colorAlpha ( 0x00FFFFFF , opacity * 0.5f ) ;
if ( centerX_ < 0.0f ) {
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
}
float stickX = centerX_ ;
float stickY = centerY_ ;
float dx , dy ;
2020-03-05 17:29:35 +00:00
dx = posX_ ;
dy = - posY_ ;
2019-11-13 17:55:18 +00:00
2021-07-11 15:51:10 +00:00
if ( ! g_Config . bHideStickBackground )
dc . Draw ( ) - > DrawImage ( bgImg_ , stickX , stickY , 1.0f * scale_ , colorBg , ALIGN_CENTER ) ;
2019-11-13 17:55:18 +00:00
if ( dragPointerId_ ! = - 1 & & g_Config . iTouchButtonStyle = = 2 & & stickDownImg_ ! = stickImageIndex_ )
2021-07-11 15:51:10 +00:00
dc . Draw ( ) - > DrawImage ( stickDownImg_ , stickX + dx * stick_size_ * scale_ , stickY - dy * stick_size_ * scale_ , 1.0f * scale_ * g_Config . fRightStickHeadScale , downBg , ALIGN_CENTER ) ;
dc . Draw ( ) - > DrawImage ( stickImageIndex_ , stickX + dx * stick_size_ * scale_ , stickY - dy * stick_size_ * scale_ , 1.0f * scale_ * g_Config . fRightStickHeadScale , colorBg , ALIGN_CENTER ) ;
2019-11-13 17:55:18 +00:00
}
2022-11-27 15:15:16 +00:00
bool PSPCustomStick : : Touch ( const TouchInput & input ) {
bool retval = GamepadView : : Touch ( input ) ;
2019-11-13 17:55:18 +00:00
if ( input . flags & TOUCH_RELEASE_ALL ) {
dragPointerId_ = - 1 ;
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
2020-03-05 17:29:35 +00:00
posX_ = 0.0f ;
posY_ = 0.0f ;
2021-08-17 07:17:37 +00:00
usedPointerMask = 0 ;
2021-09-13 11:22:27 +00:00
analogPointerMask = 0 ;
2022-11-27 15:15:16 +00:00
return false ;
2019-11-13 17:55:18 +00:00
}
if ( input . flags & TOUCH_DOWN ) {
2021-07-11 15:51:10 +00:00
float fac = 0.5f * g_Config . fRightStickHeadScale - 0.5f ;
if ( dragPointerId_ = = - 1 & & bounds_ . Expand ( bounds_ . w * fac , bounds_ . h * fac ) . Contains ( input . x , input . y ) ) {
2019-11-13 17:55:18 +00:00
if ( g_Config . bAutoCenterTouchAnalog ) {
centerX_ = input . x ;
centerY_ = input . y ;
} else {
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
}
dragPointerId_ = input . id ;
2021-08-17 07:17:37 +00:00
usedPointerMask | = 1 < < input . id ;
2021-09-13 11:22:27 +00:00
analogPointerMask | = 1 < < input . id ;
2019-11-13 17:55:18 +00:00
ProcessTouch ( input . x , input . y , true ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2019-11-13 17:55:18 +00:00
}
}
if ( input . flags & TOUCH_MOVE ) {
if ( input . id = = dragPointerId_ ) {
ProcessTouch ( input . x , input . y , true ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2019-11-13 17:55:18 +00:00
}
}
if ( input . flags & TOUCH_UP ) {
if ( input . id = = dragPointerId_ ) {
dragPointerId_ = - 1 ;
centerX_ = bounds_ . centerX ( ) ;
centerY_ = bounds_ . centerY ( ) ;
2021-08-17 07:17:37 +00:00
usedPointerMask & = ~ ( 1 < < input . id ) ;
2021-09-13 11:22:27 +00:00
analogPointerMask & = ~ ( 1 < < input . id ) ;
2019-11-13 17:55:18 +00:00
ProcessTouch ( input . x , input . y , false ) ;
2022-11-27 15:15:16 +00:00
retval = true ;
2019-11-13 17:55:18 +00:00
}
}
2022-11-27 16:32:34 +00:00
return retval ;
2019-11-13 17:55:18 +00:00
}
void PSPCustomStick : : ProcessTouch ( float x , float y , bool down ) {
2022-11-27 23:40:55 +00:00
static const int buttons [ ] = { 0 , CTRL_LTRIGGER , CTRL_RTRIGGER , CTRL_SQUARE , CTRL_TRIANGLE , CTRL_CIRCLE , CTRL_CROSS , CTRL_UP , CTRL_DOWN , CTRL_LEFT , CTRL_RIGHT , CTRL_START , CTRL_SELECT } ;
2024-07-16 13:45:33 +00:00
static const int analogs [ ] = { VIRTKEY_AXIS_RIGHT_Y_MAX , VIRTKEY_AXIS_RIGHT_Y_MIN , VIRTKEY_AXIS_RIGHT_X_MIN , VIRTKEY_AXIS_RIGHT_X_MAX , VIRTKEY_AXIS_Y_MAX , VIRTKEY_AXIS_Y_MIN , VIRTKEY_AXIS_X_MIN , VIRTKEY_AXIS_X_MAX } ;
2022-11-27 23:32:56 +00:00
u32 press = 0 ;
u32 release = 0 ;
2019-11-13 17:55:18 +00:00
2022-11-27 23:40:55 +00:00
auto toggle = [ & ] ( int config , bool simpleCheck , bool diagCheck = true ) {
if ( config < = 0 | | ( size_t ) config > = ARRAY_SIZE ( buttons ) )
return ;
if ( simpleCheck & & ( ! g_Config . bRightAnalogDisableDiagonal | | diagCheck ) )
press | = buttons [ config ] ;
else
release | = buttons [ config ] ;
} ;
2019-11-13 17:55:18 +00:00
2024-07-16 13:45:33 +00:00
auto analog = [ & ] ( float dx , float dy ) {
if ( g_Config . bRightAnalogDisableDiagonal ) {
if ( fabs ( dx ) > fabs ( dy ) ) {
dy = 0.0f ;
} else {
dx = 0.0f ;
}
}
auto assign = [ & ] ( int config , float value ) {
if ( config < ARRAY_SIZE ( buttons ) | | config > = ARRAY_SIZE ( buttons ) + ARRAY_SIZE ( analogs ) ) {
return ;
}
switch ( analogs [ config - ARRAY_SIZE ( buttons ) ] ) {
case VIRTKEY_AXIS_Y_MAX :
__CtrlSetAnalogY ( 0 , value ) ;
break ;
case VIRTKEY_AXIS_Y_MIN :
__CtrlSetAnalogY ( 0 , value * - 1.0f ) ;
break ;
case VIRTKEY_AXIS_X_MIN :
__CtrlSetAnalogX ( 0 , value * - 1.0f ) ;
break ;
case VIRTKEY_AXIS_X_MAX :
__CtrlSetAnalogX ( 0 , value ) ;
break ;
case VIRTKEY_AXIS_RIGHT_Y_MAX :
__CtrlSetAnalogY ( 1 , value ) ;
break ;
case VIRTKEY_AXIS_RIGHT_Y_MIN :
__CtrlSetAnalogY ( 1 , value * - 1.0f ) ;
break ;
case VIRTKEY_AXIS_RIGHT_X_MIN :
__CtrlSetAnalogX ( 1 , value * - 1.0f ) ;
break ;
case VIRTKEY_AXIS_RIGHT_X_MAX :
__CtrlSetAnalogX ( 1 , value ) ;
break ;
}
} ;
// if/when we ever get iLeftAnalog settings, check stick_ for the config to use
// let 0.0f through during centering
if ( dy > = 0.0f ) {
// down
assign ( g_Config . iRightAnalogUp , 0.0f ) ;
assign ( g_Config . iRightAnalogDown , dy ) ;
}
if ( dy < = 0.0f ) {
// up
assign ( g_Config . iRightAnalogDown , 0.0f ) ;
assign ( g_Config . iRightAnalogUp , dy * - 1.0f ) ;
}
if ( dx < = 0.0f ) {
// left
assign ( g_Config . iRightAnalogRight , 0.0f ) ;
assign ( g_Config . iRightAnalogLeft , dx * - 1.0f ) ;
}
if ( dx > = 0.0f ) {
// right
assign ( g_Config . iRightAnalogLeft , 0.0f ) ;
assign ( g_Config . iRightAnalogRight , dx ) ;
}
} ;
2019-11-13 17:55:18 +00:00
if ( down & & centerX_ > = 0.0f ) {
float inv_stick_size = 1.0f / ( stick_size_ * scale_ ) ;
float dx = ( x - centerX_ ) * inv_stick_size ;
float dy = ( y - centerY_ ) * inv_stick_size ;
dx = std : : min ( 1.0f , std : : max ( - 1.0f , dx ) ) ;
dy = std : : min ( 1.0f , std : : max ( - 1.0f , dy ) ) ;
2022-11-27 23:40:55 +00:00
toggle ( g_Config . iRightAnalogRight , dx > 0.5f , fabs ( dx ) > fabs ( dy ) ) ;
toggle ( g_Config . iRightAnalogLeft , dx < - 0.5f , fabs ( dx ) > fabs ( dy ) ) ;
toggle ( g_Config . iRightAnalogUp , dy < - 0.5f , fabs ( dx ) < = fabs ( dy ) ) ;
toggle ( g_Config . iRightAnalogDown , dy > 0.5f , fabs ( dx ) < = fabs ( dy ) ) ;
toggle ( g_Config . iRightAnalogPress , true ) ;
2019-11-13 17:55:18 +00:00
2024-07-16 13:45:33 +00:00
analog ( dx , dy ) ;
2020-03-05 17:29:35 +00:00
posX_ = dx ;
posY_ = dy ;
2019-11-13 17:55:18 +00:00
} else {
2022-11-27 23:40:55 +00:00
toggle ( g_Config . iRightAnalogRight , false ) ;
toggle ( g_Config . iRightAnalogLeft , false ) ;
toggle ( g_Config . iRightAnalogUp , false ) ;
toggle ( g_Config . iRightAnalogDown , false ) ;
toggle ( g_Config . iRightAnalogPress , false ) ;
2019-11-13 17:55:18 +00:00
2024-07-16 13:45:33 +00:00
analog ( 0.0f , 0.0f ) ;
2020-03-05 17:29:35 +00:00
posX_ = 0.0f ;
posY_ = 0.0f ;
2019-11-13 17:55:18 +00:00
}
2022-11-27 23:32:56 +00:00
2023-04-01 06:55:45 +00:00
if ( release | | press ) {
__CtrlUpdateButtons ( press , release ) ;
}
2019-11-13 17:55:18 +00:00
}
2014-02-10 14:55:21 +00:00
void InitPadLayout ( float xres , float yres , float globalScale ) {
2013-12-02 14:15:19 +00:00
const float scale = globalScale ;
2018-06-17 05:14:41 +00:00
const int halfW = xres / 2 ;
auto initTouchPos = [ = ] ( ConfigTouchPos & touch , float x , float y ) {
if ( touch . x = = - 1.0f | | touch . y = = - 1.0f ) {
touch . x = x / xres ;
2024-09-26 09:08:07 +00:00
touch . y = std : : max ( y , 20.0f * globalScale ) / yres ;
2018-06-17 05:14:41 +00:00
touch . scale = scale ;
}
} ;
2013-10-09 20:15:56 +00:00
2013-11-29 15:46:47 +00:00
// PSP buttons (triangle, circle, square, cross)---------------------
// space between the PSP buttons (triangle, circle, square and cross)
2013-12-02 14:15:19 +00:00
if ( g_Config . fActionButtonSpacing < 0 ) {
g_Config . fActionButtonSpacing = 1.0f ;
2013-10-09 20:15:56 +00:00
}
2013-11-29 15:46:47 +00:00
// Position of the circle button (the PSP circle button). It is the farthest to the left
2013-12-02 14:15:19 +00:00
float Action_button_spacing = g_Config . fActionButtonSpacing * baseActionButtonSpacing ;
2014-02-10 14:55:21 +00:00
int Action_button_center_X = xres - Action_button_spacing * 2 ;
int Action_button_center_Y = yres - Action_button_spacing * 2 ;
2019-07-18 01:16:46 +00:00
if ( g_Config . touchRightAnalogStick . show ) {
Action_button_center_Y - = 150 * scale ;
}
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchActionButtonCenter , Action_button_center_X , Action_button_center_Y ) ;
2013-10-09 20:15:56 +00:00
2013-11-29 15:46:47 +00:00
//D-PAD (up down left right) (aka PSP cross)----------------------------
2013-10-09 20:15:56 +00:00
//radius to the D-pad
2013-12-02 14:15:19 +00:00
// TODO: Make configurable
2013-10-09 20:15:56 +00:00
2013-12-02 14:15:19 +00:00
int D_pad_X = 2.5 * D_pad_Radius * scale ;
2014-02-10 14:55:21 +00:00
int D_pad_Y = yres - D_pad_Radius * scale ;
2018-06-17 05:14:41 +00:00
if ( g_Config . touchAnalogStick . show ) {
2013-10-09 20:15:56 +00:00
D_pad_Y - = 200 * scale ;
}
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchDpad , D_pad_X , D_pad_Y ) ;
2013-10-09 20:15:56 +00:00
//analog stick-------------------------------------------------------
//keep the analog stick right below the D pad
int analog_stick_X = D_pad_X ;
2014-02-10 14:55:21 +00:00
int analog_stick_Y = yres - 80 * scale ;
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchAnalogStick , analog_stick_X , analog_stick_Y ) ;
2013-10-09 20:15:56 +00:00
2019-07-18 01:16:46 +00:00
//right analog stick-------------------------------------------------
//keep the right analog stick right below the face buttons
int right_analog_stick_X = Action_button_center_X ;
int right_analog_stick_Y = yres - 80 * scale ;
initTouchPos ( g_Config . touchRightAnalogStick , right_analog_stick_X , right_analog_stick_Y ) ;
2013-10-09 20:15:56 +00:00
//select, start, throttle--------------------------------------------
//space between the bottom keys (space between select, start and un-throttle)
2014-07-22 08:37:20 +00:00
float bottom_key_spacing = 100 ;
2023-02-25 12:09:44 +00:00
if ( g_display . dp_xres < 750 ) {
2014-07-22 08:37:20 +00:00
bottom_key_spacing * = 0.8f ;
}
2013-11-29 15:46:47 +00:00
2024-05-26 07:39:44 +00:00
// On IOS, nudge the bottom button up a little to avoid the task switcher.
# if PPSSPP_PLATFORM(IOS)
const float bottom_button_Y = 80.0f ;
# else
const float bottom_button_Y = 60.0f ;
# endif
2018-06-17 05:14:41 +00:00
int start_key_X = halfW + bottom_key_spacing * scale ;
2024-05-26 07:39:44 +00:00
int start_key_Y = yres - bottom_button_Y * scale ;
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchStartKey , start_key_X , start_key_Y ) ;
2013-10-09 20:15:56 +00:00
2018-06-17 05:14:41 +00:00
int select_key_X = halfW ;
2024-05-26 07:39:44 +00:00
int select_key_Y = yres - bottom_button_Y * scale ;
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchSelectKey , select_key_X , select_key_Y ) ;
2013-10-09 20:15:56 +00:00
2021-08-17 14:48:47 +00:00
int fast_forward_key_X = halfW - bottom_key_spacing * scale ;
2024-05-26 07:39:44 +00:00
int fast_forward_key_Y = yres - bottom_button_Y * scale ;
2021-08-17 14:48:47 +00:00
initTouchPos ( g_Config . touchFastForwardKey , fast_forward_key_X , fast_forward_key_Y ) ;
2013-10-09 20:15:56 +00:00
2015-05-12 17:45:14 +00:00
// L and R------------------------------------------------------------
// Put them above the analog stick / above the buttons to the right.
// The corners were very hard to reach..
int l_key_X = 60 * scale ;
2015-05-31 16:25:43 +00:00
int l_key_Y = yres - 380 * scale ;
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchLKey , l_key_X , l_key_Y ) ;
2013-10-09 20:15:56 +00:00
2014-02-10 14:55:21 +00:00
int r_key_X = xres - 60 * scale ;
2015-05-31 16:25:43 +00:00
int r_key_Y = l_key_Y ;
2018-06-17 05:14:41 +00:00
initTouchPos ( g_Config . touchRKey , r_key_X , r_key_Y ) ;
2015-06-28 05:34:05 +00:00
2023-06-20 07:30:38 +00:00
struct { float x ; float y ; } customButtonPositions [ 10 ] = {
{ 1.2f , 0.5f } ,
{ 2.2f , 0.5f } ,
{ 3.2f , 0.5f } ,
{ 1.2f , 0.333f } ,
{ 2.2f , 0.333f } ,
{ - 1.2f , 0.5f } ,
{ - 2.2f , 0.5f } ,
{ - 3.2f , 0.5f } ,
{ - 1.2f , 0.333f } ,
{ - 2.2f , 0.333f } ,
} ;
for ( int i = 0 ; i < Config : : CUSTOM_BUTTON_COUNT ; i + + ) {
float y_offset = ( float ) ( i / 10 ) * 0.08333f ;
int combo_key_X = halfW + bottom_key_spacing * scale * customButtonPositions [ i % 10 ] . x ;
int combo_key_Y = yres * ( y_offset + customButtonPositions [ i % 10 ] . y ) ;
initTouchPos ( g_Config . touchCustom [ i ] , combo_key_X , combo_key_Y ) ;
}
2018-06-17 05:14:41 +00:00
}
2013-10-09 20:15:56 +00:00
2021-08-28 18:06:05 +00:00
UI : : ViewGroup * CreatePadLayout ( float xres , float yres , bool * pause , bool showPauseButton , ControlMapper * controllMapper ) {
2013-07-20 10:06:06 +00:00
using namespace UI ;
AnchorLayout * root = new AnchorLayout ( new LayoutParams ( FILL_PARENT , FILL_PARENT ) ) ;
2018-06-17 05:14:41 +00:00
if ( ! g_Config . bShowTouchControls ) {
return root ;
}
struct ButtonOffset {
float x ;
float y ;
} ;
auto buttonLayoutParams = [ = ] ( const ConfigTouchPos & touch , ButtonOffset off = { 0 , 0 } ) {
return new AnchorLayoutParams ( touch . x * xres + off . x , touch . y * yres + off . y , NONE , NONE , true ) ;
} ;
// Space between the PSP buttons (traingle, circle, square and cross)
const float actionButtonSpacing = g_Config . fActionButtonSpacing * baseActionButtonSpacing ;
// Position of the circle button (the PSP circle button). It is the farthest to the right.
ButtonOffset circleOffset { actionButtonSpacing , 0.0f } ;
ButtonOffset crossOffset { 0.0f , actionButtonSpacing } ;
ButtonOffset triangleOffset { 0.0f , - actionButtonSpacing } ;
ButtonOffset squareOffset { - actionButtonSpacing , 0.0f } ;
2015-06-28 05:34:05 +00:00
2014-02-10 14:55:21 +00:00
const int halfW = xres / 2 ;
2013-07-20 10:54:33 +00:00
2020-02-29 20:51:14 +00:00
const ImageID roundImage = g_Config . iTouchButtonStyle ? ImageID ( " I_ROUND_LINE " ) : ImageID ( " I_ROUND " ) ;
const ImageID rectImage = g_Config . iTouchButtonStyle ? ImageID ( " I_RECT_LINE " ) : ImageID ( " I_RECT " ) ;
const ImageID shoulderImage = g_Config . iTouchButtonStyle ? ImageID ( " I_SHOULDER_LINE " ) : ImageID ( " I_SHOULDER " ) ;
const ImageID stickImage = g_Config . iTouchButtonStyle ? ImageID ( " I_STICK_LINE " ) : ImageID ( " I_STICK " ) ;
const ImageID stickBg = g_Config . iTouchButtonStyle ? ImageID ( " I_STICK_BG_LINE " ) : ImageID ( " I_STICK_BG " ) ;
2013-12-10 22:19:37 +00:00
2021-02-22 00:38:02 +00:00
auto addPSPButton = [ = ] ( int buttonBit , const char * key , ImageID bgImg , ImageID bgDownImg , ImageID img , const ConfigTouchPos & touch , ButtonOffset off = { 0 , 0 } ) - > PSPButton * {
2018-06-17 05:14:41 +00:00
if ( touch . show ) {
2021-02-22 00:38:02 +00:00
return root - > Add ( new PSPButton ( buttonBit , key , bgImg , bgDownImg , img , touch . scale , buttonLayoutParams ( touch , off ) ) ) ;
2017-03-07 09:33:53 +00:00
}
2018-06-17 05:14:41 +00:00
return nullptr ;
} ;
2021-02-22 00:38:02 +00:00
auto addBoolButton = [ = ] ( bool * value , const char * key , ImageID bgImg , ImageID bgDownImg , ImageID img , const ConfigTouchPos & touch ) - > BoolButton * {
2018-06-17 05:14:41 +00:00
if ( touch . show ) {
2021-02-22 00:38:02 +00:00
return root - > Add ( new BoolButton ( value , key , bgImg , bgDownImg , img , touch . scale , buttonLayoutParams ( touch ) ) ) ;
2018-06-17 05:14:41 +00:00
}
return nullptr ;
} ;
2023-05-08 08:13:16 +00:00
auto addCustomButton = [ = ] ( const ConfigCustomButton & cfg , const char * key , const ConfigTouchPos & touch ) - > CustomButton * {
2023-03-26 08:48:59 +00:00
using namespace CustomKeyData ;
2018-06-17 06:00:21 +00:00
if ( touch . show ) {
2024-09-15 10:11:39 +00:00
_dbg_assert_ ( cfg . shape < ARRAY_SIZE ( customKeyShapes ) ) ;
_dbg_assert_ ( cfg . image < ARRAY_SIZE ( customKeyImages ) ) ;
// Note: cfg.shape and cfg.image are bounds-checked elsewhere.
2023-05-08 08:13:16 +00:00
auto aux = root - > Add ( new CustomButton ( cfg . key , key , cfg . toggle , cfg . repeat , controllMapper ,
2023-03-26 08:48:59 +00:00
g_Config . iTouchButtonStyle = = 0 ? customKeyShapes [ cfg . shape ] . i : customKeyShapes [ cfg . shape ] . l , customKeyShapes [ cfg . shape ] . i ,
customKeyImages [ cfg . image ] . i , touch . scale , customKeyShapes [ cfg . shape ] . d , buttonLayoutParams ( touch ) ) ) ;
aux - > SetAngle ( customKeyImages [ cfg . image ] . r , customKeyShapes [ cfg . shape ] . r ) ;
aux - > FlipImageH ( customKeyShapes [ cfg . shape ] . f ) ;
2021-03-04 09:37:35 +00:00
return aux ;
2018-06-17 06:00:21 +00:00
}
return nullptr ;
} ;
2018-06-17 05:14:41 +00:00
2021-08-28 18:06:05 +00:00
if ( showPauseButton ) {
2021-02-22 00:38:02 +00:00
root - > Add ( new BoolButton ( pause , " Pause button " , roundImage , ImageID ( " I_ROUND " ) , ImageID ( " I_ARROW " ) , 1.0f , new AnchorLayoutParams ( halfW , 20 , NONE , NONE , true ) ) ) - > SetAngle ( 90 ) ;
2018-06-17 05:14:41 +00:00
}
// touchActionButtonCenter.show will always be true, since that's the default.
if ( g_Config . bShowTouchCircle )
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_CIRCLE , " Circle button " , roundImage , ImageID ( " I_ROUND " ) , ImageID ( " I_CIRCLE " ) , g_Config . touchActionButtonCenter , circleOffset ) ;
2018-06-17 05:14:41 +00:00
if ( g_Config . bShowTouchCross )
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_CROSS , " Cross button " , roundImage , ImageID ( " I_ROUND " ) , ImageID ( " I_CROSS " ) , g_Config . touchActionButtonCenter , crossOffset ) ;
2018-06-17 05:14:41 +00:00
if ( g_Config . bShowTouchTriangle )
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_TRIANGLE , " Triangle button " , roundImage , ImageID ( " I_ROUND " ) , ImageID ( " I_TRIANGLE " ) , g_Config . touchActionButtonCenter , triangleOffset ) ;
2018-06-17 05:14:41 +00:00
if ( g_Config . bShowTouchSquare )
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_SQUARE , " Square button " , roundImage , ImageID ( " I_ROUND " ) , ImageID ( " I_SQUARE " ) , g_Config . touchActionButtonCenter , squareOffset ) ;
2018-06-17 05:14:41 +00:00
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_START , " Start button " , rectImage , ImageID ( " I_RECT " ) , ImageID ( " I_START " ) , g_Config . touchStartKey ) ;
addPSPButton ( CTRL_SELECT , " Select button " , rectImage , ImageID ( " I_RECT " ) , ImageID ( " I_SELECT " ) , g_Config . touchSelectKey ) ;
2018-06-17 05:14:41 +00:00
2021-08-17 14:48:47 +00:00
BoolButton * fastForward = addBoolButton ( & PSP_CoreParameter ( ) . fastForward , " Fast-forward button " , rectImage , ImageID ( " I_RECT " ) , ImageID ( " I_ARROW " ) , g_Config . touchFastForwardKey ) ;
if ( fastForward ) {
fastForward - > SetAngle ( 180.0f ) ;
fastForward - > OnChange . Add ( [ ] ( UI : : EventParams & e ) {
2018-12-30 20:16:33 +00:00
if ( e . a & & coreState = = CORE_STEPPING ) {
2024-11-01 21:52:47 +00:00
Core_Resume ( ) ;
2018-06-23 18:06:56 +00:00
}
return UI : : EVENT_DONE ;
} ) ;
}
2018-06-17 06:00:21 +00:00
2021-02-22 00:38:02 +00:00
addPSPButton ( CTRL_LTRIGGER , " Left shoulder button " , shoulderImage , ImageID ( " I_SHOULDER " ) , ImageID ( " I_L " ) , g_Config . touchLKey ) ;
PSPButton * rTrigger = addPSPButton ( CTRL_RTRIGGER , " Right shoulder button " , shoulderImage , ImageID ( " I_SHOULDER " ) , ImageID ( " I_R " ) , g_Config . touchRKey ) ;
2018-06-17 05:14:41 +00:00
if ( rTrigger )
rTrigger - > FlipImageH ( true ) ;
2023-12-20 09:35:02 +00:00
if ( g_Config . touchDpad . show ) {
const ImageID dirImage = g_Config . iTouchButtonStyle ? ImageID ( " I_DIR_LINE " ) : ImageID ( " I_DIR " ) ;
2021-02-22 00:38:02 +00:00
root - > Add ( new PSPDpad ( dirImage , " D-pad " , ImageID ( " I_DIR " ) , ImageID ( " I_ARROW " ) , g_Config . touchDpad . scale , g_Config . fDpadSpacing , buttonLayoutParams ( g_Config . touchDpad ) ) ) ;
2023-12-20 09:35:02 +00:00
}
2018-06-17 05:14:41 +00:00
if ( g_Config . touchAnalogStick . show )
2021-02-22 00:38:02 +00:00
root - > Add ( new PSPStick ( stickBg , " Left analog stick " , stickImage , ImageID ( " I_STICK " ) , 0 , g_Config . touchAnalogStick . scale , buttonLayoutParams ( g_Config . touchAnalogStick ) ) ) ;
2018-06-17 05:14:41 +00:00
2019-11-13 17:55:18 +00:00
if ( g_Config . touchRightAnalogStick . show ) {
if ( g_Config . bRightAnalogCustom )
2024-07-16 13:45:33 +00:00
root - > Add ( new PSPCustomStick ( stickBg , " Right analog stick " , stickImage , ImageID ( " I_STICK " ) , 1 , g_Config . touchRightAnalogStick . scale , buttonLayoutParams ( g_Config . touchRightAnalogStick ) ) ) ;
2019-11-13 17:55:18 +00:00
else
2021-02-22 00:38:02 +00:00
root - > Add ( new PSPStick ( stickBg , " Right analog stick " , stickImage , ImageID ( " I_STICK " ) , 1 , g_Config . touchRightAnalogStick . scale , buttonLayoutParams ( g_Config . touchRightAnalogStick ) ) ) ;
2019-11-13 17:55:18 +00:00
}
2019-07-18 01:16:46 +00:00
2024-09-15 10:11:39 +00:00
// Sanitize custom button images, while adding them.
2023-06-20 07:30:38 +00:00
for ( int i = 0 ; i < Config : : CUSTOM_BUTTON_COUNT ; i + + ) {
2024-09-15 10:11:39 +00:00
if ( g_Config . CustomButton [ i ] . shape > = ARRAY_SIZE ( CustomKeyData : : customKeyShapes ) ) {
g_Config . CustomButton [ i ] . shape = 0 ;
}
if ( g_Config . CustomButton [ i ] . image > = ARRAY_SIZE ( CustomKeyData : : customKeyImages ) ) {
g_Config . CustomButton [ i ] . image = 0 ;
}
2023-06-20 07:30:38 +00:00
char temp [ 64 ] ;
snprintf ( temp , sizeof ( temp ) , " Custom %d button " , i + 1 ) ;
addCustomButton ( g_Config . CustomButton [ i ] , temp , g_Config . touchCustom [ i ] ) ;
}
2012-11-01 15:19:01 +00:00
2021-08-17 07:17:37 +00:00
if ( g_Config . bGestureControlEnabled )
2021-08-30 10:26:13 +00:00
root - > Add ( new GestureGamepad ( controllMapper ) ) ;
2021-08-17 07:17:37 +00:00
2013-07-20 12:05:07 +00:00
return root ;
2012-11-01 15:19:01 +00:00
}
2021-08-17 07:17:37 +00:00
2022-11-27 15:15:16 +00:00
bool GestureGamepad : : Touch ( const TouchInput & input ) {
2021-08-17 07:17:37 +00:00
if ( usedPointerMask & ( 1 < < input . id ) ) {
if ( input . id = = dragPointerId_ )
dragPointerId_ = - 1 ;
2022-11-27 15:15:16 +00:00
return false ;
2021-08-17 07:17:37 +00:00
}
if ( input . flags & TOUCH_RELEASE_ALL ) {
dragPointerId_ = - 1 ;
2022-11-27 15:15:16 +00:00
return false ;
2021-08-17 07:17:37 +00:00
}
if ( input . flags & TOUCH_DOWN ) {
if ( dragPointerId_ = = - 1 ) {
dragPointerId_ = input . id ;
lastX_ = input . x ;
lastY_ = input . y ;
2023-09-01 07:32:21 +00:00
downX_ = input . x ;
downY_ = input . y ;
2021-08-17 07:17:37 +00:00
const float now = time_now_d ( ) ;
if ( now - lastTapRelease_ < 0.3f & & ! haveDoubleTapped_ ) {
if ( g_Config . iDoubleTapGesture ! = 0 )
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iDoubleTapGesture - 1 ] , KEY_DOWN ) ;
2021-08-17 07:17:37 +00:00
haveDoubleTapped_ = true ;
}
lastTouchDown_ = now ;
}
}
if ( input . flags & TOUCH_MOVE ) {
if ( input . id = = dragPointerId_ ) {
deltaX_ + = input . x - lastX_ ;
deltaY_ + = input . y - lastY_ ;
lastX_ = input . x ;
lastY_ = input . y ;
2023-09-01 07:32:21 +00:00
if ( g_Config . bAnalogGesture ) {
const float k = g_Config . fAnalogGestureSensibility * 0.02 ;
float dx = ( input . x - downX_ ) * g_display . dpi_scale_x * k ;
float dy = ( input . y - downY_ ) * g_display . dpi_scale_y * k ;
dx = std : : min ( 1.0f , std : : max ( - 1.0f , dx ) ) ;
dy = std : : min ( 1.0f , std : : max ( - 1.0f , dy ) ) ;
__CtrlSetAnalogXY ( 0 , dx , - dy ) ;
}
2021-08-17 07:17:37 +00:00
}
}
if ( input . flags & TOUCH_UP ) {
if ( input . id = = dragPointerId_ ) {
dragPointerId_ = - 1 ;
if ( time_now_d ( ) - lastTouchDown_ < 0.3f )
lastTapRelease_ = time_now_d ( ) ;
if ( haveDoubleTapped_ ) {
if ( g_Config . iDoubleTapGesture ! = 0 )
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iDoubleTapGesture - 1 ] , KEY_UP ) ;
2021-08-17 07:17:37 +00:00
haveDoubleTapped_ = false ;
}
2023-09-01 07:32:21 +00:00
if ( g_Config . bAnalogGesture )
__CtrlSetAnalogXY ( 0 , 0 , 0 ) ;
2021-08-17 07:17:37 +00:00
}
}
2022-11-27 15:15:16 +00:00
return true ;
2021-08-17 07:17:37 +00:00
}
2023-09-01 07:32:21 +00:00
void GestureGamepad : : Draw ( UIContext & dc ) {
float opacity = g_Config . iTouchButtonOpacity / 100.0 ;
if ( opacity < = 0.0f )
return ;
uint32_t colorBg = colorAlpha ( GetButtonColor ( ) , opacity ) ;
if ( g_Config . bAnalogGesture & & dragPointerId_ ! = - 1 ) {
dc . Draw ( ) - > DrawImage ( ImageID ( " I_CIRCLE " ) , downX_ , downY_ , 0.7f , colorBg , ALIGN_CENTER ) ;
}
}
2021-08-17 07:17:37 +00:00
void GestureGamepad : : Update ( ) {
const float th = 1.0f ;
2023-02-25 12:09:44 +00:00
float dx = deltaX_ * g_display . dpi_scale_x * g_Config . fSwipeSensitivity ;
float dy = deltaY_ * g_display . dpi_scale_y * g_Config . fSwipeSensitivity ;
2021-08-17 07:17:37 +00:00
if ( g_Config . iSwipeRight ! = 0 ) {
if ( dx > th ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeRight - 1 ] , KEY_DOWN ) ;
2021-08-17 07:17:37 +00:00
swipeRightReleased_ = false ;
} else if ( ! swipeRightReleased_ ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeRight - 1 ] , KEY_UP ) ;
2021-08-17 07:17:37 +00:00
swipeRightReleased_ = true ;
}
}
if ( g_Config . iSwipeLeft ! = 0 ) {
if ( dx < - th ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeLeft - 1 ] , KEY_DOWN ) ;
2021-08-17 07:17:37 +00:00
swipeLeftReleased_ = false ;
} else if ( ! swipeLeftReleased_ ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeLeft - 1 ] , KEY_UP ) ;
2021-08-17 07:17:37 +00:00
swipeLeftReleased_ = true ;
}
}
if ( g_Config . iSwipeUp ! = 0 ) {
if ( dy < - th ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeUp - 1 ] , KEY_DOWN ) ;
2021-08-17 07:17:37 +00:00
swipeUpReleased_ = false ;
} else if ( ! swipeUpReleased_ ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeUp - 1 ] , KEY_UP ) ;
2021-08-17 07:17:37 +00:00
swipeUpReleased_ = true ;
}
}
if ( g_Config . iSwipeDown ! = 0 ) {
if ( dy > th ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeDown - 1 ] , KEY_DOWN ) ;
2021-08-17 07:17:37 +00:00
swipeDownReleased_ = false ;
} else if ( ! swipeDownReleased_ ) {
2023-03-26 16:04:40 +00:00
controlMapper_ - > PSPKey ( DEVICE_ID_TOUCH , GestureKey : : keyList [ g_Config . iSwipeDown - 1 ] , KEY_UP ) ;
2021-08-17 07:17:37 +00:00
swipeDownReleased_ = true ;
}
}
deltaX_ * = g_Config . fSwipeSmoothing ;
deltaY_ * = g_Config . fSwipeSmoothing ;
}