1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "qemu-common.h"
18#include "android/user-events.h"
19#include "android/display-core.h"
20#include "android/hw-events.h"
21#include "android/charmap.h"
22#include "android/globals.h"  /* for android_hw */
23#include "android/utils/misc.h"
24#include "android/utils/debug.h"
25#include "android/multitouch-screen.h"
26
27#define  E(...)    derror(__VA_ARGS__)
28#define  W(...)    dwarning(__VA_ARGS__)
29#define  D(...)    VERBOSE_PRINT(mtscreen,__VA_ARGS__)
30#define  D_ACTIVE  VERBOSE_CHECK(mtscreen)
31
32#define TRACE_ON    1
33
34#if TRACE_ON
35#define  T(...)    VERBOSE_PRINT(mtscreen,__VA_ARGS__)
36#else
37#define  T(...)
38#endif
39
40
41/* Maximum number of pointers, supported by multi-touch emulation. */
42#define MTS_POINTERS_NUM    10
43/* Signals that pointer is not tracked (or is "up"). */
44#define MTS_POINTER_UP      -1
45/* Special tracking ID for a mouse pointer. */
46#define MTS_POINTER_MOUSE   -2
47
48/* Describes state of a multi-touch pointer  */
49typedef struct MTSPointerState {
50    /* Tracking ID assigned to the pointer by an app emulating multi-touch. */
51    int tracking_id;
52    /* X - coordinate of the tracked pointer. */
53    int x;
54    /* Y - coordinate of the tracked pointer. */
55    int y;
56    /* Current pressure value. */
57    int pressure;
58} MTSPointerState;
59
60/* Describes state of an emulated multi-touch screen. */
61typedef struct MTSState {
62    /* Multi-touch port connected to the device. */
63    AndroidMTSPort* mtsp;
64    /* Emulator's display state. */
65    DisplayState*   ds;
66    /* Number of tracked pointers. */
67    int             tracked_ptr_num;
68    /* Index in the 'tracked_pointers' array of the last pointer for which
69     * ABS_MT_SLOT was sent. -1 indicates that no slot selection has been made
70     * yet. */
71    int             current_slot;
72    /* Accumulator for ABS_TOUCH_MAJOR value. */
73    int             touch_major;
74    /* Array of multi-touch pointers. */
75    MTSPointerState tracked_pointers[MTS_POINTERS_NUM];
76    /* Header collecting framebuffer information and updates. */
77    MTFrameHeader   fb_header;
78    /* Boolean value indicating if framebuffer updates are currently being
79     * transferred to the application running on the device. */
80    int             fb_transfer_in_progress;
81    /* Indicates direction in which lines are arranged in the framebuffer. If
82     * this value is negative, lines are arranged in bottom-up format (i.e. the
83     * bottom line is at the beginning of the buffer). */
84    int             ydir;
85    /* Current framebuffer pointer. */
86    uint8_t*        current_fb;
87} MTSState;
88
89/* Default multi-touch screen descriptor */
90static MTSState _MTSState = { 0 };
91
92/* Pushes event to the event device. */
93static void
94_push_event(int type, int code, int value)
95{
96    user_event_generic(type, code, value);
97}
98
99/* Gets an index in the MTS's tracking pointers array MTS for the given
100 * tracking id.
101 * Return:
102 *  Index of a matching entry in the MTS's tracking pointers array, or -1 if
103 *  matching entry was not found.
104 */
105static int
106_mtsstate_get_pointer_index(const MTSState* mts_state, int tracking_id)
107{
108    int index;
109    for (index = 0; index < MTS_POINTERS_NUM; index++) {
110        if (mts_state->tracked_pointers[index].tracking_id == tracking_id) {
111            return index;
112        }
113    }
114    return -1;
115}
116
117/* Gets an index of the first untracking pointer in the MTS's tracking pointers
118 * array.
119 * Return:
120 *  An index of the first untracking pointer, or -1 if all pointers are tracked.
121 */
122static int
123_mtsstate_get_available_pointer_index(const MTSState* mts_state)
124{
125    return _mtsstate_get_pointer_index(mts_state, MTS_POINTER_UP);
126}
127
128/* Handles a "pointer down" event
129 * Param:
130 *  mts_state - MTS state descriptor.
131 *  tracking_id - Tracking ID of the "downed" pointer.
132 *  x, y - "Downed" pointer coordinates,
133 *  pressure - Pressure value for the pointer.
134 */
135static void
136_mts_pointer_down(MTSState* mts_state, int tracking_id, int x, int y, int pressure)
137{
138    /* Get first available slot for the new pointer. */
139    const int slot_index = _mtsstate_get_available_pointer_index(mts_state);
140
141    /* Make sure there is a place for the pointer. */
142    if (slot_index >= 0) {
143        /* Initialize pointer's entry. */
144        mts_state->tracked_ptr_num++;
145        mts_state->tracked_pointers[slot_index].tracking_id = tracking_id;
146        mts_state->tracked_pointers[slot_index].x = x;
147        mts_state->tracked_pointers[slot_index].y = y;
148        mts_state->tracked_pointers[slot_index].pressure = pressure;
149
150        /* Send events indicating a "pointer down" to the EventHub */
151        /* Make sure that correct slot is selected. */
152        if (slot_index != mts_state->current_slot) {
153            _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
154        }
155        _push_event(EV_ABS, ABS_MT_TRACKING_ID, slot_index);
156        _push_event(EV_ABS, ABS_MT_TOUCH_MAJOR, ++mts_state->touch_major);
157        _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
158        _push_event(EV_ABS, ABS_MT_POSITION_X, x);
159        _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
160        _push_event(EV_SYN, SYN_REPORT, 0);
161        mts_state->current_slot = slot_index;
162    } else {
163        D("MTS pointer count is exceeded.");
164        return;
165    }
166}
167
168/* Handles a "pointer up" event
169 * Param:
170 *  mts_state - MTS state descriptor.
171 *  slot_index - Pointer's index in the MTS's array of tracked pointers.
172 */
173static void
174_mts_pointer_up(MTSState* mts_state, int slot_index)
175{
176    /* Make sure that correct slot is selected. */
177    if (slot_index != mts_state->current_slot) {
178        _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
179    }
180
181    /* Send event indicating "pointer up" to the EventHub. */
182    _push_event(EV_ABS, ABS_MT_TRACKING_ID, -1);
183    _push_event(EV_SYN, SYN_REPORT, 0);
184
185    /* Update MTS descriptor, removing the tracked pointer. */
186    mts_state->tracked_pointers[slot_index].tracking_id = MTS_POINTER_UP;
187    mts_state->tracked_pointers[slot_index].x = 0;
188    mts_state->tracked_pointers[slot_index].y = 0;
189    mts_state->tracked_pointers[slot_index].pressure = 0;
190
191    /* Since current slot is no longer tracked, make sure we will do a "select"
192     * next time we send events to the EventHub. */
193    mts_state->current_slot = -1;
194    mts_state->tracked_ptr_num--;
195    assert(mts_state->tracked_ptr_num >= 0);
196}
197
198/* Handles a "pointer move" event
199 * Param:
200 *  mts_state - MTS state descriptor.
201 *  slot_index - Pointer's index in the MTS's array of tracked pointers.
202 *  x, y - New pointer coordinates,
203 *  pressure - Pressure value for the pointer.
204 */
205static void
206_mts_pointer_move(MTSState* mts_state, int slot_index, int x, int y, int pressure)
207{
208    MTSPointerState* ptr_state = &mts_state->tracked_pointers[slot_index];
209
210    /* Make sure that coordinates have really changed. */
211    if (ptr_state->x == x && ptr_state->y == y) {
212        /* Coordinates didn't change. Bail out. */
213        return;
214    }
215
216    /* Make sure that the right slot is selected. */
217    if (slot_index != mts_state->current_slot) {
218        _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
219        mts_state->current_slot = slot_index;
220    }
221
222    /* Push the changes down. */
223    if (ptr_state->pressure != pressure && pressure != 0) {
224        _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
225        ptr_state->pressure = pressure;
226    }
227    if (ptr_state->x != x) {
228        _push_event(EV_ABS, ABS_MT_POSITION_X, x);
229        ptr_state->x = x;
230    }
231    if (ptr_state->y != y) {
232        _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
233        ptr_state->y = y;
234    }
235    _push_event(EV_SYN, SYN_REPORT, 0);
236}
237
238/********************************************************************************
239 *                       Multi-touch API
240 *******************************************************************************/
241
242/* Multi-touch service initialization flag. */
243static int _is_mt_initialized = 0;
244
245/* Callback that is invoked when framebuffer update has been transmitted to the
246 * device. */
247static AsyncIOAction
248_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status)
249{
250    MTSState* const mts_state = (MTSState*)opaque;
251
252    if (status == ASIO_STATE_SUCCEEDED) {
253        /* Lets see if we have accumulated more changes while transmission has been
254         * in progress. */
255        if (mts_state->fb_header.w && mts_state->fb_header.h &&
256            !mts_state->fb_transfer_in_progress) {
257            mts_state->fb_transfer_in_progress = 1;
258            /* Send accumulated updates. */
259            if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
260                                    mts_state->current_fb, _on_fb_sent, mts_state,
261                                    mts_state->ydir)) {
262                mts_state->fb_transfer_in_progress = 0;
263            }
264        }
265    }
266
267    return ASIO_ACTION_DONE;
268}
269
270/* Common handler for framebuffer updates invoked by both, software, and OpenGLES
271 * renderers.
272 */
273static void
274_mt_fb_common_update(MTSState* mts_state, int x, int y, int w, int h)
275{
276    if (mts_state->fb_header.w == 0 && mts_state->fb_header.h == 0) {
277        /* First update after previous one has been transmitted to the device. */
278        mts_state->fb_header.x = x;
279        mts_state->fb_header.y = y;
280        mts_state->fb_header.w = w;
281        mts_state->fb_header.h = h;
282    } else {
283        /*
284         * Accumulate framebuffer changes in the header.
285         */
286
287        /* "right" and "bottom" coordinates of the current update. */
288        int right = mts_state->fb_header.x + mts_state->fb_header.w;
289        int bottom = mts_state->fb_header.y + mts_state->fb_header.h;
290
291        /* "right" and "bottom" coordinates of the new update. */
292        const int new_right = x + w;
293        const int new_bottom = y + h;
294
295        /* Accumulate changed rectangle coordinates in the header. */
296        if (mts_state->fb_header.x > x) {
297            mts_state->fb_header.x = x;
298        }
299        if (mts_state->fb_header.y > y) {
300            mts_state->fb_header.y = y;
301        }
302        if (right < new_right) {
303            right = new_right;
304        }
305        if (bottom < new_bottom) {
306            bottom = new_bottom;
307        }
308        mts_state->fb_header.w = right - mts_state->fb_header.x;
309        mts_state->fb_header.h = bottom - mts_state->fb_header.y;
310    }
311
312    /* We will send updates to the device only after previous transmission is
313     * completed. */
314    if (!mts_state->fb_transfer_in_progress) {
315        mts_state->fb_transfer_in_progress = 1;
316        if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
317                                mts_state->current_fb, _on_fb_sent, mts_state,
318                                mts_state->ydir)) {
319            mts_state->fb_transfer_in_progress = 0;
320        }
321    }
322}
323
324/* A callback invoked on framebuffer updates by software renderer.
325 * Param:
326 *  opaque - MTSState instance.
327 *  x, y, w, h - Defines an updated rectangle inside the framebuffer.
328 */
329static void
330_mt_fb_update(void* opaque, int x, int y, int w, int h)
331{
332    MTSState* const mts_state = (MTSState*)opaque;
333    const DisplaySurface* const surface = mts_state->ds->surface;
334
335    T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d",
336      x, y, w, h);
337
338    /* TODO: For sofware renderer general framebuffer properties can change on
339     * the fly. Find a callback that can catch that. For now, just copy FB
340     * properties over in every FB update. */
341    mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
342    mts_state->fb_header.bpl = surface->linesize;
343    mts_state->fb_header.disp_width = surface->width;
344    mts_state->fb_header.disp_height = surface->height;
345    mts_state->current_fb = surface->data;
346    mts_state->ydir = 1;
347
348    _mt_fb_common_update(mts_state, x, y, w, h);
349}
350
351void
352multitouch_opengles_fb_update(void* context,
353                              int w, int h, int ydir,
354                              int format, int type,
355                              unsigned char* pixels)
356{
357    MTSState* const mts_state = &_MTSState;
358
359    /* Make sure MT port is initialized. */
360    if (!_is_mt_initialized) {
361        return;
362    }
363
364    T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h);
365
366    /* GLES format is always RGBA8888 */
367    mts_state->fb_header.bpp = 4;
368    mts_state->fb_header.bpl = 4 * w;
369    mts_state->fb_header.disp_width = w;
370    mts_state->fb_header.disp_height = h;
371    mts_state->current_fb = pixels;
372    mts_state->ydir = ydir;
373
374    /* GLES emulator alwas update the entire framebuffer. */
375    _mt_fb_common_update(mts_state, 0, 0, w, h);
376}
377
378void
379multitouch_refresh_screen(void)
380{
381    MTSState* const mts_state = &_MTSState;
382
383    /* Make sure MT port is initialized. */
384    if (!_is_mt_initialized) {
385        return;
386    }
387
388    /* Lets see if any updates have been received so far. */
389    if (NULL != mts_state->current_fb) {
390        _mt_fb_common_update(mts_state, 0, 0, mts_state->fb_header.disp_width,
391                             mts_state->fb_header.disp_height);
392    }
393}
394
395void
396multitouch_fb_updated(void)
397{
398    MTSState* const mts_state = &_MTSState;
399
400    /* This concludes framebuffer update. */
401    mts_state->fb_transfer_in_progress = 0;
402
403    /* Lets see if we have accumulated more changes while transmission has been
404     * in progress. */
405    if (mts_state->fb_header.w && mts_state->fb_header.h) {
406        mts_state->fb_transfer_in_progress = 1;
407        /* Send accumulated updates. */
408        if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
409                                mts_state->current_fb, _on_fb_sent, mts_state,
410                                mts_state->ydir)) {
411            mts_state->fb_transfer_in_progress = 0;
412        }
413    }
414}
415
416void
417multitouch_init(AndroidMTSPort* mtsp)
418{
419    if (!_is_mt_initialized) {
420        MTSState* const mts_state = &_MTSState;
421        DisplayState* const ds = get_displaystate();
422        DisplayUpdateListener* dul;
423        int index;
424
425        /*
426         * Initialize the descriptor.
427         */
428
429        memset(mts_state, 0, sizeof(MTSState));
430        mts_state->tracked_ptr_num = 0;
431        mts_state->current_slot = -1;
432        for (index = 0; index < MTS_POINTERS_NUM; index++) {
433            mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP;
434        }
435        mts_state->mtsp = mtsp;
436        mts_state->fb_header.header_size = sizeof(MTFrameHeader);
437        mts_state->fb_transfer_in_progress = 0;
438
439        /*
440         * Set framebuffer update listener.
441         */
442
443        ANEW0(dul);
444        dul->opaque = &_MTSState;
445        dul->dpy_update = _mt_fb_update;
446
447        /* Initialize framebuffer information in the screen descriptor. */
448        mts_state->ds = ds;
449        mts_state->fb_header.disp_width = ds->surface->width;
450        mts_state->fb_header.disp_height = ds->surface->height;
451        mts_state->fb_header.x = mts_state->fb_header.y = 0;
452        mts_state->fb_header.w = mts_state->fb_header.h = 0;
453        mts_state->fb_header.bpp = ds->surface->pf.bytes_per_pixel;
454        mts_state->fb_header.bpl = ds->surface->linesize;
455        mts_state->fb_transfer_in_progress = 0;
456
457        register_displayupdatelistener(ds, dul);
458
459        _is_mt_initialized = 1;
460    }
461}
462
463void
464multitouch_update_pointer(MTESource source,
465                          int tracking_id,
466                          int x,
467                          int y,
468                          int pressure)
469{
470    MTSState* const mts_state = &_MTSState;
471
472    /* Assign a fixed tracking ID to the mouse pointer. */
473    if (source == MTES_MOUSE) {
474        tracking_id = MTS_POINTER_MOUSE;
475    }
476
477    /* Find the tracked pointer for the tracking ID. */
478    const int slot_index = _mtsstate_get_pointer_index(mts_state, tracking_id);
479    if (slot_index < 0) {
480        /* This is the first time the pointer is seen. Must be "pressed",
481         * otherwise it's "hoovering", which we don't support yet. */
482        if (pressure == 0) {
483            if (tracking_id != MTS_POINTER_MOUSE) {
484                D("Unexpected MTS pointer update for tracking id: %d",
485                   tracking_id);
486            }
487            return;
488        }
489
490        /* This is a "pointer down" event */
491        _mts_pointer_down(mts_state, tracking_id, x, y, pressure);
492    } else if (pressure == 0) {
493        /* This is a "pointer up" event */
494        _mts_pointer_up(mts_state, slot_index);
495    } else {
496        /* This is a "pointer move" event */
497        _mts_pointer_move(mts_state, slot_index, x, y, pressure);
498    }
499}
500
501int
502multitouch_get_max_slot()
503{
504    return MTS_POINTERS_NUM - 1;
505}
506