1 /*         ______   ___    ___
2  *        /\  _  \ /\_ \  /\_ \
3  *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___
4  *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
5  *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
6  *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7  *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8  *                                           /\____/
9  *                                           \_/__/
10  *
11  *      Android family device touch input driver.
12  *
13  *      By Thomas Fjellstrom.
14  *
15  *      Based on the iOS touch input driver by Michał Cichoń.
16  *
17  *      See readme.txt for copyright information.
18  */
19 
20 #include "allegro5/allegro.h"
21 #include "allegro5/internal/aintern_android.h"
22 #include "allegro5/internal/aintern_display.h"
23 #include "allegro5/internal/aintern_touch_input.h"
24 
25 ALLEGRO_DEBUG_CHANNEL("android")
26 
27 
28 /* forward declaration */
29 static void android_touch_input_handle_cancel(int id, double timestamp,
30    float x, float y, bool primary, ALLEGRO_DISPLAY *disp);
31 
32 
33 static ALLEGRO_TOUCH_INPUT_STATE touch_input_state;
34 static ALLEGRO_MOUSE_STATE mouse_state;
35 static ALLEGRO_TOUCH_INPUT touch_input;
36 static bool installed = false;
37 
38 
reset_touch_input_state(void)39 static void reset_touch_input_state(void)
40 {
41    int i;
42 
43    for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; i++) {
44       touch_input_state.touches[i].id = -1;
45    }
46 }
47 
48 
generate_touch_input_event(unsigned int type,double timestamp,int id,float x,float y,float dx,float dy,bool primary,ALLEGRO_DISPLAY * disp)49 static void generate_touch_input_event(unsigned int type, double timestamp,
50    int id, float x, float y, float dx, float dy, bool primary,
51    ALLEGRO_DISPLAY *disp)
52 {
53    ALLEGRO_EVENT event;
54 
55    bool want_touch_event           = _al_event_source_needs_to_generate_event(&touch_input.es);
56    bool want_mouse_emulation_event;
57 
58    if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_5_0_x) {
59       want_mouse_emulation_event = _al_event_source_needs_to_generate_event(&touch_input.mouse_emulation_es) && al_is_mouse_installed();
60    }
61    else {
62       want_mouse_emulation_event = _al_event_source_needs_to_generate_event(&touch_input.mouse_emulation_es) && primary && al_is_mouse_installed();
63    }
64 
65    if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_NONE)
66       want_mouse_emulation_event = false;
67    else if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_INCLUSIVE)
68       want_touch_event = al_is_mouse_installed() ? (want_touch_event && !primary) : want_touch_event;
69    else if (touch_input.mouse_emulation_mode == ALLEGRO_MOUSE_EMULATION_EXCLUSIVE)
70       want_touch_event = al_is_mouse_installed() ? false : want_touch_event;
71 
72 
73    if (!want_touch_event && !want_mouse_emulation_event)
74       return;
75 
76    if (want_touch_event) {
77 
78       event.touch.type      = type;
79       event.touch.display   = (ALLEGRO_DISPLAY*)disp;
80       event.touch.timestamp = timestamp;
81       event.touch.id        = id;
82       event.touch.x         = x;
83       event.touch.y         = y;
84       event.touch.dx        = dx;
85       event.touch.dy        = dy;
86       event.touch.primary   = primary;
87 
88       _al_event_source_lock(&touch_input.es);
89       _al_event_source_emit_event(&touch_input.es, &event);
90       _al_event_source_unlock(&touch_input.es);
91    }
92 
93    if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_NONE) {
94       mouse_state.x = (int)x;
95       mouse_state.y = (int)y;
96       if (type == ALLEGRO_EVENT_TOUCH_BEGIN)
97          mouse_state.buttons++;
98       else if (type == ALLEGRO_EVENT_TOUCH_END)
99          mouse_state.buttons--;
100 
101       mouse_state.pressure = mouse_state.buttons ? 1.0 : 0.0; /* TODO */
102 
103       _al_event_source_lock(&touch_input.mouse_emulation_es);
104       if (want_mouse_emulation_event) {
105 
106          switch (type) {
107             case ALLEGRO_EVENT_TOUCH_BEGIN: type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN; break;
108             case ALLEGRO_EVENT_TOUCH_CANCEL:
109             case ALLEGRO_EVENT_TOUCH_END:   type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;   break;
110             case ALLEGRO_EVENT_TOUCH_MOVE:  type = ALLEGRO_EVENT_MOUSE_AXES;        break;
111          }
112 
113          event.mouse.type      = type;
114          event.mouse.timestamp = timestamp;
115          event.mouse.display   = (ALLEGRO_DISPLAY*)disp;
116          event.mouse.x         = (int)x;
117          event.mouse.y         = (int)y;
118          event.mouse.dx        = (int)dx;
119          event.mouse.dy        = (int)dy;
120          event.mouse.dz        = 0;
121          event.mouse.dw        = 0;
122          if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_5_0_x) {
123             event.mouse.button = 1;
124          }
125          else {
126             event.mouse.button = id;
127          }
128          event.mouse.pressure  = mouse_state.pressure;
129 
130          if (touch_input.mouse_emulation_mode != ALLEGRO_MOUSE_EMULATION_5_0_x) {
131             al_set_mouse_xy(event.mouse.display, event.mouse.x, event.mouse.y);
132          }
133 
134          _al_event_source_emit_event(&touch_input.mouse_emulation_es, &event);
135       }
136       _al_event_source_unlock(&touch_input.mouse_emulation_es);
137    }
138 }
139 
140 
init_touch_input(void)141 static bool init_touch_input(void)
142 {
143    if (installed)
144       return false;
145 
146    reset_touch_input_state();
147    memset(&mouse_state, 0, sizeof(mouse_state));
148 
149    _al_event_source_init(&touch_input.es);
150    _al_event_source_init(&touch_input.mouse_emulation_es);
151    touch_input.mouse_emulation_mode = ALLEGRO_MOUSE_EMULATION_TRANSPARENT;
152 
153    installed = true;
154 
155    return true;
156 }
157 
158 
exit_touch_input(void)159 static void exit_touch_input(void)
160 {
161    if (!installed)
162       return;
163 
164    reset_touch_input_state();
165    memset(&mouse_state, 0, sizeof(mouse_state));
166 
167    _al_event_source_free(&touch_input.es);
168    _al_event_source_free(&touch_input.mouse_emulation_es);
169 
170    installed = false;
171 }
172 
173 
get_touch_input(void)174 static ALLEGRO_TOUCH_INPUT* get_touch_input(void)
175 {
176    return &touch_input;
177 }
178 
179 
get_touch_input_state(ALLEGRO_TOUCH_INPUT_STATE * ret_state)180 static void get_touch_input_state(ALLEGRO_TOUCH_INPUT_STATE *ret_state)
181 {
182    _al_event_source_lock(&touch_input.es);
183    *ret_state = touch_input_state;
184    _al_event_source_unlock(&touch_input.es);
185 }
186 
187 
set_mouse_emulation_mode(int mode)188 static void set_mouse_emulation_mode(int mode)
189 {
190    if (touch_input.mouse_emulation_mode != mode) {
191 
192       int i;
193 
194       for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i) {
195 
196          ALLEGRO_TOUCH_STATE* touch = touch_input_state.touches + i;
197 
198          if (touch->id > 0) {
199             android_touch_input_handle_cancel(touch->id, al_get_time(),
200                touch->x, touch->y, touch->primary, touch->display);
201          }
202       }
203 
204       touch_input.mouse_emulation_mode = mode;
205    }
206 }
207 
208 
find_free_touch_state(void)209 static ALLEGRO_TOUCH_STATE* find_free_touch_state(void)
210 {
211    int i;
212 
213    for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i)
214       if (touch_input_state.touches[i].id < 0)
215          return touch_input_state.touches + i;
216 
217    return NULL;
218 }
219 
220 
find_touch_state_with_id(int id)221 static ALLEGRO_TOUCH_STATE* find_touch_state_with_id(int id)
222 {
223    int i;
224 
225    for (i = 0; i < ALLEGRO_TOUCH_INPUT_MAX_TOUCH_COUNT; ++i)
226       if (touch_input_state.touches[i].id == id)
227          return touch_input_state.touches + i;
228 
229    return NULL;
230 }
231 
232 
android_touch_input_handle_begin(int id,double timestamp,float x,float y,bool primary,ALLEGRO_DISPLAY * disp)233 static void android_touch_input_handle_begin(int id, double timestamp,
234    float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
235 {
236    ALLEGRO_TOUCH_STATE* state = find_free_touch_state();
237    (void)primary;
238 
239    if (NULL == state)
240       return;
241 
242    _al_event_source_lock(&touch_input.es);
243    state->id      = id;
244    state->x       = x;
245    state->y       = y;
246    state->dx      = 0.0f;
247    state->dy      = 0.0f;
248    state->primary = primary;
249    state->display = disp;
250    _al_event_source_unlock(&touch_input.es);
251 
252    generate_touch_input_event(ALLEGRO_EVENT_TOUCH_BEGIN, timestamp,
253       state->id, state->x, state->y, state->dx, state->dy, state->primary,
254       disp);
255 }
256 
257 
android_touch_input_handle_end(int id,double timestamp,float x,float y,bool primary,ALLEGRO_DISPLAY * disp)258 static void android_touch_input_handle_end(int id, double timestamp,
259    float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
260 {
261    ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
262    (void)primary;
263 
264    if (NULL == state)
265       return;
266 
267    _al_event_source_lock(&touch_input.es);
268    state->dx      = x - state->x;
269    state->dy      = y - state->y;
270    state->x       = x;
271    state->y       = y;
272    _al_event_source_unlock(&touch_input.es);
273 
274    generate_touch_input_event(ALLEGRO_EVENT_TOUCH_END, timestamp,
275       state->id, state->x, state->y, state->dx, state->dy, state->primary,
276       disp);
277 
278    _al_event_source_lock(&touch_input.es);
279    state->id = -1;
280    _al_event_source_unlock(&touch_input.es);
281 }
282 
283 
android_touch_input_handle_move(int id,double timestamp,float x,float y,bool primary,ALLEGRO_DISPLAY * disp)284 static void android_touch_input_handle_move(int id, double timestamp,
285    float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
286 {
287    ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
288    (void)primary;
289 
290    if (NULL == state)
291       return;
292 
293    _al_event_source_lock(&touch_input.es);
294    state->dx      = x - state->x;
295    state->dy      = y - state->y;
296    state->x       = x;
297    state->y       = y;
298    _al_event_source_unlock(&touch_input.es);
299 
300    generate_touch_input_event(ALLEGRO_EVENT_TOUCH_MOVE, timestamp,
301       state->id, state->x, state->y, state->dx, state->dy, state->primary,
302       disp);
303 }
304 
305 
android_touch_input_handle_cancel(int id,double timestamp,float x,float y,bool primary,ALLEGRO_DISPLAY * disp)306 static void android_touch_input_handle_cancel(int id, double timestamp,
307    float x, float y, bool primary, ALLEGRO_DISPLAY *disp)
308 {
309    ALLEGRO_TOUCH_STATE* state = find_touch_state_with_id(id);
310    (void)primary;
311 
312    if (NULL == state)
313       return;
314 
315    _al_event_source_lock(&touch_input.es);
316    state->dx      = x - state->x;
317    state->dy      = y - state->y;
318    state->x       = x;
319    state->y       = y;
320    _al_event_source_unlock(&touch_input.es);
321 
322    generate_touch_input_event(ALLEGRO_EVENT_TOUCH_CANCEL, timestamp,
323       state->id, state->x, state->y, state->dx, state->dy, state->primary, disp);
324 
325    _al_event_source_lock(&touch_input.es);
326    state->id = -1;
327    _al_event_source_unlock(&touch_input.es);
328 }
329 
330 
331 JNI_FUNC(void, TouchListener, nativeOnTouch, (JNIEnv *env, jobject obj,
332    jint id, jint action, jfloat x, jfloat y, jboolean primary))
333 {
334    (void)env;
335    (void)obj;
336 
337    ALLEGRO_SYSTEM *system = al_get_system_driver();
338    ASSERT(system != NULL);
339 
340    ALLEGRO_DISPLAY **dptr = _al_vector_ref(&system->displays, 0);
341    ALLEGRO_DISPLAY *display = *dptr;
342    ASSERT(display != NULL);
343 
344    switch (action) {
345       case ALLEGRO_EVENT_TOUCH_BEGIN:
346          android_touch_input_handle_begin(id, al_get_time(), x, y, primary,
347             display);
348          break;
349 
350       case ALLEGRO_EVENT_TOUCH_END:
351          android_touch_input_handle_end(id, al_get_time(), x, y, primary,
352             display);
353          break;
354 
355       case ALLEGRO_EVENT_TOUCH_MOVE:
356          android_touch_input_handle_move(id, al_get_time(), x, y, primary,
357             display);
358          break;
359 
360       case ALLEGRO_EVENT_TOUCH_CANCEL:
361          android_touch_input_handle_cancel(id, al_get_time(), x, y,
362             primary, display);
363          break;
364 
365       default:
366          ALLEGRO_ERROR("unknown touch action: %i", action);
367          break;
368    }
369 }
370 
371 
372 /* the driver vtable */
373 #define TOUCH_INPUT_ANDROID AL_ID('A','T','I','D')
374 
375 static ALLEGRO_TOUCH_INPUT_DRIVER touch_input_driver =
376 {
377    TOUCH_INPUT_ANDROID,
378    init_touch_input,
379    exit_touch_input,
380    get_touch_input,
381    get_touch_input_state,
382    set_mouse_emulation_mode,
383    NULL
384 };
385 
386 
_al_get_android_touch_input_driver(void)387 ALLEGRO_TOUCH_INPUT_DRIVER *_al_get_android_touch_input_driver(void)
388 {
389     return &touch_input_driver;
390 }
391 
392 
_al_android_mouse_get_state(ALLEGRO_MOUSE_STATE * ret_state)393 void _al_android_mouse_get_state(ALLEGRO_MOUSE_STATE *ret_state)
394 {
395    _al_event_source_lock(&touch_input.es);
396    *ret_state = mouse_state;
397    _al_event_source_unlock(&touch_input.es);
398 }
399 
400 
401 /* vim: set sts=3 sw=3 et: */
402