1/*         ______   ___    ___
2 *        /\  _  \ /\_ \  /\_ \
3 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 *                                           /\____/
9 *                                           \_/__/
10 *
11 *      iPhone family device touch input driver.
12 *
13 *      By Michał Cichoń.
14 *
15 *      See readme.txt for copyright information.
16 */
17#include "allegro5/allegro.h"
18#include "allegro5/internal/aintern_touch_input.h"
19#include "allegro5/internal/aintern_display.h"
20#include "allegro5/internal/aintern_iphone.h"
21
22
23static ALLEGRO_TOUCH_INPUT_STATE touch_input_state;
24static ALLEGRO_MOUSE_STATE mouse_state;
25static ALLEGRO_TOUCH_INPUT touch_input;
26static bool installed = false;
27
28static void generate_touch_input_event(unsigned int type, double timestamp, int id, float x, float y, float dx, float dy, bool primary, ALLEGRO_DISPLAY *disp)
29{
30   ALLEGRO_EVENT event;
31
32   bool want_touch_event           = _al_event_source_needs_to_generate_event(&touch_input.es);
33   bool want_mouse_emulation_event;
34
35   if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_5_0_x) {
36      want_mouse_emulation_event = _al_event_source_needs_to_generate_event(&touch_input.mouse_emulation_es) && al_is_mouse_installed();
37   }
38   else {
39      want_mouse_emulation_event = _al_event_source_needs_to_generate_event(&touch_input.mouse_emulation_es) && primary && al_is_mouse_installed();
40   }
41
42   if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_NONE)
43      want_mouse_emulation_event = false;
44   else if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_INCLUSIVE)
45      want_touch_event = al_is_mouse_installed() ? (want_touch_event && !primary) : want_touch_event;
46   else if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_EXCLUSIVE)
47      want_touch_event = al_is_mouse_installed() ? false : want_touch_event;
48
49
50   if (!want_touch_event && !want_mouse_emulation_event)
51      return;
52
53   if (want_touch_event) {
54
55      event.touch.type      = type;
56      event.touch.display   = (ALLEGRO_DISPLAY*)disp;
57      event.touch.timestamp = timestamp;
58      event.touch.id        = id;
59      event.touch.x         = x;
60      event.touch.y         = y;
61      event.touch.dx        = dx;
62      event.touch.dy        = dy;
63      event.touch.primary   = primary;
64
65      _al_event_source_lock(&touch_input.es);
66      _al_event_source_emit_event(&touch_input.es, &event);
67      _al_event_source_unlock(&touch_input.es);
68   }
69
70   if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_NONE) {
71      _al_event_source_lock(&touch_input.mouse_emulation_es);
72      if (want_mouse_emulation_event) {
73
74         switch (type) {
75            case ALLEGRO_EVENT_TOUCH_BEGIN: type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN; break;
76            case ALLEGRO_EVENT_TOUCH_CANCEL:
77            case ALLEGRO_EVENT_TOUCH_END:   type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;   break;
78            case ALLEGRO_EVENT_TOUCH_MOVE:  type = ALLEGRO_EVENT_MOUSE_AXES;        break;
79         }
80
81         event.mouse.type      = type;
82         event.mouse.timestamp = timestamp;
83         event.mouse.display   = (ALLEGRO_DISPLAY*)disp;
84         event.mouse.x         = (int)x;
85         event.mouse.y         = (int)y;
86         event.mouse.dx        = (int)dx;
87         event.mouse.dy        = (int)dy;
88         event.mouse.dz        = 0;
89         event.mouse.dw        = 0;
90         if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_5_0_x) {
91            event.mouse.button = 1;
92         }
93         else {
94            event.mouse.button = id;
95         }
96         event.mouse.pressure  = 1.0;
97
98         if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_5_0_x) {
99            al_set_mouse_xy(event.mouse.display, event.mouse.x, event.mouse.y);
100         }
101
102         _al_event_source_emit_event(&touch_input.mouse_emulation_es, &event);
103      }
104
105      mouse_state.x = (int)x;
106      mouse_state.y = (int)y;
107      if (type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
108         mouse_state.buttons |= id;
109      else if (type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
110         mouse_state.buttons &= ~id;
111
112      _al_event_source_unlock(&touch_input.mouse_emulation_es);
113   }
114}
115
116
117static bool init_touch_input(void)
118{
119   if (installed)
120      return false;
121
122   memset(&touch_input_state, 0, sizeof(touch_input_state));
123   memset(&mouse_state, 0, sizeof(mouse_state));
124
125   _al_event_source_init(&touch_input.es);
126   _al_event_source_init(&touch_input.mouse_emulation_es);
127   touch_input.mouse_emulation_mode = ALLEGRO_MOUSE_EMULATION_TRANSPARENT;
128
129   installed = true;
130
131   return true;
132}
133
134
135static void exit_touch_input(void)
136{
137   if (!installed)
138      return;
139
140   memset(&touch_input_state, 0, sizeof(touch_input_state));
141   memset(&mouse_state, 0, sizeof(mouse_state));
142
143   _al_event_source_free(&touch_input.es);
144   _al_event_source_free(&touch_input.mouse_emulation_es);
145
146   installed = false;
147}
148
149
150static ALLEGRO_TOUCH_INPUT* get_touch_input(void)
151{
152   return &touch_input;
153}
154
155
156static void get_touch_input_state(ALLEGRO_TOUCH_INPUT_STATE *ret_state)
157{
158   _al_event_source_lock(&touch_input.es);
159   *ret_state = touch_input_state;
160   _al_event_source_unlock(&touch_input.es);
161}
162
163
164static void set_mouse_emulation_mode(int mode)
165{
166   if (touch_input.mouse_emulation_mode != mode) {
167
168      int i;
169
170      for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i) {
171
172         ALLEGRO_TOUCH_STATE* touch = touch_input_state.touches + i;
173
174         if (touch->id > 0) {
175            _al_iphone_touch_input_handle_cancel(touch->id, al_get_time(),
176               touch->x, touch->y, touch->primary, touch->display);
177         }
178      }
179
180      touch_input.mouse_emulation_mode = mode;
181   }
182}
183
184
185static ALLEGRO_TOUCH_STATE* find_free_touch_state()
186{
187   int i;
188
189   for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i)
190      if (touch_input_state.touches[i].id <= 0)
191         return touch_input_state.touches + i;
192
193   return NULL;
194}
195
196
197static ALLEGRO_TOUCH_STATE* find_touch_state_with_id(int id)
198{
199   int i;
200
201   for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i)
202      if (touch_input_state.touches[i].id == id)
203         return touch_input_state.touches + i;
204
205   return NULL;
206}
207
208
209
210void _al_iphone_touch_input_handle_begin(int id, double timestamp, float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
211{
212   ALLEGRO_TOUCH_STATE* state = find_free_touch_state();
213   (void)primary;
214
215   if (NULL == state)
216      return;
217
218   _al_event_source_lock(&touch_input.es);
219   state->id      = id;
220   state->x       = x;
221   state->y       = y;
222   state->dx      = 0.0f;
223   state->dy      = 0.0f;
224   state->primary = primary;
225   state->display = disp;
226   _al_event_source_unlock(&touch_input.es);
227
228   generate_touch_input_event(ALLEGRO_EVENT_TOUCH_BEGIN, timestamp,
229      state->id, state->x, state->y, state->dx, state->dy, state->primary, disp);
230}
231
232
233void _al_iphone_touch_input_handle_end(int id, double timestamp, float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
234{
235   ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
236   (void)primary;
237
238   if (NULL == state)
239      return;
240
241   _al_event_source_lock(&touch_input.es);
242   state->dx      = x - state->x;
243   state->dy      = y - state->y;
244   state->x       = x;
245   state->y       = y;
246   _al_event_source_unlock(&touch_input.es);
247
248   generate_touch_input_event(ALLEGRO_EVENT_TOUCH_END, timestamp,
249      state->id, state->x, state->y, state->dx, state->dy, state->primary, disp);
250
251   _al_event_source_lock(&touch_input.es);
252   memset(state, 0, sizeof(ALLEGRO_TOUCH_STATE));
253   _al_event_source_unlock(&touch_input.es);
254}
255
256
257void _al_iphone_touch_input_handle_move(int id, double timestamp, float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
258{
259   ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
260   (void)primary;
261
262   if (NULL == state)
263      return;
264
265   _al_event_source_lock(&touch_input.es);
266   state->dx      = x - state->x;
267   state->dy      = y - state->y;
268   state->x       = x;
269   state->y       = y;
270   _al_event_source_unlock(&touch_input.es);
271
272   generate_touch_input_event(ALLEGRO_EVENT_TOUCH_MOVE, timestamp,
273      state->id, state->x, state->y, state->dx, state->dy, state->primary, disp);
274}
275
276
277void _al_iphone_touch_input_handle_cancel(int id, double timestamp, float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
278{
279   ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
280   (void)primary;
281
282   if (NULL == state)
283      return;
284
285   _al_event_source_lock(&touch_input.es);
286   state->dx      = x - state->x;
287   state->dy      = y - state->y;
288   state->x       = x;
289   state->y       = y;
290   _al_event_source_unlock(&touch_input.es);
291
292   generate_touch_input_event(ALLEGRO_EVENT_TOUCH_CANCEL, timestamp,
293      state->id, state->x, state->y, state->dx, state->dy, state->primary, disp);
294
295   _al_event_source_lock(&touch_input.es);
296   memset(state, 0, sizeof(ALLEGRO_TOUCH_STATE));
297   _al_event_source_unlock(&touch_input.es);
298}
299
300
301/* the driver vtable */
302#define TOUCH_INPUT_IPHONE AL_ID('I','T','I','D')
303
304static ALLEGRO_TOUCH_INPUT_DRIVER touch_input_driver =
305{
306   TOUCH_INPUT_IPHONE,
307   init_touch_input,
308   exit_touch_input,
309   get_touch_input,
310   get_touch_input_state,
311   set_mouse_emulation_mode,
312   NULL
313};
314
315ALLEGRO_TOUCH_INPUT_DRIVER *_al_get_iphone_touch_input_driver(void)
316{
317    return &touch_input_driver;
318}
319
320void imouse_get_state(ALLEGRO_MOUSE_STATE *ret_state)
321{
322   _al_event_source_lock(&touch_input.es);
323   *ret_state = mouse_state;
324   _al_event_source_unlock(&touch_input.es);
325}
326
327