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