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