Home | History | Annotate | Download | only in android
      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. */
     38 struct 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. */
     54 static 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. */
     79 static 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. */
     86 static 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. */
     93 static 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. */
    100 static 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. */
    107 static 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. */
    118 static 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. */
    138 static 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. */
    154 static 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. */
    167 static 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. */
    183 static 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  */
    203 static 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  */
    250 static 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  */
    271 static 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 
    287 AndroidMTSPort*
    288 mts_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 
    320 void
    321 mts_port_destroy(AndroidMTSPort* mtsp)
    322 {
    323     _mts_port_free(mtsp);
    324 }
    325 
    326 int
    327 mts_port_is_connected(AndroidMTSPort* mtsp)
    328 {
    329     return mtsp->is_connected;
    330 }
    331 
    332 int
    333 mts_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 
    377 int
    378 mts_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  */
    408 static 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 
    420 int
    421 mts_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