blob: 88a76fe0433446e29f36259e553dca779bce6ad2 [file] [log] [blame]
/*
* 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;
}