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