multitouch-port.c revision 6df71197252e119baccbe4dd060935222af4f8ba
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/sdk-controller-socket.h"
23#include "android/multitouch-port.h"
24#include "android/globals.h"  /* for android_hw */
25#include "android/opengles.h"
26#include "android/utils/misc.h"
27#include "android/utils/jpeg-compress.h"
28#include "android/utils/debug.h"
29
30#define  E(...)    derror(__VA_ARGS__)
31#define  W(...)    dwarning(__VA_ARGS__)
32#define  D(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
33#define  D_ACTIVE  VERBOSE_CHECK(mtport)
34
35#define TRACE_ON    1
36
37#if TRACE_ON
38#define  T(...)    VERBOSE_PRINT(mtport,__VA_ARGS__)
39#else
40#define  T(...)
41#endif
42
43/* Timeout (millisec) to use when communicating with SDK controller. */
44#define SDKCTL_MT_TIMEOUT      3000
45
46/*
47 * Message types used in multi-touch emulation.
48 */
49
50/* Pointer move message. */
51#define SDKCTL_MT_MOVE          1
52/* First pointer down message. */
53#define SDKCTL_MT_FISRT_DOWN    2
54/* Last pointer up message. */
55#define SDKCTL_MT_LAST_UP       3
56/* Pointer down message. */
57#define SDKCTL_MT_POINTER_DOWN  4
58/* Pointer up message. */
59#define SDKCTL_MT_POINTER_UP    5
60/* Sends framebuffer update. */
61#define SDKCTL_MT_FB_UPDATE     6
62
63/* Multi-touch port descriptor. */
64struct AndroidMTSPort {
65    /* Caller identifier. */
66    void*               opaque;
67    /* Communication socket. */
68    SDKCtlSocket*       sdkctl;
69    /* Initialized JPEG compressor instance. */
70    AJPEGDesc*          jpeg_compressor;
71    /* Direct packet descriptor for framebuffer updates. */
72    SDKCtlDirectPacket* fb_packet;
73};
74
75/* Data sent with SDKCTL_MT_QUERY_START */
76typedef struct QueryDispData {
77    /* Width of the emulator display. */
78    int     width;
79    /* Height of the emulator display. */
80    int     height;
81} QueryDispData;
82
83/* Multi-touch event structure received from SDK controller port. */
84typedef struct AndroidMTEvent {
85    /* Pointer identifier. */
86    int     pid;
87    /* Pointer 'x' coordinate. */
88    int     x;
89    /* Pointer 'y' coordinate. */
90    int     y;
91    /* Pointer pressure. */
92    int     pressure;
93} AndroidMTEvent;
94
95/* Multi-touch pointer descriptor received from SDK controller port. */
96typedef struct AndroidMTPtr {
97    /* Pointer identifier. */
98    int     pid;
99} AndroidMTPtr;
100
101/* Destroys and frees the descriptor. */
102static void
103_mts_port_free(AndroidMTSPort* mtsp)
104{
105    if (mtsp != NULL) {
106        if (mtsp->fb_packet != NULL) {
107            sdkctl_direct_packet_release(mtsp->fb_packet);
108        }
109        if (mtsp->jpeg_compressor != NULL) {
110            jpeg_compressor_destroy(mtsp->jpeg_compressor);
111        }
112        if (mtsp->sdkctl != NULL) {
113            sdkctl_socket_release(mtsp->sdkctl);
114        }
115        AFREE(mtsp);
116    }
117}
118
119/********************************************************************************
120 *                          Multi-touch action handlers
121 *******************************************************************************/
122
123/*
124 * Although there are a lot of similarities in the way the handlers below are
125 * implemented, for the sake of tracing / debugging it's better to have a
126 * separate handler for each distinctive action.
127 */
128
129/* First pointer down event handler. */
130static void
131_on_action_down(int tracking_id, int x, int y, int pressure)
132{
133    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
134}
135
136/* Last pointer up event handler. */
137static void
138_on_action_up(int tracking_id)
139{
140    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
141}
142
143/* Pointer down event handler. */
144static void
145_on_action_pointer_down(int tracking_id, int x, int y, int pressure)
146{
147    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
148}
149
150/* Pointer up event handler. */
151static void
152_on_action_pointer_up(int tracking_id)
153{
154    multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
155}
156
157/* Pointer move event handler. */
158static void
159_on_action_move(int tracking_id, int x, int y, int pressure)
160{
161    multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
162}
163
164/********************************************************************************
165 *                          Multi-touch event handlers
166 *******************************************************************************/
167
168/* Handles "pointer move" event.
169 * Param:
170 *  param - Array of moving pointers.
171 *  pointers_count - Number of pointers in the array.
172 */
173static void
174_on_move(const AndroidMTEvent* param, int pointers_count)
175{
176    int n;
177    for (n = 0; n < pointers_count; n++, param++) {
178        T("Multi-touch: MOVE(%d): %d-> %d:%d:%d",
179          n, param->pid, param->x, param->y, param->pressure);
180         _on_action_move(param->pid, param->x, param->y, param->pressure);
181    }
182}
183
184/* Handles "first pointer down" event. */
185static void
186_on_down(const AndroidMTEvent* param)
187{
188    T("Multi-touch: 1-ST DOWN: %d-> %d:%d:%d",
189      param->pid, param->x, param->y, param->pressure);
190    _on_action_down(param->pid, param->x, param->y, param->pressure);
191}
192
193/* Handles "last pointer up" event. */
194static void
195_on_up(const AndroidMTPtr* param)
196{
197    T("Multi-touch: LAST UP: %d", param->pid);
198    _on_action_up(param->pid);
199}
200
201/* Handles "next pointer down" event. */
202static void
203_on_pdown(const AndroidMTEvent* param)
204{
205    T("Multi-touch: DOWN: %d-> %d:%d:%d",
206      param->pid, param->x, param->y, param->pressure);
207    _on_action_pointer_down(param->pid, param->x, param->y, param->pressure);
208}
209
210/* Handles "next pointer up" event. */
211static void
212_on_pup(const AndroidMTPtr* param)
213{
214    T("Multi-touch: UP: %d", param->pid);
215    _on_action_pointer_up(param->pid);
216}
217
218/********************************************************************************
219 *                      Device communication callbacks
220 *******************************************************************************/
221
222/* A callback that is invoked on SDK controller socket connection events. */
223static AsyncIOAction
224_on_multitouch_socket_connection(void* opaque,
225                                 SDKCtlSocket* sdkctl,
226                                 AsyncIOState status)
227{
228    if (status == ASIO_STATE_FAILED) {
229        /* Reconnect (after timeout delay) on failures */
230        if (sdkctl_socket_is_handshake_ok(sdkctl)) {
231            sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT,
232                                    SDKCTL_MT_TIMEOUT);
233        }
234    }
235    return ASIO_ACTION_DONE;
236}
237
238/* A callback that is invoked on SDK controller port connection events. */
239static void
240_on_multitouch_port_connection(void* opaque,
241                               SDKCtlSocket* sdkctl,
242                               SdkCtlPortStatus status)
243{
244    switch (status) {
245        case SDKCTL_PORT_CONNECTED:
246            D("Multi-touch: SDK Controller is connected");
247            break;
248
249        case SDKCTL_PORT_DISCONNECTED:
250            D("Multi-touch: SDK Controller is disconnected");
251            // Disable OpenGLES framebuffer updates.
252            if (android_hw->hw_gpu_enabled) {
253                android_setPostCallback(NULL, NULL);
254            }
255            break;
256
257        case SDKCTL_PORT_ENABLED:
258            D("Multi-touch: SDK Controller port is enabled.");
259            // Enable OpenGLES framebuffer updates.
260            if (android_hw->hw_gpu_enabled) {
261                android_setPostCallback(multitouch_opengles_fb_update, NULL);
262            }
263            /* Refresh (possibly stale) device screen. */
264            multitouch_refresh_screen();
265            break;
266
267        case SDKCTL_PORT_DISABLED:
268            D("Multi-touch: SDK Controller port is disabled.");
269            // Disable OpenGLES framebuffer updates.
270            if (android_hw->hw_gpu_enabled) {
271                android_setPostCallback(NULL, NULL);
272            }
273            break;
274
275        case SDKCTL_HANDSHAKE_CONNECTED:
276            D("Multi-touch: Handshake succeeded with connected port.");
277            break;
278
279        case SDKCTL_HANDSHAKE_NO_PORT:
280            D("Multi-touch: Handshake succeeded with disconnected port.");
281            break;
282
283        case SDKCTL_HANDSHAKE_DUP:
284            W("Multi-touch: Handshake failed due to port duplication.");
285            sdkctl_socket_disconnect(sdkctl);
286            break;
287
288        case SDKCTL_HANDSHAKE_UNKNOWN_QUERY:
289            W("Multi-touch: Handshake failed due to unknown query.");
290            sdkctl_socket_disconnect(sdkctl);
291            break;
292
293        case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE:
294        default:
295            W("Multi-touch: Handshake failed due to unknown reason.");
296            sdkctl_socket_disconnect(sdkctl);
297            break;
298    }
299}
300
301/* A callback that is invoked when a message is received from the device. */
302static void
303_on_multitouch_message(void* client_opaque,
304                       SDKCtlSocket* sdkctl,
305                       SDKCtlMessage* message,
306                       int msg_type,
307                       void* msg_data,
308                       int msg_size)
309{
310    switch (msg_type) {
311        case SDKCTL_MT_MOVE: {
312            assert((msg_size / sizeof(AndroidMTEvent)) && !(msg_size % sizeof(AndroidMTEvent)));
313            _on_move((const AndroidMTEvent*)msg_data, msg_size / sizeof(AndroidMTEvent));
314            break;
315        }
316
317        case SDKCTL_MT_FISRT_DOWN:
318            assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent)));
319            _on_down((const AndroidMTEvent*)msg_data);
320            break;
321
322        case SDKCTL_MT_LAST_UP:
323            _on_up((const AndroidMTPtr*)msg_data);
324            break;
325
326        case SDKCTL_MT_POINTER_DOWN:
327            assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent)));
328            _on_pdown((const AndroidMTEvent*)msg_data);
329            break;
330
331        case SDKCTL_MT_POINTER_UP:
332            _on_pup((const AndroidMTPtr*)msg_data);
333            break;
334
335        default:
336            W("Multi-touch: Unknown message %d", msg_type);
337            break;
338    }
339}
340
341/********************************************************************************
342 *                          MTS port API
343 *******************************************************************************/
344
345AndroidMTSPort*
346mts_port_create(void* opaque)
347{
348    AndroidMTSPort* mtsp;
349
350    ANEW0(mtsp);
351    mtsp->opaque                = opaque;
352
353    /* Initialize default MTS descriptor. */
354    multitouch_init(mtsp);
355
356    /* Create JPEG compressor. Put message header + MTFrameHeader in front of the
357     * compressed data. this way we will have entire query ready to be
358     * transmitted to the device. */
359    mtsp->jpeg_compressor =
360        jpeg_compressor_create(sdkctl_message_get_header_size() + sizeof(MTFrameHeader), 4096);
361
362    mtsp->sdkctl = sdkctl_socket_new(SDKCTL_MT_TIMEOUT, "multi-touch",
363                                     _on_multitouch_socket_connection,
364                                     _on_multitouch_port_connection,
365                                     _on_multitouch_message, mtsp);
366    sdkctl_init_recycler(mtsp->sdkctl, 64, 8);
367
368    /* Create a direct packet that will wrap up framebuffer updates. Note that
369     * we need to do this after we have initialized the recycler! */
370    mtsp->fb_packet = sdkctl_direct_packet_new(mtsp->sdkctl);
371
372    /* Now we can initiate connection witm MT port on the device. */
373    sdkctl_socket_connect(mtsp->sdkctl, SDKCTL_DEFAULT_TCP_PORT,
374                          SDKCTL_MT_TIMEOUT);
375
376    return mtsp;
377}
378
379void
380mts_port_destroy(AndroidMTSPort* mtsp)
381{
382    _mts_port_free(mtsp);
383}
384
385/********************************************************************************
386 *                       Handling framebuffer updates
387 *******************************************************************************/
388
389/* Compresses a framebuffer region into JPEG image.
390 * Param:
391 *  mtsp - Multi-touch port descriptor with initialized JPEG compressor.
392 *  fmt Descriptor for framebuffer region to compress.
393 *  fb Beginning of the framebuffer.
394 *  jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
395 *      value 10 provides pretty decent image for the purpose of multi-touch
396 *      emulation.
397 */
398static void
399_fb_compress(const AndroidMTSPort* mtsp,
400             const MTFrameHeader* fmt,
401             const uint8_t* fb,
402             int jpeg_quality,
403             int ydir)
404{
405    T("Multi-touch: compressing %d bytes frame buffer", fmt->w * fmt->h * fmt->bpp);
406
407    jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
408                                fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl,
409                                fb, jpeg_quality, ydir);
410}
411
412int
413mts_port_send_frame(AndroidMTSPort* mtsp,
414                    MTFrameHeader* fmt,
415                    const uint8_t* fb,
416                    on_sdkctl_direct_cb cb,
417                    void* cb_opaque,
418                    int ydir)
419{
420    /* Make sure that port is connected. */
421    if (!sdkctl_socket_is_port_ready(mtsp->sdkctl)) {
422        return -1;
423    }
424
425    /* Compress framebuffer region. 10% quality seems to be sufficient. */
426    fmt->format = MTFB_JPEG;
427    _fb_compress(mtsp, fmt, fb, 10, ydir);
428
429    /* Total size of the update data: header + JPEG image. */
430    const int update_size =
431        sizeof(MTFrameHeader) + jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
432
433    /* Update message starts at the beginning of the buffer allocated by the
434     * compressor's destination manager. */
435    uint8_t* const msg = (uint8_t*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
436
437    /* Initialize message header. */
438    sdkctl_init_message_header(msg, SDKCTL_MT_FB_UPDATE, update_size);
439
440    /* Copy framebuffer update header to the message. */
441    memcpy(msg + sdkctl_message_get_header_size(), fmt, sizeof(MTFrameHeader));
442
443    /* Compression rate... */
444    const float comp_rate = ((float)jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor) / (fmt->w * fmt->h * fmt->bpp)) * 100;
445
446    /* Zeroing the rectangle in the update header we indicate that it contains
447     * no updates. */
448    fmt->x = fmt->y = fmt->w = fmt->h = 0;
449
450    /* Send update to the device. */
451    sdkctl_direct_packet_send(mtsp->fb_packet, msg, cb, cb_opaque);
452
453    T("Multi-touch: Sent %d bytes in framebuffer update. Compression rate is %.2f%%",
454      update_size, comp_rate);
455
456    return 0;
457}
458