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 "utils/panic.h"
19#include "android/hw-events.h"
20#include "android/charmap.h"
21#include "android/multitouch-screen.h"
22#include "android/multitouch-port.h"
23#include "android/globals.h"  /* for android_hw */
24#include "android/utils/misc.h"
25#include "android/utils/jpeg-compress.h"
26
27#define  E(...)    derror(__VA_ARGS__)
28#define  W(...)    dwarning(__VA_ARGS__)
29#define  D(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
30#define  D_ACTIVE  VERBOSE_CHECK(mtport)
31
32/* Query timeout in milliseconds. */
33#define MTSP_QUERY_TIMEOUT       3000
34#define MTSP_MAX_MSG             2048
35#define MTSP_MAX_EVENT           2048
36
37/* Multi-touch port descriptor. */
38struct AndroidMTSPort {
39    /* Caller identifier. */
40    void*           opaque;
41    /* Connected android device. */
42    AndroidDevice*  device;
43    /* Initialized JPEG compressor instance. */
44    AJPEGDesc*      jpeg_compressor;
45    /* Connection status: 1 connected, 0 - disconnected. */
46    int             is_connected;
47    /* Buffer where to receive multitouch messages. */
48    char            mts_msg[MTSP_MAX_MSG];
49    /* Buffer where to receive multitouch events. */
50    char            events[MTSP_MAX_EVENT];
51};
52
53/* Destroys and frees the descriptor. */
54static void
55_mts_port_free(AndroidMTSPort* mtsp)
56{
57    if (mtsp != NULL) {
58        if (mtsp->jpeg_compressor != NULL) {
59            jpeg_compressor_destroy(mtsp->jpeg_compressor);
60        }
61        if (mtsp->device != NULL) {
62            android_device_destroy(mtsp->device);
63        }
64        AFREE(mtsp);
65    }
66}
67
68/********************************************************************************
69 *                          Multi-touch action handlers
70 *******************************************************************************/
71
72/*
73 * Although there are a lot of similarities in the way the handlers below are
74 * implemented, for the sake of tracing / debugging it's better to have a
75 * separate handler for each distinctive action.
76 */
77
78/* First pointer down event handler. */
79static void
80_on_action_down(int tracking_id, int x, int y, int pressure)
81{
82    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
83}
84
85/* Last pointer up event handler. */
86static void
87_on_action_up(int tracking_id)
88{
89    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
90}
91
92/* Pointer down event handler. */
93static void
94_on_action_pointer_down(int tracking_id, int x, int y, int pressure)
95{
96    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
97}
98
99/* Pointer up event handler. */
100static void
101_on_action_pointer_up(int tracking_id)
102{
103    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
104}
105
106/* Pointer move event handler. */
107static void
108_on_action_move(int tracking_id, int x, int y, int pressure)
109{
110    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
111}
112
113/********************************************************************************
114 *                          Multi-touch event handlers
115 *******************************************************************************/
116
117/* Handles "pointer move" event. */
118static void
119_on_move(const char* param)
120{
121    const char* pid = param;
122    D(">>> MOVE: %s", param);
123    while (pid && *pid) {
124        int pid_val, x, y, pressure = 0;
125        if (!get_token_value_int(pid, "pid", &pid_val) &&
126            !get_token_value_int(pid, "x", &x) &&
127            !get_token_value_int(pid, "y", &y)) {
128            get_token_value_int(pid, "pressure", &pressure);
129            _on_action_move(pid_val, x, y, pressure);
130            pid = strstr(pid + 1, "pid");
131        } else {
132            break;
133        }
134    }
135}
136
137/* Handles "first pointer down" event. */
138static void
139_on_down(const char* param)
140{
141    int pid_val, x, y, pressure = 0;
142    D(">>> 1-ST DOWN: %s", param);
143    if (!get_token_value_int(param, "pid", &pid_val) &&
144        !get_token_value_int(param, "x", &x) &&
145        !get_token_value_int(param, "y", &y)) {
146        get_token_value_int(param, "pressure", &pressure);
147        _on_action_down(pid_val, x, y, pressure);
148    } else {
149        W("Invalid parameters '%s' for MTS 'down' event", param);
150    }
151}
152
153/* Handles "last pointer up" event. */
154static void
155_on_up(const char* param)
156{
157    int pid_val;
158    D(">>> LAST UP: %s", param);
159    if (!get_token_value_int(param, "pid", &pid_val)) {
160        _on_action_up(pid_val);
161    } else {
162        W("Invalid parameters '%s' for MTS 'up' event", param);
163    }
164}
165
166/* Handles "next pointer down" event. */
167static void
168_on_pdown(const char* param)
169{
170    int pid_val, x, y, pressure = 0;
171    D(">>> DOWN: %s", param);
172    if (!get_token_value_int(param, "pid", &pid_val) &&
173        !get_token_value_int(param, "x", &x) &&
174        !get_token_value_int(param, "y", &y)) {
175        get_token_value_int(param, "pressure", &pressure);
176        _on_action_pointer_down(pid_val, x, y, pressure);
177    } else {
178        W("Invalid parameters '%s' for MTS 'pointer down' event", param);
179    }
180}
181
182/* Handles "next pointer up" event. */
183static void
184_on_pup(const char* param)
185{
186    int pid_val;
187    D(">>> UP: %s", param);
188    if (!get_token_value_int(param, "pid", &pid_val)) {
189        _on_action_pointer_up(pid_val);
190    } else {
191        W("Invalid parameters '%s' for MTS 'up' event", param);
192    }
193}
194
195/********************************************************************************
196 *                      Device communication callbacks
197 *******************************************************************************/
198
199/* Main event handler.
200 * This routine is invoked when an event message has been received from the
201 * device.
202 */
203static void
204_on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
205{
206    char* action;
207    int res;
208    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
209
210    if (errno) {
211        D("Multi-touch notification has failed: %s", strerror(errno));
212        return;
213    }
214
215    /* Dispatch the event to an appropriate handler. */
216    res = get_token_value_alloc(msg, "action", &action);
217    if (!res) {
218        const char* param = strchr(msg, ' ');
219        if (param) {
220            param++;
221        }
222        if (!strcmp(action, "move")) {
223            _on_move(param);
224        } else if (!strcmp(action, "down")) {
225            _on_down(param);
226        } else if (!strcmp(action, "up")) {
227            _on_up(param);
228        } else if (!strcmp(action, "pdown")) {
229            _on_pdown(param);
230        } else if (!strcmp(action, "pup")) {
231            _on_pup(param);
232        } else {
233            D("Unknown multi-touch event action '%s'", action);
234        }
235        free(action);
236    }
237
238    /* Listen to the next event. */
239    android_device_listen(ad, mtsp->events, sizeof(mtsp->events),
240                          _on_event_received);
241}
242
243/* A callback that is invoked when android device is connected (i.e. both,
244 * command and event channels have been established).
245 * Param:
246 *  opaque - AndroidMTSPort instance.
247 *  ad - Android device used by this port.
248 *  failure - Connections status.
249 */
250static void
251_on_device_connected(void* opaque, AndroidDevice* ad, int failure)
252{
253    if (!failure) {
254        AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
255        mtsp->is_connected = 1;
256        D("Multi-touch emulation has started");
257        android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events),
258                              _on_event_received);
259        mts_port_start(mtsp);
260    }
261}
262
263/* Invoked when an I/O failure occurs on a socket.
264 * Note that this callback will not be invoked on connection failures.
265 * Param:
266 *  opaque - AndroidMTSPort instance.
267 *  ad - Android device instance
268 *  ads - Connection socket where failure has occured.
269 *  failure - Contains 'errno' indicating the reason for failure.
270 */
271static void
272_on_io_failure(void* opaque, AndroidDevice* ad, int failure)
273{
274    AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
275    E("Multi-touch port got disconnected: %s", strerror(failure));
276    mtsp->is_connected = 0;
277    android_device_disconnect(ad);
278
279    /* Try to reconnect again. */
280    android_device_connect_async(ad, _on_device_connected);
281}
282
283/********************************************************************************
284 *                          MTS port API
285 *******************************************************************************/
286
287AndroidMTSPort*
288mts_port_create(void* opaque)
289{
290    AndroidMTSPort* mtsp;
291    int res;
292
293    ANEW0(mtsp);
294    mtsp->opaque = opaque;
295    mtsp->is_connected = 0;
296
297    /* Initialize default MTS descriptor. */
298    multitouch_init(mtsp);
299
300    /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front
301     * of the compressed data. this way we will have entire query ready to be
302     * transmitted to the device. */
303    mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096);
304
305    mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure);
306    if (mtsp->device == NULL) {
307        _mts_port_free(mtsp);
308        return NULL;
309    }
310
311    res = android_device_connect_async(mtsp->device, _on_device_connected);
312    if (res != 0) {
313        mts_port_destroy(mtsp);
314        return NULL;
315    }
316
317    return mtsp;
318}
319
320void
321mts_port_destroy(AndroidMTSPort* mtsp)
322{
323    _mts_port_free(mtsp);
324}
325
326int
327mts_port_is_connected(AndroidMTSPort* mtsp)
328{
329    return mtsp->is_connected;
330}
331
332int
333mts_port_start(AndroidMTSPort* mtsp)
334{
335    char qresp[MTSP_MAX_MSG];
336    char query[256];
337    AndroidHwConfig* config = android_hw;
338
339    /* Query the device to start capturing multi-touch events, also providing
340     * the device with width / height of the emulator's screen. This is required
341     * so device can properly adjust multi-touch event coordinates, and display
342     * emulator's framebuffer. */
343    snprintf(query, sizeof(query), "start:%dx%d",
344             config->hw_lcd_width, config->hw_lcd_height);
345    int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp),
346                                   MTSP_QUERY_TIMEOUT);
347    if (!res) {
348        /* By protocol device should reply with its view dimensions. */
349        if (*qresp) {
350            int width, height;
351            if (sscanf(qresp, "%dx%d", &width, &height) == 2) {
352                multitouch_set_device_screen_size(width, height);
353                D("Multi-touch emulation has started. Device dims: %dx%d",
354                  width, height);
355            } else {
356                E("Unexpected reply to MTS 'start' query: %s", qresp);
357                android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
358                                     MTSP_QUERY_TIMEOUT);
359                res = -1;
360            }
361        } else {
362            E("MTS protocol error: no reply to query 'start'");
363            android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
364                                 MTSP_QUERY_TIMEOUT);
365            res = -1;
366        }
367    } else {
368        if (errno) {
369            D("Query 'start' failed on I/O: %s", strerror(errno));
370        } else {
371            D("Query 'start' failed on device: %s", qresp);
372        }
373    }
374    return res;
375}
376
377int
378mts_port_stop(AndroidMTSPort* mtsp)
379{
380    char qresp[MTSP_MAX_MSG];
381    const int res =
382        android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
383                             MTSP_QUERY_TIMEOUT);
384    if (res) {
385        if (errno) {
386            D("Query 'stop' failed on I/O: %s", strerror(errno));
387        } else {
388            D("Query 'stop' failed on device: %s", qresp);
389        }
390    }
391
392    return res;
393}
394
395/********************************************************************************
396 *                       Handling framebuffer updates
397 *******************************************************************************/
398
399/* Compresses a framebuffer region into JPEG image.
400 * Param:
401 *  mtsp - Multi-touch port descriptor with initialized JPEG compressor.
402 *  fmt Descriptor for framebuffer region to compress.
403 *  fb Beginning of the framebuffer.
404 *  jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
405 *      value 10 provides pretty decent image for the purpose of multi-touch
406 *      emulation.
407 */
408static void
409_fb_compress(const AndroidMTSPort* mtsp,
410             const MTFrameHeader* fmt,
411             const uint8_t* fb,
412             int jpeg_quality,
413             int ydir)
414{
415    jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
416                                fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl,
417                                fb, jpeg_quality, ydir);
418}
419
420int
421mts_port_send_frame(AndroidMTSPort* mtsp,
422                    MTFrameHeader* fmt,
423                    const uint8_t* fb,
424                    async_send_cb cb,
425                    void* cb_opaque,
426                    int ydir)
427{
428    char* query;
429    int blob_size, off;
430
431    /* Make sure that port is connected. */
432    if (!mts_port_is_connected(mtsp)) {
433        return -1;
434    }
435
436    /* Compress framebuffer region. 10% quality seems to be sufficient. */
437    fmt->format = MTFB_JPEG;
438    _fb_compress(mtsp, fmt, fb, 10, ydir);
439
440    /* Total size of the blob: header + JPEG image. */
441    blob_size = sizeof(MTFrameHeader) +
442                jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
443
444    /* Query starts at the beginning of the buffer allocated by the compressor's
445     * destination manager. */
446    query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
447
448    /* Build the $BLOB query to transfer to the device. */
449    snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor),
450             "$BLOB:%09d", blob_size);
451    off = strlen(query) + 1;
452
453    /* Copy framebuffer update header to the query. */
454    memcpy(query + off, fmt, sizeof(MTFrameHeader));
455
456    /* Zeroing the rectangle in the update header we indicate that it contains
457     * no updates. */
458    fmt->x = fmt->y = fmt->w = fmt->h = 0;
459
460    /* Initiate asynchronous transfer of the updated framebuffer rectangle. */
461    if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) {
462        D("Unable to send query '%s': %s", query, strerror(errno));
463        return -1;
464    }
465
466    return 0;
467}
468