1 // Copyright (c) 2012- PPSSPP Project.
2 
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6 
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 // GNU General Public License 2.0 for more details.
11 
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14 
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17 
18 #include <algorithm>
19 
20 #include "Common/Data/Color/RGBAUtil.h"
21 #include "Common/Data/Text/I18n.h"
22 #include "Common/System/Display.h"
23 #include "Common/System/System.h"
24 #include "Common/Render/TextureAtlas.h"
25 #include "Common/Math/math_util.h"
26 #include "Common/UI/Context.h"
27 
28 #include "Common/Log.h"
29 #include "Common/TimeUtil.h"
30 #include "Core/Config.h"
31 #include "Core/Core.h"
32 #include "Core/System.h"
33 #include "Core/HLE/sceCtrl.h"
34 #include "Core/ControlMapper.h"
35 #include "UI/GamepadEmu.h"
36 
37 static uint32_t usedPointerMask = 0;
38 static uint32_t analogPointerMask = 0;
39 
GetButtonColor()40 static u32 GetButtonColor() {
41 	return g_Config.iTouchButtonStyle != 0 ? 0xFFFFFF : 0xc0b080;
42 }
43 
GamepadView(const char * key,UI::LayoutParams * layoutParams)44 GamepadView::GamepadView(const char *key, UI::LayoutParams *layoutParams) : UI::View(layoutParams), key_(key) {
45 	lastFrameTime_ = time_now_d();
46 }
47 
Touch(const TouchInput & input)48 void GamepadView::Touch(const TouchInput &input) {
49 	secondsWithoutTouch_ = 0.0f;
50 }
51 
Update()52 void GamepadView::Update() {
53 	const double now = time_now_d();
54 	float delta = now - lastFrameTime_;
55 	if (delta > 0) {
56 		secondsWithoutTouch_ += delta;
57 	}
58 	lastFrameTime_ = now;
59 }
60 
DescribeText() const61 std::string GamepadView::DescribeText() const {
62 	auto co = GetI18NCategory("Controls");
63 	return co->T(key_);
64 }
65 
GetButtonOpacity()66 float GamepadView::GetButtonOpacity() {
67 	if (coreState != CORE_RUNNING) {
68 		return 0.0f;
69 	}
70 
71 	float fadeAfterSeconds = g_Config.iTouchButtonHideSeconds;
72 	float fadeTransitionSeconds = std::min(fadeAfterSeconds, 0.5f);
73 	float opacity = g_Config.iTouchButtonOpacity / 100.0f;
74 
75 	float multiplier = 1.0f;
76 	if (secondsWithoutTouch_ >= fadeAfterSeconds && fadeAfterSeconds > 0.0f) {
77 		if (secondsWithoutTouch_ >= fadeAfterSeconds + fadeTransitionSeconds) {
78 			multiplier = 0.0f;
79 		} else {
80 			float secondsIntoFade = secondsWithoutTouch_ - fadeAfterSeconds;
81 			multiplier = 1.0f - (secondsIntoFade / fadeTransitionSeconds);
82 		}
83 	}
84 
85 	return opacity * multiplier;
86 }
87 
GetContentDimensions(const UIContext & dc,float & w,float & h) const88 void MultiTouchButton::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
89 	const AtlasImage *image = dc.Draw()->GetAtlas()->getImage(bgImg_);
90 	if (image) {
91 		w = image->w * scale_;
92 		h = image->h * scale_;
93 	} else {
94 		w = 0.0f;
95 		h = 0.0f;
96 	}
97 }
98 
Touch(const TouchInput & input)99 void MultiTouchButton::Touch(const TouchInput &input) {
100 	GamepadView::Touch(input);
101 	if ((input.flags & TOUCH_DOWN) && bounds_.Contains(input.x, input.y)) {
102 		pointerDownMask_ |= 1 << input.id;
103 		usedPointerMask |= 1 << input.id;
104 	}
105 	if (input.flags & TOUCH_MOVE) {
106 		if (bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id)))
107 			pointerDownMask_ |= 1 << input.id;
108 		else
109 			pointerDownMask_ &= ~(1 << input.id);
110 	}
111 	if (input.flags & TOUCH_UP) {
112 		pointerDownMask_ &= ~(1 << input.id);
113 		usedPointerMask &= ~(1 << input.id);
114 	}
115 	if (input.flags & TOUCH_RELEASE_ALL) {
116 		pointerDownMask_ = 0;
117 		usedPointerMask = 0;
118 	}
119 }
120 
Draw(UIContext & dc)121 void MultiTouchButton::Draw(UIContext &dc) {
122 	float opacity = GetButtonOpacity();
123 	if (opacity <= 0.0f)
124 		return;
125 
126 	float scale = scale_;
127 	if (IsDown()) {
128 		if (g_Config.iTouchButtonStyle == 2) {
129 			opacity *= 1.35f;
130 		} else {
131 			scale *= 2.0f;
132 			opacity *= 1.15f;
133 		}
134 	}
135 
136 	uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
137 	uint32_t downBg = colorAlpha(0xFFFFFF, opacity * 0.5f);
138 	uint32_t color = colorAlpha(0xFFFFFF, opacity);
139 
140 	if (IsDown() && g_Config.iTouchButtonStyle == 2) {
141 		if (bgImg_ != bgDownImg_)
142 			dc.Draw()->DrawImageRotated(bgDownImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), downBg, flipImageH_);
143 	}
144 
145 	dc.Draw()->DrawImageRotated(bgImg_, bounds_.centerX(), bounds_.centerY(), scale, bgAngle_ * (M_PI * 2 / 360.0f), colorBg, flipImageH_);
146 
147 	int y = bounds_.centerY();
148 	// Hack round the fact that the center of the rectangular picture the triangle is contained in
149 	// is not at the "weight center" of the triangle.
150 	if (img_ == ImageID("I_TRIANGLE"))
151 		y -= 2.8f * scale;
152 	dc.Draw()->DrawImageRotated(img_, bounds_.centerX(), y, scale, angle_ * (M_PI * 2 / 360.0f), color);
153 }
154 
Touch(const TouchInput & input)155 void BoolButton::Touch(const TouchInput &input) {
156 	bool lastDown = pointerDownMask_ != 0;
157 	MultiTouchButton::Touch(input);
158 	bool down = pointerDownMask_ != 0;
159 
160 	if (down != lastDown) {
161 		*value_ = down;
162 		UI::EventParams params{ this };
163 		params.a = down;
164 		OnChange.Trigger(params);
165 	}
166 }
167 
Touch(const TouchInput & input)168 void PSPButton::Touch(const TouchInput &input) {
169 	bool lastDown = pointerDownMask_ != 0;
170 	MultiTouchButton::Touch(input);
171 	bool down = pointerDownMask_ != 0;
172 	if (down && !lastDown) {
173 		if (g_Config.bHapticFeedback) {
174 			Vibrate(HAPTIC_VIRTUAL_KEY);
175 		}
176 		__CtrlButtonDown(pspButtonBit_);
177 	} else if (lastDown && !down) {
178 		__CtrlButtonUp(pspButtonBit_);
179 	}
180 }
181 
IsDown()182 bool ComboKey::IsDown() {
183 	return (toggle_ && on_) || (!toggle_ && pointerDownMask_ != 0);
184 }
185 
GetContentDimensions(const UIContext & dc,float & w,float & h) const186 void ComboKey::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
187 	MultiTouchButton::GetContentDimensions(dc, w, h);
188 	if (invertedContextDimension_) {
189 		float tmp = w;
190 		w = h;
191 		h = tmp;
192 	}
193 }
194 
Touch(const TouchInput & input)195 void ComboKey::Touch(const TouchInput &input) {
196 	using namespace CustomKey;
197 	bool lastDown = pointerDownMask_ != 0;
198 	MultiTouchButton::Touch(input);
199 	bool down = pointerDownMask_ != 0;
200 
201 	if (down && !lastDown) {
202 		if (g_Config.bHapticFeedback)
203 			Vibrate(HAPTIC_VIRTUAL_KEY);
204 		for (int i = 0; i < ARRAY_SIZE(comboKeyList); i++) {
205 			if (pspButtonBit_ & (1ULL << i)) {
206 				controllMapper_->pspKey(comboKeyList[i].c, (on_ && toggle_) ? KEY_UP : KEY_DOWN);
207 			}
208 		}
209 		if (toggle_)
210 			on_ = !on_;
211 	} else if (!toggle_ && lastDown && !down) {
212 		for (int i = 0; i < ARRAY_SIZE(comboKeyList); i++) {
213 			if (pspButtonBit_ & (1ULL << i)) {
214 				controllMapper_->pspKey(comboKeyList[i].c, KEY_UP);
215 			}
216 		}
217 	}
218 }
219 
IsDown()220 bool PSPButton::IsDown() {
221 	return (__CtrlPeekButtons() & pspButtonBit_) != 0;
222 }
223 
PSPDpad(ImageID arrowIndex,const char * key,ImageID arrowDownIndex,ImageID overlayIndex,float scale,float spacing,UI::LayoutParams * layoutParams)224 PSPDpad::PSPDpad(ImageID arrowIndex, const char *key, ImageID arrowDownIndex, ImageID overlayIndex, float scale, float spacing, UI::LayoutParams *layoutParams)
225 	: GamepadView(key, layoutParams), arrowIndex_(arrowIndex), arrowDownIndex_(arrowDownIndex), overlayIndex_(overlayIndex),
226 		scale_(scale), spacing_(spacing), dragPointerId_(-1), down_(0) {
227 }
228 
GetContentDimensions(const UIContext & dc,float & w,float & h) const229 void PSPDpad::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
230 	w = D_pad_Radius * spacing_ * 4;
231 	h = D_pad_Radius * spacing_ * 4;
232 }
233 
Touch(const TouchInput & input)234 void PSPDpad::Touch(const TouchInput &input) {
235 	GamepadView::Touch(input);
236 
237 	if (input.flags & TOUCH_DOWN) {
238 		if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y)) {
239 			dragPointerId_ = input.id;
240 			usedPointerMask |= 1 << input.id;
241 			ProcessTouch(input.x, input.y, true);
242 		}
243 	}
244 	if (input.flags & TOUCH_MOVE) {
245 		if (dragPointerId_ == -1 && bounds_.Contains(input.x, input.y) && !(analogPointerMask & (1 << input.id))) {
246 			dragPointerId_ = input.id;
247 		}
248 		if (input.id == dragPointerId_) {
249 			ProcessTouch(input.x, input.y, true);
250 		}
251 	}
252 	if (input.flags & TOUCH_UP) {
253 		if (input.id == dragPointerId_) {
254 			dragPointerId_ = -1;
255 			usedPointerMask &= ~(1 << input.id);
256 			ProcessTouch(input.x, input.y, false);
257 		}
258 	}
259 }
260 
ProcessTouch(float x,float y,bool down)261 void PSPDpad::ProcessTouch(float x, float y, bool down) {
262 	float stick_size = spacing_ * D_pad_Radius * scale_;
263 	float inv_stick_size = 1.0f / stick_size;
264 	const float deadzone = 0.17f;
265 
266 	float dx = (x - bounds_.centerX()) * inv_stick_size;
267 	float dy = (y - bounds_.centerY()) * inv_stick_size;
268 	float rad = sqrtf(dx*dx + dy*dy);
269 	if (rad < deadzone || rad > 2.0f)
270 		down = false;
271 
272 	int ctrlMask = 0;
273 	int lastDown = down_;
274 
275 	bool fourWay = g_Config.bDisableDpadDiagonals || rad < 0.7f;
276 	if (down) {
277 		if (fourWay) {
278 			int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 4) + 0.5f)) & 3;
279 			switch (direction) {
280 			case 0: ctrlMask |= CTRL_RIGHT; break;
281 			case 1: ctrlMask |= CTRL_DOWN; break;
282 			case 2: ctrlMask |= CTRL_LEFT; break;
283 			case 3: ctrlMask |= CTRL_UP; break;
284 			}
285 			// 4 way pad
286 		} else {
287 			// 8 way pad
288 			int direction = (int)(floorf((atan2f(dy, dx) / (2 * M_PI) * 8) + 0.5f)) & 7;
289 			switch (direction) {
290 			case 0: ctrlMask |= CTRL_RIGHT; break;
291 			case 1: ctrlMask |= CTRL_RIGHT | CTRL_DOWN; break;
292 			case 2: ctrlMask |= CTRL_DOWN; break;
293 			case 3: ctrlMask |= CTRL_DOWN | CTRL_LEFT; break;
294 			case 4: ctrlMask |= CTRL_LEFT; break;
295 			case 5: ctrlMask |= CTRL_UP | CTRL_LEFT; break;
296 			case 6: ctrlMask |= CTRL_UP; break;
297 			case 7: ctrlMask |= CTRL_UP | CTRL_RIGHT; break;
298 			}
299 		}
300 	}
301 
302 	down_ = ctrlMask;
303 	int pressed = down_ & ~lastDown;
304 	int released = (~down_) & lastDown;
305 	static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
306 	for (int i = 0; i < 4; i++) {
307 		if (pressed & dir[i]) {
308 			if (g_Config.bHapticFeedback) {
309 				Vibrate(HAPTIC_VIRTUAL_KEY);
310 			}
311 			__CtrlButtonDown(dir[i]);
312 		}
313 		if (released & dir[i]) {
314 			__CtrlButtonUp(dir[i]);
315 		}
316 	}
317 }
318 
Draw(UIContext & dc)319 void PSPDpad::Draw(UIContext &dc) {
320 	float opacity = GetButtonOpacity();
321 	if (opacity <= 0.0f)
322 		return;
323 
324 	static const float xoff[4] = {1, 0, -1, 0};
325 	static const float yoff[4] = {0, 1, 0, -1};
326 	static const int dir[4] = {CTRL_RIGHT, CTRL_DOWN, CTRL_LEFT, CTRL_UP};
327 	int buttons = __CtrlPeekButtons();
328 	float r = D_pad_Radius * spacing_;
329 	for (int i = 0; i < 4; i++) {
330 		bool isDown = (buttons & dir[i]) != 0;
331 
332 		float x = bounds_.centerX() + xoff[i] * r;
333 		float y = bounds_.centerY() + yoff[i] * r;
334 		float x2 = bounds_.centerX() + xoff[i] * (r + 10.f * scale_);
335 		float y2 = bounds_.centerY() + yoff[i] * (r + 10.f * scale_);
336 		float angle = i * M_PI / 2;
337 		float imgScale = isDown ? scale_ * 2 : scale_;
338 		float imgOpacity = opacity;
339 
340 		if (isDown && g_Config.iTouchButtonStyle == 2) {
341 			imgScale = scale_;
342 			imgOpacity *= 1.35f;
343 
344 			uint32_t downBg = colorAlpha(0x00FFFFFF, imgOpacity * 0.5f);
345 			if (arrowIndex_ != arrowDownIndex_)
346 				dc.Draw()->DrawImageRotated(arrowDownIndex_, x, y, imgScale, angle + PI, downBg, false);
347 		}
348 
349 		uint32_t colorBg = colorAlpha(GetButtonColor(), imgOpacity);
350 		uint32_t color = colorAlpha(0xFFFFFF, imgOpacity);
351 
352 		dc.Draw()->DrawImageRotated(arrowIndex_, x, y, imgScale, angle + PI, colorBg, false);
353 		if (overlayIndex_.isValid())
354 			dc.Draw()->DrawImageRotated(overlayIndex_, x2, y2, imgScale, angle + PI, color);
355 	}
356 }
357 
PSPStick(ImageID bgImg,const char * key,ImageID stickImg,ImageID stickDownImg,int stick,float scale,UI::LayoutParams * layoutParams)358 PSPStick::PSPStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, int stick, float scale, UI::LayoutParams *layoutParams)
359 	: GamepadView(key, layoutParams), dragPointerId_(-1), bgImg_(bgImg), stickImageIndex_(stickImg), stickDownImg_(stickDownImg), stick_(stick), scale_(scale), centerX_(-1), centerY_(-1) {
360 	stick_size_ = 50;
361 }
362 
GetContentDimensions(const UIContext & dc,float & w,float & h) const363 void PSPStick::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
364 	dc.Draw()->GetAtlas()->measureImage(bgImg_, &w, &h);
365 	w *= scale_;
366 	h *= scale_;
367 }
368 
Draw(UIContext & dc)369 void PSPStick::Draw(UIContext &dc) {
370 	float opacity = GetButtonOpacity();
371 	if (opacity <= 0.0f)
372 		return;
373 
374 	if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
375 		opacity *= 1.35f;
376 	}
377 
378 	uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
379 	uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
380 
381 	if (centerX_ < 0.0f) {
382 		centerX_ = bounds_.centerX();
383 		centerY_ = bounds_.centerY();
384 	}
385 
386 	float stickX = centerX_;
387 	float stickY = centerY_;
388 
389 	float dx, dy;
390 	__CtrlPeekAnalog(stick_, &dx, &dy);
391 
392 	if (!g_Config.bHideStickBackground)
393 		dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
394 	float headScale = stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale;
395 	if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
396 		dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, downBg, ALIGN_CENTER);
397 	dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f * scale_ * headScale, colorBg, ALIGN_CENTER);
398 }
399 
Touch(const TouchInput & input)400 void PSPStick::Touch(const TouchInput &input) {
401 	GamepadView::Touch(input);
402 	if (input.flags & TOUCH_RELEASE_ALL) {
403 		dragPointerId_ = -1;
404 		centerX_ = bounds_.centerX();
405 		centerY_ = bounds_.centerY();
406 		__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
407 		usedPointerMask = 0;
408 		analogPointerMask = 0;
409 		return;
410 	}
411 	if (input.flags & TOUCH_DOWN) {
412 		float fac = 0.5f*(stick_ ? g_Config.fRightStickHeadScale : g_Config.fLeftStickHeadScale)-0.5f;
413 		if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
414 			if (g_Config.bAutoCenterTouchAnalog) {
415 				centerX_ = input.x;
416 				centerY_ = input.y;
417 			} else {
418 				centerX_ = bounds_.centerX();
419 				centerY_ = bounds_.centerY();
420 			}
421 			dragPointerId_ = input.id;
422 			usedPointerMask |= 1 << input.id;
423 			analogPointerMask |= 1 << input.id;
424 			ProcessTouch(input.x, input.y, true);
425 		}
426 	}
427 	if (input.flags & TOUCH_MOVE) {
428 		if (input.id == dragPointerId_) {
429 			ProcessTouch(input.x, input.y, true);
430 		}
431 	}
432 	if (input.flags & TOUCH_UP) {
433 		if (input.id == dragPointerId_) {
434 			dragPointerId_ = -1;
435 			centerX_ = bounds_.centerX();
436 			centerY_ = bounds_.centerY();
437 			usedPointerMask &= ~(1 << input.id);
438 			analogPointerMask &= ~(1 << input.id);
439 			ProcessTouch(input.x, input.y, false);
440 		}
441 	}
442 }
443 
ProcessTouch(float x,float y,bool down)444 void PSPStick::ProcessTouch(float x, float y, bool down) {
445 	if (down && centerX_ >= 0.0f) {
446 		float inv_stick_size = 1.0f / (stick_size_ * scale_);
447 
448 		float dx = (x - centerX_) * inv_stick_size;
449 		float dy = (y - centerY_) * inv_stick_size;
450 		// Do not clamp to a circle! The PSP has nearly square range!
451 
452 		// Old code to clamp to a circle
453 		// float len = sqrtf(dx * dx + dy * dy);
454 		// if (len > 1.0f) {
455 		//	dx /= len;
456 		//	dy /= len;
457 		//}
458 
459 		// Still need to clamp to a square
460 		dx = std::min(1.0f, std::max(-1.0f, dx));
461 		dy = std::min(1.0f, std::max(-1.0f, dy));
462 
463 		__CtrlSetAnalogXY(stick_, dx, -dy);
464 	} else {
465 		__CtrlSetAnalogXY(stick_, 0.0f, 0.0f);
466 	}
467 }
468 
PSPCustomStick(ImageID bgImg,const char * key,ImageID stickImg,ImageID stickDownImg,float scale,UI::LayoutParams * layoutParams)469 PSPCustomStick::PSPCustomStick(ImageID bgImg, const char *key, ImageID stickImg, ImageID stickDownImg, float scale, UI::LayoutParams *layoutParams)
470 	: PSPStick(bgImg, key, stickImg, stickDownImg, -1, scale, layoutParams) {
471 }
472 
Draw(UIContext & dc)473 void PSPCustomStick::Draw(UIContext &dc) {
474 	float opacity = GetButtonOpacity();
475 	if (opacity <= 0.0f)
476 		return;
477 
478 	if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2) {
479 		opacity *= 1.35f;
480 	}
481 
482 	uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
483 	uint32_t downBg = colorAlpha(0x00FFFFFF, opacity * 0.5f);
484 
485 	if (centerX_ < 0.0f) {
486 		centerX_ = bounds_.centerX();
487 		centerY_ = bounds_.centerY();
488 	}
489 
490 	float stickX = centerX_;
491 	float stickY = centerY_;
492 
493 	float dx, dy;
494 	dx = posX_;
495 	dy = -posY_;
496 
497 	if (!g_Config.bHideStickBackground)
498 		dc.Draw()->DrawImage(bgImg_, stickX, stickY, 1.0f * scale_, colorBg, ALIGN_CENTER);
499 	if (dragPointerId_ != -1 && g_Config.iTouchButtonStyle == 2 && stickDownImg_ != stickImageIndex_)
500 		dc.Draw()->DrawImage(stickDownImg_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, downBg, ALIGN_CENTER);
501 	dc.Draw()->DrawImage(stickImageIndex_, stickX + dx * stick_size_ * scale_, stickY - dy * stick_size_ * scale_, 1.0f*scale_*g_Config.fRightStickHeadScale, colorBg, ALIGN_CENTER);
502 }
503 
Touch(const TouchInput & input)504 void PSPCustomStick::Touch(const TouchInput &input) {
505 	GamepadView::Touch(input);
506 	if (input.flags & TOUCH_RELEASE_ALL) {
507 		dragPointerId_ = -1;
508 		centerX_ = bounds_.centerX();
509 		centerY_ = bounds_.centerY();
510 		posX_ = 0.0f;
511 		posY_ = 0.0f;
512 		usedPointerMask = 0;
513 		analogPointerMask = 0;
514 		return;
515 	}
516 	if (input.flags & TOUCH_DOWN) {
517 		float fac = 0.5f*g_Config.fRightStickHeadScale-0.5f;
518 		if (dragPointerId_ == -1 && bounds_.Expand(bounds_.w*fac, bounds_.h*fac).Contains(input.x, input.y)) {
519 			if (g_Config.bAutoCenterTouchAnalog) {
520 				centerX_ = input.x;
521 				centerY_ = input.y;
522 			} else {
523 				centerX_ = bounds_.centerX();
524 				centerY_ = bounds_.centerY();
525 			}
526 			dragPointerId_ = input.id;
527 			usedPointerMask |= 1 << input.id;
528 			analogPointerMask |= 1 << input.id;
529 			ProcessTouch(input.x, input.y, true);
530 		}
531 	}
532 	if (input.flags & TOUCH_MOVE) {
533 		if (input.id == dragPointerId_) {
534 			ProcessTouch(input.x, input.y, true);
535 		}
536 	}
537 	if (input.flags & TOUCH_UP) {
538 		if (input.id == dragPointerId_) {
539 			dragPointerId_ = -1;
540 			centerX_ = bounds_.centerX();
541 			centerY_ = bounds_.centerY();
542 			usedPointerMask &= ~(1 << input.id);
543 			analogPointerMask &= ~(1 << input.id);
544 			ProcessTouch(input.x, input.y, false);
545 		}
546 	}
547 }
548 
ProcessTouch(float x,float y,bool down)549 void PSPCustomStick::ProcessTouch(float x, float y, bool down) {
550 	static const int button[16] = {CTRL_LTRIGGER, CTRL_RTRIGGER, CTRL_SQUARE, CTRL_TRIANGLE, CTRL_CIRCLE, CTRL_CROSS, CTRL_UP, CTRL_DOWN, CTRL_LEFT, CTRL_RIGHT, CTRL_START, CTRL_SELECT};
551 
552 	if (down && centerX_ >= 0.0f) {
553 		float inv_stick_size = 1.0f / (stick_size_ * scale_);
554 
555 		float dx = (x - centerX_) * inv_stick_size;
556 		float dy = (y - centerY_) * inv_stick_size;
557 
558 		dx = std::min(1.0f, std::max(-1.0f, dx));
559 		dy = std::min(1.0f, std::max(-1.0f, dy));
560 
561 		if (g_Config.iRightAnalogRight != 0) {
562 			if (dx > 0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) > fabs(dy)))
563 				__CtrlButtonDown(button[g_Config.iRightAnalogRight-1]);
564 			else
565 				__CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
566 		}
567 		if (g_Config.iRightAnalogLeft != 0) {
568 			if (dx < -0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) > fabs(dy)))
569 				__CtrlButtonDown(button[g_Config.iRightAnalogLeft-1]);
570 			else
571 				__CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
572 		}
573 		if (g_Config.iRightAnalogUp != 0) {
574 			if (dy < -0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) <= fabs(dy)))
575 				__CtrlButtonDown(button[g_Config.iRightAnalogUp-1]);
576 			else
577 				__CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
578 		}
579 		if (g_Config.iRightAnalogDown != 0) {
580 			if (dy > 0.5f && (!g_Config.bRightAnalogDisableDiagonal || fabs(dx) <= fabs(dy)))
581 				__CtrlButtonDown(button[g_Config.iRightAnalogDown-1]);
582 			else
583 				__CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
584 		}
585 		if (g_Config.iRightAnalogPress != 0)
586 			__CtrlButtonDown(button[g_Config.iRightAnalogPress-1]);
587 
588 		posX_ = dx;
589 		posY_ = dy;
590 
591 	} else {
592 		if (g_Config.iRightAnalogUp != 0)
593 			__CtrlButtonUp(button[g_Config.iRightAnalogUp-1]);
594 		if (g_Config.iRightAnalogDown != 0)
595 			__CtrlButtonUp(button[g_Config.iRightAnalogDown-1]);
596 		if (g_Config.iRightAnalogLeft != 0)
597 			__CtrlButtonUp(button[g_Config.iRightAnalogLeft-1]);
598 		if (g_Config.iRightAnalogRight != 0)
599 			__CtrlButtonUp(button[g_Config.iRightAnalogRight-1]);
600 		if (g_Config.iRightAnalogPress != 0)
601 			__CtrlButtonUp(button[g_Config.iRightAnalogPress-1]);
602 
603 		posX_ = 0.0f;
604 		posY_ = 0.0f;
605 	}
606 }
607 
InitPadLayout(float xres,float yres,float globalScale)608 void InitPadLayout(float xres, float yres, float globalScale) {
609 	const float scale = globalScale;
610 	const int halfW = xres / 2;
611 
612 	auto initTouchPos = [=](ConfigTouchPos &touch, float x, float y) {
613 		if (touch.x == -1.0f || touch.y == -1.0f) {
614 			touch.x = x / xres;
615 			touch.y = y / yres;
616 			touch.scale = scale;
617 		}
618 	};
619 
620 	// PSP buttons (triangle, circle, square, cross)---------------------
621 	// space between the PSP buttons (triangle, circle, square and cross)
622 	if (g_Config.fActionButtonSpacing < 0) {
623 		g_Config.fActionButtonSpacing = 1.0f;
624 	}
625 
626 	// Position of the circle button (the PSP circle button). It is the farthest to the left
627 	float Action_button_spacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
628 	int Action_button_center_X = xres - Action_button_spacing * 2;
629 	int Action_button_center_Y = yres - Action_button_spacing * 2;
630 	if (g_Config.touchRightAnalogStick.show) {
631 		Action_button_center_Y -= 150 * scale;
632 	}
633 	initTouchPos(g_Config.touchActionButtonCenter, Action_button_center_X, Action_button_center_Y);
634 
635 	//D-PAD (up down left right) (aka PSP cross)----------------------------
636 	//radius to the D-pad
637 	// TODO: Make configurable
638 
639 	int D_pad_X = 2.5 * D_pad_Radius * scale;
640 	int D_pad_Y = yres - D_pad_Radius * scale;
641 	if (g_Config.touchAnalogStick.show) {
642 		D_pad_Y -= 200 * scale;
643 	}
644 	initTouchPos(g_Config.touchDpad, D_pad_X, D_pad_Y);
645 
646 	//analog stick-------------------------------------------------------
647 	//keep the analog stick right below the D pad
648 	int analog_stick_X = D_pad_X;
649 	int analog_stick_Y = yres - 80 * scale;
650 	initTouchPos(g_Config.touchAnalogStick, analog_stick_X, analog_stick_Y);
651 
652 	//right analog stick-------------------------------------------------
653 	//keep the right analog stick right below the face buttons
654 	int right_analog_stick_X = Action_button_center_X;
655 	int right_analog_stick_Y = yres - 80 * scale;
656 	initTouchPos(g_Config.touchRightAnalogStick, right_analog_stick_X, right_analog_stick_Y);
657 
658 	//select, start, throttle--------------------------------------------
659 	//space between the bottom keys (space between select, start and un-throttle)
660 	float bottom_key_spacing = 100;
661 	if (dp_xres < 750) {
662 		bottom_key_spacing *= 0.8f;
663 	}
664 
665 	int start_key_X = halfW + bottom_key_spacing * scale;
666 	int start_key_Y = yres - 60 * scale;
667 	initTouchPos(g_Config.touchStartKey, start_key_X, start_key_Y);
668 
669 	int select_key_X = halfW;
670 	int select_key_Y = yres - 60 * scale;
671 	initTouchPos(g_Config.touchSelectKey, select_key_X, select_key_Y);
672 
673 	int fast_forward_key_X = halfW - bottom_key_spacing * scale;
674 	int fast_forward_key_Y = yres - 60 * scale;
675 	initTouchPos(g_Config.touchFastForwardKey, fast_forward_key_X, fast_forward_key_Y);
676 
677 	// L and R------------------------------------------------------------
678 	// Put them above the analog stick / above the buttons to the right.
679 	// The corners were very hard to reach..
680 
681 	int l_key_X = 60 * scale;
682 	int l_key_Y = yres - 380 * scale;
683 	initTouchPos(g_Config.touchLKey, l_key_X, l_key_Y);
684 
685 	int r_key_X = xres - 60 * scale;
686 	int r_key_Y = l_key_Y;
687 	initTouchPos(g_Config.touchRKey, r_key_X, r_key_Y);
688 
689 	//Combo key
690 	int combo_key_X = halfW + bottom_key_spacing * scale * 1.2f;
691 	int combo_key_Y = yres / 2;
692 	initTouchPos(g_Config.touchCombo0, combo_key_X, combo_key_Y);
693 
694 	int combo1_key_X = halfW + bottom_key_spacing * scale * 2.2f;
695 	int combo1_key_Y = yres / 2;
696 	initTouchPos(g_Config.touchCombo1, combo1_key_X, combo1_key_Y);
697 
698 	int combo2_key_X = halfW + bottom_key_spacing * scale * 3.2f;
699 	int combo2_key_Y = yres / 2;
700 	initTouchPos(g_Config.touchCombo2, combo2_key_X, combo2_key_Y);
701 
702 	int combo3_key_X = halfW + bottom_key_spacing * scale * 1.2f;
703 	int combo3_key_Y = yres / 3;
704 	initTouchPos(g_Config.touchCombo3, combo3_key_X, combo3_key_Y);
705 
706 	int combo4_key_X = halfW + bottom_key_spacing * scale * 2.2f;
707 	int combo4_key_Y = yres / 3;
708 	initTouchPos(g_Config.touchCombo4, combo4_key_X, combo4_key_Y);
709 
710 	int combo5_key_X = halfW - bottom_key_spacing * scale * 1.2f;
711 	int combo5_key_Y = yres / 2;
712 	initTouchPos(g_Config.touchCombo5, combo5_key_X, combo5_key_Y);
713 
714 	int combo6_key_X = halfW - bottom_key_spacing * scale * 2.2f;
715 	int combo6_key_Y = yres / 2;
716 	initTouchPos(g_Config.touchCombo6, combo6_key_X, combo6_key_Y);
717 
718 	int combo7_key_X = halfW - bottom_key_spacing * scale * 3.2f;
719 	int combo7_key_Y = yres / 2;
720 	initTouchPos(g_Config.touchCombo7, combo7_key_X, combo7_key_Y);
721 
722 	int combo8_key_X = halfW - bottom_key_spacing * scale * 1.2f;
723 	int combo8_key_Y = yres / 3;
724 	initTouchPos(g_Config.touchCombo8, combo8_key_X, combo8_key_Y);
725 
726 	int combo9_key_X = halfW - bottom_key_spacing * scale * 2.2f;
727 	int combo9_key_Y = yres / 3;
728 	initTouchPos(g_Config.touchCombo9, combo9_key_X, combo9_key_Y);
729 }
730 
CreatePadLayout(float xres,float yres,bool * pause,bool showPauseButton,ControlMapper * controllMapper)731 UI::ViewGroup *CreatePadLayout(float xres, float yres, bool *pause, bool showPauseButton, ControlMapper* controllMapper) {
732 	using namespace UI;
733 
734 	AnchorLayout *root = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
735 	if (!g_Config.bShowTouchControls) {
736 		return root;
737 	}
738 
739 	struct ButtonOffset {
740 		float x;
741 		float y;
742 	};
743 	auto buttonLayoutParams = [=](const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) {
744 		return new AnchorLayoutParams(touch.x * xres + off.x, touch.y * yres + off.y, NONE, NONE, true);
745 	};
746 
747 	// Space between the PSP buttons (traingle, circle, square and cross)
748 	const float actionButtonSpacing = g_Config.fActionButtonSpacing * baseActionButtonSpacing;
749 	// Position of the circle button (the PSP circle button).  It is the farthest to the right.
750 	ButtonOffset circleOffset{ actionButtonSpacing, 0.0f };
751 	ButtonOffset crossOffset{ 0.0f, actionButtonSpacing };
752 	ButtonOffset triangleOffset{ 0.0f, -actionButtonSpacing };
753 	ButtonOffset squareOffset{ -actionButtonSpacing, 0.0f };
754 
755 	const int halfW = xres / 2;
756 
757 	const ImageID roundImage = g_Config.iTouchButtonStyle ? ImageID("I_ROUND_LINE") : ImageID("I_ROUND");
758 	const ImageID rectImage = g_Config.iTouchButtonStyle ? ImageID("I_RECT_LINE") : ImageID("I_RECT");
759 	const ImageID shoulderImage = g_Config.iTouchButtonStyle ? ImageID("I_SHOULDER_LINE") : ImageID("I_SHOULDER");
760 	const ImageID dirImage = g_Config.iTouchButtonStyle ? ImageID("I_DIR_LINE") : ImageID("I_DIR");
761 	const ImageID stickImage = g_Config.iTouchButtonStyle ? ImageID("I_STICK_LINE") : ImageID("I_STICK");
762 	const ImageID stickBg = g_Config.iTouchButtonStyle ? ImageID("I_STICK_BG_LINE") : ImageID("I_STICK_BG");
763 
764 	auto addPSPButton = [=](int buttonBit, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch, ButtonOffset off = { 0, 0 }) -> PSPButton * {
765 		if (touch.show) {
766 			return root->Add(new PSPButton(buttonBit, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch, off)));
767 		}
768 		return nullptr;
769 	};
770 	auto addBoolButton = [=](bool *value, const char *key, ImageID bgImg, ImageID bgDownImg, ImageID img, const ConfigTouchPos &touch) -> BoolButton * {
771 		if (touch.show) {
772 			return root->Add(new BoolButton(value, key, bgImg, bgDownImg, img, touch.scale, buttonLayoutParams(touch)));
773 		}
774 		return nullptr;
775 	};
776 	auto addComboKey = [=](const ConfigCustomButton& cfg, const char *key, const ConfigTouchPos &touch) -> ComboKey * {
777 		using namespace CustomKey;
778 		if (touch.show) {
779 			auto aux = root->Add(new ComboKey(cfg.key, key, cfg.toggle, controllMapper,
780 					g_Config.iTouchButtonStyle == 0 ? comboKeyShapes[cfg.shape].i : comboKeyShapes[cfg.shape].l, comboKeyShapes[cfg.shape].i,
781 					comboKeyImages[cfg.image].i, touch.scale, comboKeyShapes[cfg.shape].d, buttonLayoutParams(touch)));
782 			aux->SetAngle(comboKeyImages[cfg.image].r, comboKeyShapes[cfg.shape].r);
783 			aux->FlipImageH(comboKeyShapes[cfg.shape].f);
784 			return aux;
785 		}
786 		return nullptr;
787 	};
788 
789 	if (showPauseButton) {
790 		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);
791 	}
792 
793 	// touchActionButtonCenter.show will always be true, since that's the default.
794 	if (g_Config.bShowTouchCircle)
795 		addPSPButton(CTRL_CIRCLE, "Circle button", roundImage, ImageID("I_ROUND"), ImageID("I_CIRCLE"), g_Config.touchActionButtonCenter, circleOffset);
796 	if (g_Config.bShowTouchCross)
797 		addPSPButton(CTRL_CROSS, "Cross button", roundImage, ImageID("I_ROUND"), ImageID("I_CROSS"), g_Config.touchActionButtonCenter, crossOffset);
798 	if (g_Config.bShowTouchTriangle)
799 		addPSPButton(CTRL_TRIANGLE, "Triangle button", roundImage, ImageID("I_ROUND"), ImageID("I_TRIANGLE"), g_Config.touchActionButtonCenter, triangleOffset);
800 	if (g_Config.bShowTouchSquare)
801 		addPSPButton(CTRL_SQUARE, "Square button", roundImage, ImageID("I_ROUND"), ImageID("I_SQUARE"), g_Config.touchActionButtonCenter, squareOffset);
802 
803 	addPSPButton(CTRL_START, "Start button", rectImage, ImageID("I_RECT"), ImageID("I_START"), g_Config.touchStartKey);
804 	addPSPButton(CTRL_SELECT, "Select button", rectImage, ImageID("I_RECT"), ImageID("I_SELECT"), g_Config.touchSelectKey);
805 
806 	BoolButton *fastForward = addBoolButton(&PSP_CoreParameter().fastForward, "Fast-forward button", rectImage, ImageID("I_RECT"), ImageID("I_ARROW"), g_Config.touchFastForwardKey);
807 	if (fastForward) {
808 		fastForward->SetAngle(180.0f);
809 		fastForward->OnChange.Add([](UI::EventParams &e) {
810 			if (e.a && coreState == CORE_STEPPING) {
811 				Core_EnableStepping(false);
812 			}
813 			return UI::EVENT_DONE;
814 		});
815 	}
816 
817 	addPSPButton(CTRL_LTRIGGER, "Left shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_L"), g_Config.touchLKey);
818 	PSPButton *rTrigger = addPSPButton(CTRL_RTRIGGER, "Right shoulder button", shoulderImage, ImageID("I_SHOULDER"), ImageID("I_R"), g_Config.touchRKey);
819 	if (rTrigger)
820 		rTrigger->FlipImageH(true);
821 
822 	if (g_Config.touchDpad.show)
823 		root->Add(new PSPDpad(dirImage, "D-pad", ImageID("I_DIR"), ImageID("I_ARROW"), g_Config.touchDpad.scale, g_Config.fDpadSpacing, buttonLayoutParams(g_Config.touchDpad)));
824 
825 	if (g_Config.touchAnalogStick.show)
826 		root->Add(new PSPStick(stickBg, "Left analog stick", stickImage, ImageID("I_STICK"), 0, g_Config.touchAnalogStick.scale, buttonLayoutParams(g_Config.touchAnalogStick)));
827 
828 	if (g_Config.touchRightAnalogStick.show) {
829 		if (g_Config.bRightAnalogCustom)
830 			root->Add(new PSPCustomStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
831 		else
832 			root->Add(new PSPStick(stickBg, "Right analog stick", stickImage, ImageID("I_STICK"), 1, g_Config.touchRightAnalogStick.scale, buttonLayoutParams(g_Config.touchRightAnalogStick)));
833 	}
834 
835 	addComboKey(g_Config.CustomKey0, "Custom 1 button", g_Config.touchCombo0);
836 	addComboKey(g_Config.CustomKey1, "Custom 2 button", g_Config.touchCombo1);
837 	addComboKey(g_Config.CustomKey2, "Custom 3 button", g_Config.touchCombo2);
838 	addComboKey(g_Config.CustomKey3, "Custom 4 button", g_Config.touchCombo3);
839 	addComboKey(g_Config.CustomKey4, "Custom 5 button", g_Config.touchCombo4);
840 	addComboKey(g_Config.CustomKey5, "Custom 6 button", g_Config.touchCombo5);
841 	addComboKey(g_Config.CustomKey6, "Custom 7 button", g_Config.touchCombo6);
842 	addComboKey(g_Config.CustomKey7, "Custom 8 button", g_Config.touchCombo7);
843 	addComboKey(g_Config.CustomKey8, "Custom 9 button", g_Config.touchCombo8);
844 	addComboKey(g_Config.CustomKey9, "Custom 10 button", g_Config.touchCombo9);
845 
846 	if (g_Config.bGestureControlEnabled)
847 		root->Add(new GestureGamepad(controllMapper));
848 
849 	return root;
850 }
851 
852 
Touch(const TouchInput & input)853 void GestureGamepad::Touch(const TouchInput &input) {
854 	if (usedPointerMask & (1 << input.id)) {
855 		if (input.id == dragPointerId_)
856 			dragPointerId_ = -1;
857 		return;
858 	}
859 
860 	if (input.flags & TOUCH_RELEASE_ALL) {
861 		dragPointerId_ = -1;
862 		return;
863 	}
864 
865 	if (input.flags & TOUCH_DOWN) {
866 		if (dragPointerId_ == -1) {
867 			dragPointerId_ = input.id;
868 			lastX_ = input.x;
869 			lastY_ = input.y;
870 
871 			const float now = time_now_d();
872 			if (now - lastTapRelease_ < 0.3f && !haveDoubleTapped_) {
873 				if (g_Config.iDoubleTapGesture != 0 )
874 					controllMapper_->pspKey(GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_DOWN);
875 				haveDoubleTapped_ = true;
876 			}
877 
878 			lastTouchDown_ = now;
879 		}
880 	}
881 	if (input.flags & TOUCH_MOVE) {
882 		if (input.id == dragPointerId_) {
883 			deltaX_ += input.x - lastX_;
884 			deltaY_ += input.y - lastY_;
885 			lastX_ = input.x;
886 			lastY_ = input.y;
887 		}
888 	}
889 	if (input.flags & TOUCH_UP) {
890 		if (input.id == dragPointerId_) {
891 			dragPointerId_ = -1;
892 			if (time_now_d() - lastTouchDown_ < 0.3f)
893 				lastTapRelease_ = time_now_d();
894 
895 			if (haveDoubleTapped_) {
896 				if (g_Config.iDoubleTapGesture != 0)
897 					controllMapper_->pspKey(GestureKey::keyList[g_Config.iDoubleTapGesture-1], KEY_UP);
898 				haveDoubleTapped_ = false;
899 			}
900 		}
901 	}
902 }
903 
Update()904 void GestureGamepad::Update() {
905 	const float th = 1.0f;
906 	float dx = deltaX_ * g_dpi_scale_x * g_Config.fSwipeSensitivity;
907 	float dy = deltaY_ * g_dpi_scale_y * g_Config.fSwipeSensitivity;
908 	if (g_Config.iSwipeRight != 0) {
909 		if (dx > th) {
910 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeRight-1], KEY_DOWN);
911 			swipeRightReleased_ = false;
912 		} else if (!swipeRightReleased_) {
913 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeRight-1], KEY_UP);
914 			swipeRightReleased_ = true;
915 		}
916 	}
917 	if (g_Config.iSwipeLeft != 0) {
918 		if (dx < -th) {
919 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_DOWN);
920 			swipeLeftReleased_ = false;
921 		} else if (!swipeLeftReleased_) {
922 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeLeft-1], KEY_UP);
923 			swipeLeftReleased_ = true;
924 		}
925 	}
926 	if (g_Config.iSwipeUp != 0) {
927 		if (dy < -th) {
928 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeUp-1], KEY_DOWN);
929 			swipeUpReleased_ = false;
930 		} else if (!swipeUpReleased_) {
931 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeUp-1], KEY_UP);
932 			swipeUpReleased_ = true;
933 		}
934 	}
935 	if (g_Config.iSwipeDown != 0) {
936 		if (dy > th) {
937 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeDown-1], KEY_DOWN);
938 			swipeDownReleased_ = false;
939 		} else if (!swipeDownReleased_) {
940 			controllMapper_->pspKey(GestureKey::keyList[g_Config.iSwipeDown-1], KEY_UP);
941 			swipeDownReleased_ = true;
942 		}
943 	}
944 	deltaX_ *= g_Config.fSwipeSmoothing;
945 	deltaY_ *= g_Config.fSwipeSmoothing;
946 }
947