1 /* Copyright (C) 2015 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "TouchInput.h"
21 
22 #include <cinttypes>
23 
24 #include "graphics/Camera.h"
25 #include "graphics/GameView.h"
26 #include "lib/timer.h"
27 #include "lib/external_libraries/libsdl.h"
28 #include "ps/Game.h"
29 
30 // When emulation is enabled:
31 // Left-click to put finger 0 down.
32 // Then left-click-and-drag to move finger 0.
33 // Then left-click to put finger 0 up.
34 // Same with right-click for finger 1.
35 #define EMULATE_FINGERS_WITH_MOUSE 0
36 
37 extern int g_xres, g_yres;
38 
39 // NOTE: All this code is currently just a basic prototype for testing;
40 // it might need significant redesigning for proper usage.
41 
CTouchInput()42 CTouchInput::CTouchInput() :
43 	m_State(STATE_INACTIVE)
44 {
45 	for (size_t i = 0; i < MAX_FINGERS; ++i)
46 		m_Down[i] = false;
47 
48 	for (size_t i = 0; i < MAX_MOUSE; ++i)
49 		m_MouseEmulateState[i] = MOUSE_INACTIVE;
50 }
51 
~CTouchInput()52 CTouchInput::~CTouchInput()
53 {
54 }
55 
IsEnabled()56 bool CTouchInput::IsEnabled()
57 {
58 #if OS_ANDROID || EMULATE_FINGERS_WITH_MOUSE
59 	return true;
60 #else
61 	return false;
62 #endif
63 }
64 
OnFingerDown(int id,int x,int y)65 void CTouchInput::OnFingerDown(int id, int x, int y)
66 {
67 	debug_printf("finger down %d %d %d; state %d\n", id, x, y, m_State);
68 	m_Down[id] = true;
69 	m_Prev[id] = m_Pos[id] = CVector2D(x, y);
70 
71 	if (m_State == STATE_INACTIVE && id == 0)
72 	{
73 		m_State = STATE_FIRST_TOUCH;
74 		m_FirstTouchTime = timer_Time();
75 		m_FirstTouchPos = CVector2D(x, y);
76 	}
77 	else if ((m_State == STATE_FIRST_TOUCH || m_State == STATE_PANNING) && id == 1)
78 	{
79 		m_State = STATE_ZOOMING;
80 	}
81 }
82 
OnFingerUp(int id,int x,int y)83 void CTouchInput::OnFingerUp(int id, int x, int y)
84 {
85 	debug_printf("finger up %d %d %d; state %d\n", id, x, y, m_State);
86 	m_Down[id] = false;
87 	m_Pos[id] = CVector2D(x, y);
88 
89 	if (m_State == STATE_FIRST_TOUCH && id == 0 && timer_Time() < m_FirstTouchTime + 0.5)
90 	{
91 		m_State = STATE_INACTIVE;
92 
93 		SDL_Event_ ev;
94 		ev.ev.button.button = SDL_BUTTON_LEFT;
95 		ev.ev.button.x = m_Pos[0].X;
96 		ev.ev.button.y = m_Pos[0].Y;
97 
98 		ev.ev.type = SDL_MOUSEBUTTONDOWN;
99 		ev.ev.button.state = SDL_PRESSED;
100 		SDL_PushEvent(&ev.ev);
101 
102 		ev.ev.type = SDL_MOUSEBUTTONUP;
103 		ev.ev.button.state = SDL_RELEASED;
104 		SDL_PushEvent(&ev.ev);
105 	}
106 	else if (m_State == STATE_ZOOMING && id == 1)
107 	{
108  		m_State = STATE_PANNING;
109 	}
110  	else
111 	{
112 		m_State = STATE_INACTIVE;
113 	}
114 }
115 
OnFingerMotion(int id,int x,int y)116 void CTouchInput::OnFingerMotion(int id, int x, int y)
117 {
118 	debug_printf("finger motion %d %d %d; state %d\n", id, x, y, m_State);
119 
120 	CVector2D pos(x, y);
121 
122 	m_Prev[id] = m_Pos[id];
123 	m_Pos[id] = pos;
124 
125 	if (m_State == STATE_FIRST_TOUCH && id == 0)
126 	{
127 		if ((pos - m_FirstTouchPos).Length() > 16)
128 		{
129 			m_State = STATE_PANNING;
130 
131 			CCamera& camera = *(g_Game->GetView()->GetCamera());
132 			m_PanFocus = camera.GetWorldCoordinates(m_FirstTouchPos.X, m_FirstTouchPos.Y, true);
133 			m_PanDist = (m_PanFocus - camera.GetOrientation().GetTranslation()).Y;
134 		}
135 	}
136 
137 	if (m_State == STATE_PANNING && id == 0)
138 	{
139 		CCamera& camera = *(g_Game->GetView()->GetCamera());
140 		CVector3D origin, dir;
141 		camera.BuildCameraRay(x, y, origin, dir);
142 		dir *= m_PanDist / dir.Y;
143 		camera.GetOrientation().Translate(m_PanFocus - dir - origin);
144 		camera.UpdateFrustum();
145 	}
146 
147 	if (m_State == STATE_ZOOMING && id == 1)
148 	{
149 		float oldDist = (m_Prev[id] - m_Pos[1 - id]).Length();
150 		float newDist = (m_Pos[id] - m_Pos[1 - id]).Length();
151 		float zoomDist = (newDist - oldDist) * -0.005f * m_PanDist;
152 
153 		CCamera& camera = *(g_Game->GetView()->GetCamera());
154 		CVector3D origin, dir;
155 		camera.BuildCameraRay(m_Pos[0].X, m_Pos[0].Y, origin, dir);
156 		dir *= zoomDist;
157 		camera.GetOrientation().Translate(dir);
158 		camera.UpdateFrustum();
159 
160 		m_PanFocus = camera.GetWorldCoordinates(m_Pos[0].X, m_Pos[0].Y, true);
161 		m_PanDist = (m_PanFocus - camera.GetOrientation().GetTranslation()).Y;
162 	}
163 }
164 
Frame()165 void CTouchInput::Frame()
166 {
167 	double t = timer_Time();
168 	if (m_State == STATE_FIRST_TOUCH && t > m_FirstTouchTime + 1.0)
169 	{
170 		m_State = STATE_INACTIVE;
171 
172 		SDL_Event_ ev;
173 		ev.ev.button.button = SDL_BUTTON_RIGHT;
174 		ev.ev.button.x = m_Pos[0].X;
175 		ev.ev.button.y = m_Pos[0].Y;
176 
177 		ev.ev.type = SDL_MOUSEBUTTONDOWN;
178 		ev.ev.button.state = SDL_PRESSED;
179 		SDL_PushEvent(&ev.ev);
180 
181 		ev.ev.type = SDL_MOUSEBUTTONUP;
182 		ev.ev.button.state = SDL_RELEASED;
183 		SDL_PushEvent(&ev.ev);
184 	}
185 }
186 
HandleEvent(const SDL_Event_ * ev)187 InReaction CTouchInput::HandleEvent(const SDL_Event_* ev)
188 {
189 	UNUSED2(ev); // may be unused depending on #ifs
190 
191 	if (!IsEnabled())
192 		return IN_PASS;
193 
194 #if EMULATE_FINGERS_WITH_MOUSE
195 	switch(ev->ev.type)
196 	{
197 	case SDL_MOUSEBUTTONDOWN:
198 	{
199 		int button;
200 		if (ev->ev.button.button == SDL_BUTTON_LEFT)
201 			button = 0;
202 		else if (ev->ev.button.button == SDL_BUTTON_RIGHT)
203 			button = 1;
204 		else
205 			return IN_PASS;
206 
207 		m_MouseEmulateDownPos[button] = CVector2D(ev->ev.button.x, ev->ev.button.y);
208 		if (m_MouseEmulateState[button] == MOUSE_INACTIVE)
209 		{
210 			m_MouseEmulateState[button] = MOUSE_ACTIVATING;
211 			OnFingerDown(button, ev->ev.button.x, ev->ev.button.y);
212 		}
213 		else if (m_MouseEmulateState[button] == MOUSE_ACTIVE_UP)
214 		{
215 			m_MouseEmulateState[button] = MOUSE_ACTIVE_DOWN;
216 		}
217 		return IN_HANDLED;
218 	}
219 
220 	case SDL_MOUSEBUTTONUP:
221 	{
222 		int button;
223 		if (ev->ev.button.button == SDL_BUTTON_LEFT)
224 			button = 0;
225 		else if (ev->ev.button.button == SDL_BUTTON_RIGHT)
226 			button = 1;
227 		else
228 			return IN_PASS;
229 
230 		if (m_MouseEmulateState[button] == MOUSE_ACTIVATING)
231 		{
232 			m_MouseEmulateState[button] = MOUSE_ACTIVE_UP;
233 		}
234 		else if (m_MouseEmulateState[button] == MOUSE_ACTIVE_DOWN)
235 		{
236 			float dist = (m_MouseEmulateDownPos[button] - CVector2D(ev->ev.button.x, ev->ev.button.y)).Length();
237 			if (dist <= 2)
238 			{
239 				m_MouseEmulateState[button] = MOUSE_INACTIVE;
240 				OnFingerUp(button, ev->ev.button.x, ev->ev.button.y);
241 			}
242 			else
243 			{
244 				m_MouseEmulateState[button] = MOUSE_ACTIVE_UP;
245 			}
246 		}
247 		return IN_HANDLED;
248 	}
249 
250 	case SDL_MOUSEMOTION:
251 	{
252 		for (size_t i = 0; i < MAX_MOUSE; ++i)
253 		{
254 			if (m_MouseEmulateState[i] == MOUSE_ACTIVE_DOWN)
255 			{
256 				OnFingerMotion(i, ev->ev.motion.x, ev->ev.motion.y);
257 			}
258 		}
259 		return IN_HANDLED;
260 	}
261 	}
262 #endif
263 
264 	switch(ev->ev.type)
265 	{
266 	case SDL_FINGERDOWN:
267 	case SDL_FINGERUP:
268 	case SDL_FINGERMOTION:
269 	{
270 		// Map finger events onto the mouse, for basic testing
271 		debug_printf("finger %s tid=%" PRId64 " fid=%" PRId64 " x=%f y=%f dx=%f dy=%f p=%f\n",
272 			ev->ev.type == SDL_FINGERDOWN ? "down" :
273 			ev->ev.type == SDL_FINGERUP ? "up" :
274 			ev->ev.type == SDL_FINGERMOTION ? "motion" : "?",
275 			ev->ev.tfinger.touchId, ev->ev.tfinger.fingerId,
276 			ev->ev.tfinger.x, ev->ev.tfinger.y, ev->ev.tfinger.dx, ev->ev.tfinger.dy, ev->ev.tfinger.pressure);
277 
278 		if (ev->ev.type == SDL_FINGERDOWN)
279 			OnFingerDown(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
280 		else if (ev->ev.type == SDL_FINGERUP)
281 			OnFingerUp(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
282 		else if (ev->ev.type == SDL_FINGERMOTION)
283 			OnFingerMotion(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
284 		return IN_HANDLED;
285 	}
286 	}
287 
288 	return IN_PASS;
289 }
290 
291 CTouchInput g_TouchInput;
292 
touch_input_handler(const SDL_Event_ * ev)293 InReaction touch_input_handler(const SDL_Event_* ev)
294 {
295 	return g_TouchInput.HandleEvent(ev);
296 }
297