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