Line data Source code
1 : /* Copyright (C) 2019 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 :
42 1 : CTouchInput::CTouchInput() :
43 1 : m_State(STATE_INACTIVE)
44 : {
45 3 : for (size_t i = 0; i < MAX_FINGERS; ++i)
46 2 : m_Down[i] = false;
47 :
48 3 : for (size_t i = 0; i < MAX_MOUSE; ++i)
49 2 : m_MouseEmulateState[i] = MOUSE_INACTIVE;
50 1 : }
51 :
52 1 : CTouchInput::~CTouchInput()
53 : {
54 1 : }
55 :
56 7 : bool CTouchInput::IsEnabled()
57 : {
58 : #if OS_ANDROID || EMULATE_FINGERS_WITH_MOUSE
59 : return true;
60 : #else
61 7 : return false;
62 : #endif
63 : }
64 :
65 0 : void CTouchInput::OnFingerDown(int id, int x, int y)
66 : {
67 0 : debug_printf("finger down %d %d %d; state %d\n", id, x, y, m_State);
68 0 : m_Down[id] = true;
69 0 : m_Prev[id] = m_Pos[id] = CVector2D(x, y);
70 :
71 0 : if (m_State == STATE_INACTIVE && id == 0)
72 : {
73 0 : m_State = STATE_FIRST_TOUCH;
74 0 : m_FirstTouchTime = timer_Time();
75 0 : m_FirstTouchPos = CVector2D(x, y);
76 : }
77 0 : else if ((m_State == STATE_FIRST_TOUCH || m_State == STATE_PANNING) && id == 1)
78 : {
79 0 : m_State = STATE_ZOOMING;
80 : }
81 0 : }
82 :
83 0 : void CTouchInput::OnFingerUp(int id, int x, int y)
84 : {
85 0 : debug_printf("finger up %d %d %d; state %d\n", id, x, y, m_State);
86 0 : m_Down[id] = false;
87 0 : m_Pos[id] = CVector2D(x, y);
88 :
89 0 : if (m_State == STATE_FIRST_TOUCH && id == 0 && timer_Time() < m_FirstTouchTime + 0.5)
90 : {
91 0 : m_State = STATE_INACTIVE;
92 :
93 : SDL_Event_ ev;
94 0 : ev.ev.button.button = SDL_BUTTON_LEFT;
95 0 : ev.ev.button.x = m_Pos[0].X;
96 0 : ev.ev.button.y = m_Pos[0].Y;
97 :
98 0 : ev.ev.type = SDL_MOUSEBUTTONDOWN;
99 0 : ev.ev.button.state = SDL_PRESSED;
100 0 : SDL_PushEvent(&ev.ev);
101 :
102 0 : ev.ev.type = SDL_MOUSEBUTTONUP;
103 0 : ev.ev.button.state = SDL_RELEASED;
104 0 : SDL_PushEvent(&ev.ev);
105 : }
106 0 : else if (m_State == STATE_ZOOMING && id == 1)
107 : {
108 0 : m_State = STATE_PANNING;
109 : }
110 : else
111 : {
112 0 : m_State = STATE_INACTIVE;
113 : }
114 0 : }
115 :
116 0 : void CTouchInput::OnFingerMotion(int id, int x, int y)
117 : {
118 0 : debug_printf("finger motion %d %d %d; state %d\n", id, x, y, m_State);
119 :
120 0 : CVector2D pos(x, y);
121 :
122 0 : m_Prev[id] = m_Pos[id];
123 0 : m_Pos[id] = pos;
124 :
125 0 : if (m_State == STATE_FIRST_TOUCH && id == 0)
126 : {
127 0 : if ((pos - m_FirstTouchPos).Length() > 16)
128 : {
129 0 : m_State = STATE_PANNING;
130 :
131 0 : const CCamera& camera = *(g_Game->GetView()->GetCamera());
132 0 : m_PanFocus = camera.GetWorldCoordinates(m_FirstTouchPos.X, m_FirstTouchPos.Y, true);
133 0 : m_PanDist = (m_PanFocus - camera.GetOrientation().GetTranslation()).Y;
134 : }
135 : }
136 :
137 0 : if (m_State == STATE_PANNING && id == 0)
138 : {
139 0 : CCamera& camera = *(g_Game->GetView()->GetCamera());
140 0 : CVector3D origin, dir;
141 0 : camera.BuildCameraRay(x, y, origin, dir);
142 0 : dir *= m_PanDist / dir.Y;
143 0 : camera.GetOrientation().Translate(m_PanFocus - dir - origin);
144 0 : camera.UpdateFrustum();
145 : }
146 :
147 0 : if (m_State == STATE_ZOOMING && id == 1)
148 : {
149 0 : float oldDist = (m_Prev[id] - m_Pos[1 - id]).Length();
150 0 : float newDist = (m_Pos[id] - m_Pos[1 - id]).Length();
151 0 : float zoomDist = (newDist - oldDist) * -0.005f * m_PanDist;
152 :
153 0 : CCamera& camera = *(g_Game->GetView()->GetCamera());
154 0 : CVector3D origin, dir;
155 0 : camera.BuildCameraRay(m_Pos[0].X, m_Pos[0].Y, origin, dir);
156 0 : dir *= zoomDist;
157 0 : camera.GetOrientation().Translate(dir);
158 0 : camera.UpdateFrustum();
159 :
160 0 : m_PanFocus = camera.GetWorldCoordinates(m_Pos[0].X, m_Pos[0].Y, true);
161 0 : m_PanDist = (m_PanFocus - camera.GetOrientation().GetTranslation()).Y;
162 : }
163 0 : }
164 :
165 0 : void CTouchInput::Frame()
166 : {
167 0 : double t = timer_Time();
168 0 : if (m_State == STATE_FIRST_TOUCH && t > m_FirstTouchTime + 1.0)
169 : {
170 0 : m_State = STATE_INACTIVE;
171 :
172 : SDL_Event_ ev;
173 0 : ev.ev.button.button = SDL_BUTTON_RIGHT;
174 0 : ev.ev.button.x = m_Pos[0].X;
175 0 : ev.ev.button.y = m_Pos[0].Y;
176 :
177 0 : ev.ev.type = SDL_MOUSEBUTTONDOWN;
178 0 : ev.ev.button.state = SDL_PRESSED;
179 0 : SDL_PushEvent(&ev.ev);
180 :
181 0 : ev.ev.type = SDL_MOUSEBUTTONUP;
182 0 : ev.ev.button.state = SDL_RELEASED;
183 0 : SDL_PushEvent(&ev.ev);
184 : }
185 0 : }
186 :
187 7 : InReaction CTouchInput::HandleEvent(const SDL_Event_* ev)
188 : {
189 : UNUSED2(ev); // may be unused depending on #ifs
190 :
191 7 : if (!IsEnabled())
192 7 : 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 0 : switch(ev->ev.type)
265 : {
266 0 : case SDL_FINGERDOWN:
267 : case SDL_FINGERUP:
268 : case SDL_FINGERMOTION:
269 : {
270 : // Map finger events onto the mouse, for basic testing
271 0 : debug_printf("finger %s tid=%" PRId64 " fid=%" PRId64 " x=%f y=%f dx=%f dy=%f p=%f\n",
272 0 : ev->ev.type == SDL_FINGERDOWN ? "down" :
273 0 : ev->ev.type == SDL_FINGERUP ? "up" :
274 0 : ev->ev.type == SDL_FINGERMOTION ? "motion" : "?",
275 0 : ev->ev.tfinger.touchId, ev->ev.tfinger.fingerId,
276 0 : ev->ev.tfinger.x, ev->ev.tfinger.y, ev->ev.tfinger.dx, ev->ev.tfinger.dy, ev->ev.tfinger.pressure);
277 :
278 0 : if (ev->ev.type == SDL_FINGERDOWN)
279 0 : OnFingerDown(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
280 0 : else if (ev->ev.type == SDL_FINGERUP)
281 0 : OnFingerUp(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
282 0 : else if (ev->ev.type == SDL_FINGERMOTION)
283 0 : OnFingerMotion(ev->ev.tfinger.fingerId, g_xres * ev->ev.tfinger.x, g_yres * ev->ev.tfinger.y);
284 0 : return IN_HANDLED;
285 : }
286 : }
287 :
288 0 : return IN_PASS;
289 : }
290 :
291 1 : CTouchInput g_TouchInput;
292 :
293 7 : InReaction touch_input_handler(const SDL_Event_* ev)
294 : {
295 7 : return g_TouchInput.HandleEvent(ev);
296 3 : }
|