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