| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "qemu-common.h" |
| #include "utils/panic.h" |
| #include "android/hw-events.h" |
| #include "android/charmap.h" |
| #include "android/multitouch-screen.h" |
| #include "android/sdk-controller-socket.h" |
| #include "android/multitouch-port.h" |
| #include "android/globals.h" /* for android_hw */ |
| #include "android/opengles.h" |
| #include "android/utils/misc.h" |
| #include "android/utils/jpeg-compress.h" |
| #include "android/utils/debug.h" |
| |
| #define E(...) derror(__VA_ARGS__) |
| #define W(...) dwarning(__VA_ARGS__) |
| #define D(...) VERBOSE_PRINT(mtport,__VA_ARGS__) |
| #define D_ACTIVE VERBOSE_CHECK(mtport) |
| |
| #define TRACE_ON 1 |
| |
| #if TRACE_ON |
| #define T(...) VERBOSE_PRINT(mtport,__VA_ARGS__) |
| #else |
| #define T(...) |
| #endif |
| |
| /* Timeout (millisec) to use when communicating with SDK controller. */ |
| #define SDKCTL_MT_TIMEOUT 3000 |
| |
| /* |
| * Message types used in multi-touch emulation. |
| */ |
| |
| /* Pointer move message. */ |
| #define SDKCTL_MT_MOVE 1 |
| /* First pointer down message. */ |
| #define SDKCTL_MT_FISRT_DOWN 2 |
| /* Last pointer up message. */ |
| #define SDKCTL_MT_LAST_UP 3 |
| /* Pointer down message. */ |
| #define SDKCTL_MT_POINTER_DOWN 4 |
| /* Pointer up message. */ |
| #define SDKCTL_MT_POINTER_UP 5 |
| /* Sends framebuffer update. */ |
| #define SDKCTL_MT_FB_UPDATE 6 |
| /* Framebuffer update has been received. */ |
| #define SDKCTL_MT_FB_UPDATE_RECEIVED 7 |
| /* Framebuffer update has been handled. */ |
| #define SDKCTL_MT_FB_UPDATE_HANDLED 8 |
| |
| /* Multi-touch port descriptor. */ |
| struct AndroidMTSPort { |
| /* Caller identifier. */ |
| void* opaque; |
| /* Communication socket. */ |
| SDKCtlSocket* sdkctl; |
| /* Initialized JPEG compressor instance. */ |
| AJPEGDesc* jpeg_compressor; |
| /* Direct packet descriptor for framebuffer updates. */ |
| SDKCtlDirectPacket* fb_packet; |
| }; |
| |
| /* Data sent with SDKCTL_MT_QUERY_START */ |
| typedef struct QueryDispData { |
| /* Width of the emulator display. */ |
| int width; |
| /* Height of the emulator display. */ |
| int height; |
| } QueryDispData; |
| |
| /* Multi-touch event structure received from SDK controller port. */ |
| typedef struct AndroidMTEvent { |
| /* Pointer identifier. */ |
| int pid; |
| /* Pointer 'x' coordinate. */ |
| int x; |
| /* Pointer 'y' coordinate. */ |
| int y; |
| /* Pointer pressure. */ |
| int pressure; |
| } AndroidMTEvent; |
| |
| /* Multi-touch pointer descriptor received from SDK controller port. */ |
| typedef struct AndroidMTPtr { |
| /* Pointer identifier. */ |
| int pid; |
| } AndroidMTPtr; |
| |
| /* Destroys and frees the descriptor. */ |
| static void |
| _mts_port_free(AndroidMTSPort* mtsp) |
| { |
| if (mtsp != NULL) { |
| if (mtsp->fb_packet != NULL) { |
| sdkctl_direct_packet_release(mtsp->fb_packet); |
| } |
| if (mtsp->jpeg_compressor != NULL) { |
| jpeg_compressor_destroy(mtsp->jpeg_compressor); |
| } |
| if (mtsp->sdkctl != NULL) { |
| sdkctl_socket_release(mtsp->sdkctl); |
| } |
| AFREE(mtsp); |
| } |
| } |
| |
| /******************************************************************************** |
| * Multi-touch action handlers |
| *******************************************************************************/ |
| |
| /* |
| * Although there are a lot of similarities in the way the handlers below are |
| * implemented, for the sake of tracing / debugging it's better to have a |
| * separate handler for each distinctive action. |
| */ |
| |
| /* First pointer down event handler. */ |
| static void |
| _on_action_down(int tracking_id, int x, int y, int pressure) |
| { |
| multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure); |
| } |
| |
| /* Last pointer up event handler. */ |
| static void |
| _on_action_up(int tracking_id) |
| { |
| multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0); |
| } |
| |
| /* Pointer down event handler. */ |
| static void |
| _on_action_pointer_down(int tracking_id, int x, int y, int pressure) |
| { |
| multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure); |
| } |
| |
| /* Pointer up event handler. */ |
| static void |
| _on_action_pointer_up(int tracking_id) |
| { |
| multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0); |
| } |
| |
| /* Pointer move event handler. */ |
| static void |
| _on_action_move(int tracking_id, int x, int y, int pressure) |
| { |
| multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure); |
| } |
| |
| /******************************************************************************** |
| * Multi-touch event handlers |
| *******************************************************************************/ |
| |
| /* Handles "pointer move" event. |
| * Param: |
| * param - Array of moving pointers. |
| * pointers_count - Number of pointers in the array. |
| */ |
| static void |
| _on_move(const AndroidMTEvent* param, int pointers_count) |
| { |
| int n; |
| for (n = 0; n < pointers_count; n++, param++) { |
| T("Multi-touch: MOVE(%d): %d-> %d:%d:%d", |
| n, param->pid, param->x, param->y, param->pressure); |
| _on_action_move(param->pid, param->x, param->y, param->pressure); |
| } |
| } |
| |
| /* Handles "first pointer down" event. */ |
| static void |
| _on_down(const AndroidMTEvent* param) |
| { |
| T("Multi-touch: 1-ST DOWN: %d-> %d:%d:%d", |
| param->pid, param->x, param->y, param->pressure); |
| _on_action_down(param->pid, param->x, param->y, param->pressure); |
| } |
| |
| /* Handles "last pointer up" event. */ |
| static void |
| _on_up(const AndroidMTPtr* param) |
| { |
| T("Multi-touch: LAST UP: %d", param->pid); |
| _on_action_up(param->pid); |
| } |
| |
| /* Handles "next pointer down" event. */ |
| static void |
| _on_pdown(const AndroidMTEvent* param) |
| { |
| T("Multi-touch: DOWN: %d-> %d:%d:%d", |
| param->pid, param->x, param->y, param->pressure); |
| _on_action_pointer_down(param->pid, param->x, param->y, param->pressure); |
| } |
| |
| /* Handles "next pointer up" event. */ |
| static void |
| _on_pup(const AndroidMTPtr* param) |
| { |
| T("Multi-touch: UP: %d", param->pid); |
| _on_action_pointer_up(param->pid); |
| } |
| |
| /******************************************************************************** |
| * Device communication callbacks |
| *******************************************************************************/ |
| |
| /* A callback that is invoked on SDK controller socket connection events. */ |
| static AsyncIOAction |
| _on_multitouch_socket_connection(void* opaque, |
| SDKCtlSocket* sdkctl, |
| AsyncIOState status) |
| { |
| if (status == ASIO_STATE_FAILED) { |
| /* Reconnect (after timeout delay) on failures */ |
| if (sdkctl_socket_is_handshake_ok(sdkctl)) { |
| sdkctl_socket_reconnect(sdkctl, SDKCTL_DEFAULT_TCP_PORT, |
| SDKCTL_MT_TIMEOUT); |
| } |
| } |
| return ASIO_ACTION_DONE; |
| } |
| |
| /* A callback that is invoked on SDK controller port connection events. */ |
| static void |
| _on_multitouch_port_connection(void* opaque, |
| SDKCtlSocket* sdkctl, |
| SdkCtlPortStatus status) |
| { |
| switch (status) { |
| case SDKCTL_PORT_CONNECTED: |
| D("Multi-touch: SDK Controller is connected"); |
| break; |
| |
| case SDKCTL_PORT_DISCONNECTED: |
| D("Multi-touch: SDK Controller is disconnected"); |
| // Disable OpenGLES framebuffer updates. |
| if (android_hw->hw_gpu_enabled) { |
| android_setPostCallback(NULL, NULL); |
| } |
| break; |
| |
| case SDKCTL_PORT_ENABLED: |
| D("Multi-touch: SDK Controller port is enabled."); |
| // Enable OpenGLES framebuffer updates. |
| if (android_hw->hw_gpu_enabled) { |
| android_setPostCallback(multitouch_opengles_fb_update, NULL); |
| } |
| /* Refresh (possibly stale) device screen. */ |
| multitouch_refresh_screen(); |
| break; |
| |
| case SDKCTL_PORT_DISABLED: |
| D("Multi-touch: SDK Controller port is disabled."); |
| // Disable OpenGLES framebuffer updates. |
| if (android_hw->hw_gpu_enabled) { |
| android_setPostCallback(NULL, NULL); |
| } |
| break; |
| |
| case SDKCTL_HANDSHAKE_CONNECTED: |
| D("Multi-touch: Handshake succeeded with connected port."); |
| break; |
| |
| case SDKCTL_HANDSHAKE_NO_PORT: |
| D("Multi-touch: Handshake succeeded with disconnected port."); |
| break; |
| |
| case SDKCTL_HANDSHAKE_DUP: |
| W("Multi-touch: Handshake failed due to port duplication."); |
| sdkctl_socket_disconnect(sdkctl); |
| break; |
| |
| case SDKCTL_HANDSHAKE_UNKNOWN_QUERY: |
| W("Multi-touch: Handshake failed due to unknown query."); |
| sdkctl_socket_disconnect(sdkctl); |
| break; |
| |
| case SDKCTL_HANDSHAKE_UNKNOWN_RESPONSE: |
| default: |
| W("Multi-touch: Handshake failed due to unknown reason."); |
| sdkctl_socket_disconnect(sdkctl); |
| break; |
| } |
| } |
| |
| /* A callback that is invoked when a message is received from the device. */ |
| static void |
| _on_multitouch_message(void* client_opaque, |
| SDKCtlSocket* sdkctl, |
| SDKCtlMessage* message, |
| int msg_type, |
| void* msg_data, |
| int msg_size) |
| { |
| switch (msg_type) { |
| case SDKCTL_MT_MOVE: { |
| assert((msg_size / sizeof(AndroidMTEvent)) && !(msg_size % sizeof(AndroidMTEvent))); |
| _on_move((const AndroidMTEvent*)msg_data, msg_size / sizeof(AndroidMTEvent)); |
| break; |
| } |
| |
| case SDKCTL_MT_FISRT_DOWN: |
| assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); |
| _on_down((const AndroidMTEvent*)msg_data); |
| break; |
| |
| case SDKCTL_MT_LAST_UP: |
| _on_up((const AndroidMTPtr*)msg_data); |
| break; |
| |
| case SDKCTL_MT_POINTER_DOWN: |
| assert(msg_size / sizeof(AndroidMTEvent) && !(msg_size % sizeof(AndroidMTEvent))); |
| _on_pdown((const AndroidMTEvent*)msg_data); |
| break; |
| |
| case SDKCTL_MT_POINTER_UP: |
| _on_pup((const AndroidMTPtr*)msg_data); |
| break; |
| |
| case SDKCTL_MT_FB_UPDATE_RECEIVED: |
| D("Framebuffer update ACK."); |
| break; |
| |
| case SDKCTL_MT_FB_UPDATE_HANDLED: |
| D("Framebuffer update handled."); |
| multitouch_fb_updated(); |
| break; |
| |
| default: |
| W("Multi-touch: Unknown message %d", msg_type); |
| break; |
| } |
| } |
| |
| /******************************************************************************** |
| * MTS port API |
| *******************************************************************************/ |
| |
| AndroidMTSPort* |
| mts_port_create(void* opaque) |
| { |
| AndroidMTSPort* mtsp; |
| |
| ANEW0(mtsp); |
| mtsp->opaque = opaque; |
| |
| /* Initialize default MTS descriptor. */ |
| multitouch_init(mtsp); |
| |
| /* Create JPEG compressor. Put message header + MTFrameHeader in front of the |
| * compressed data. this way we will have entire query ready to be |
| * transmitted to the device. */ |
| mtsp->jpeg_compressor = |
| jpeg_compressor_create(sdkctl_message_get_header_size() + sizeof(MTFrameHeader), 4096); |
| |
| mtsp->sdkctl = sdkctl_socket_new(SDKCTL_MT_TIMEOUT, "multi-touch", |
| _on_multitouch_socket_connection, |
| _on_multitouch_port_connection, |
| _on_multitouch_message, mtsp); |
| sdkctl_init_recycler(mtsp->sdkctl, 64, 8); |
| |
| /* Create a direct packet that will wrap up framebuffer updates. Note that |
| * we need to do this after we have initialized the recycler! */ |
| mtsp->fb_packet = sdkctl_direct_packet_new(mtsp->sdkctl); |
| |
| /* Now we can initiate connection witm MT port on the device. */ |
| sdkctl_socket_connect(mtsp->sdkctl, SDKCTL_DEFAULT_TCP_PORT, |
| SDKCTL_MT_TIMEOUT); |
| |
| return mtsp; |
| } |
| |
| void |
| mts_port_destroy(AndroidMTSPort* mtsp) |
| { |
| _mts_port_free(mtsp); |
| } |
| |
| /******************************************************************************** |
| * Handling framebuffer updates |
| *******************************************************************************/ |
| |
| /* Compresses a framebuffer region into JPEG image. |
| * Param: |
| * mtsp - Multi-touch port descriptor with initialized JPEG compressor. |
| * fmt Descriptor for framebuffer region to compress. |
| * fb Beginning of the framebuffer. |
| * jpeg_quality JPEG compression quality. A number from 1 to 100. Note that |
| * value 10 provides pretty decent image for the purpose of multi-touch |
| * emulation. |
| */ |
| static void |
| _fb_compress(const AndroidMTSPort* mtsp, |
| const MTFrameHeader* fmt, |
| const uint8_t* fb, |
| int jpeg_quality, |
| int ydir) |
| { |
| T("Multi-touch: compressing %d bytes frame buffer", fmt->w * fmt->h * fmt->bpp); |
| |
| jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w, |
| fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl, |
| fb, jpeg_quality, ydir); |
| } |
| |
| int |
| mts_port_send_frame(AndroidMTSPort* mtsp, |
| MTFrameHeader* fmt, |
| const uint8_t* fb, |
| on_sdkctl_direct_cb cb, |
| void* cb_opaque, |
| int ydir) |
| { |
| /* Make sure that port is connected. */ |
| if (!sdkctl_socket_is_port_ready(mtsp->sdkctl)) { |
| return -1; |
| } |
| |
| /* Compress framebuffer region. 10% quality seems to be sufficient. */ |
| fmt->format = MTFB_JPEG; |
| _fb_compress(mtsp, fmt, fb, 10, ydir); |
| |
| /* Total size of the update data: header + JPEG image. */ |
| const int update_size = |
| sizeof(MTFrameHeader) + jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor); |
| |
| /* Update message starts at the beginning of the buffer allocated by the |
| * compressor's destination manager. */ |
| uint8_t* const msg = (uint8_t*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor); |
| |
| /* Initialize message header. */ |
| sdkctl_init_message_header(msg, SDKCTL_MT_FB_UPDATE, update_size); |
| |
| /* Copy framebuffer update header to the message. */ |
| memcpy(msg + sdkctl_message_get_header_size(), fmt, sizeof(MTFrameHeader)); |
| |
| /* Compression rate... */ |
| const float comp_rate = ((float)jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor) / (fmt->w * fmt->h * fmt->bpp)) * 100; |
| |
| /* Zeroing the rectangle in the update header we indicate that it contains |
| * no updates. */ |
| fmt->x = fmt->y = fmt->w = fmt->h = 0; |
| |
| /* Send update to the device. */ |
| sdkctl_direct_packet_send(mtsp->fb_packet, msg, cb, cb_opaque); |
| |
| T("Multi-touch: Sent %d bytes in framebuffer update. Compression rate is %.2f%%", |
| update_size, comp_rate); |
| |
| return 0; |
| } |