blob: 4cf4fee3d6f00b650cc79d924f0c07bb8168bcbe [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 "android/user-events.h"
#include "android/display-core.h"
#include "android/hw-events.h"
#include "android/charmap.h"
#include "android/globals.h" /* for android_hw */
#include "android/utils/misc.h"
#include "android/utils/debug.h"
#include "android/multitouch-screen.h"
#define E(...) derror(__VA_ARGS__)
#define W(...) dwarning(__VA_ARGS__)
#define D(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__)
#define D_ACTIVE VERBOSE_CHECK(mtscreen)
#define TRACE_ON 1
#if TRACE_ON
#define T(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__)
#else
#define T(...)
#endif
/* Maximum number of pointers, supported by multi-touch emulation. */
#define MTS_POINTERS_NUM 10
/* Signals that pointer is not tracked (or is "up"). */
#define MTS_POINTER_UP -1
/* Special tracking ID for a mouse pointer. */
#define MTS_POINTER_MOUSE -2
/* Describes state of a multi-touch pointer */
typedef struct MTSPointerState {
/* Tracking ID assigned to the pointer by an app emulating multi-touch. */
int tracking_id;
/* X - coordinate of the tracked pointer. */
int x;
/* Y - coordinate of the tracked pointer. */
int y;
/* Current pressure value. */
int pressure;
} MTSPointerState;
/* Describes state of an emulated multi-touch screen. */
typedef struct MTSState {
/* Multi-touch port connected to the device. */
AndroidMTSPort* mtsp;
/* Emulator's display state. */
DisplayState* ds;
/* Number of tracked pointers. */
int tracked_ptr_num;
/* Index in the 'tracked_pointers' array of the last pointer for which
* ABS_MT_SLOT was sent. -1 indicates that no slot selection has been made
* yet. */
int current_slot;
/* Accumulator for ABS_TOUCH_MAJOR value. */
int touch_major;
/* Array of multi-touch pointers. */
MTSPointerState tracked_pointers[MTS_POINTERS_NUM];
/* Header collecting framebuffer information and updates. */
MTFrameHeader fb_header;
/* Boolean value indicating if framebuffer updates are currently being
* transferred to the application running on the device. */
int fb_transfer_in_progress;
/* Indicates direction in which lines are arranged in the framebuffer. If
* this value is negative, lines are arranged in bottom-up format (i.e. the
* bottom line is at the beginning of the buffer). */
int ydir;
/* Current framebuffer pointer. */
uint8_t* current_fb;
} MTSState;
/* Default multi-touch screen descriptor */
static MTSState _MTSState = { 0 };
/* Pushes event to the event device. */
static void
_push_event(int type, int code, int value)
{
user_event_generic(type, code, value);
}
/* Gets an index in the MTS's tracking pointers array MTS for the given
* tracking id.
* Return:
* Index of a matching entry in the MTS's tracking pointers array, or -1 if
* matching entry was not found.
*/
static int
_mtsstate_get_pointer_index(const MTSState* mts_state, int tracking_id)
{
int index;
for (index = 0; index < MTS_POINTERS_NUM; index++) {
if (mts_state->tracked_pointers[index].tracking_id == tracking_id) {
return index;
}
}
return -1;
}
/* Gets an index of the first untracking pointer in the MTS's tracking pointers
* array.
* Return:
* An index of the first untracking pointer, or -1 if all pointers are tracked.
*/
static int
_mtsstate_get_available_pointer_index(const MTSState* mts_state)
{
return _mtsstate_get_pointer_index(mts_state, MTS_POINTER_UP);
}
/* Handles a "pointer down" event
* Param:
* mts_state - MTS state descriptor.
* tracking_id - Tracking ID of the "downed" pointer.
* x, y - "Downed" pointer coordinates,
* pressure - Pressure value for the pointer.
*/
static void
_mts_pointer_down(MTSState* mts_state, int tracking_id, int x, int y, int pressure)
{
/* Get first available slot for the new pointer. */
const int slot_index = _mtsstate_get_available_pointer_index(mts_state);
/* Make sure there is a place for the pointer. */
if (slot_index >= 0) {
/* Initialize pointer's entry. */
mts_state->tracked_ptr_num++;
mts_state->tracked_pointers[slot_index].tracking_id = tracking_id;
mts_state->tracked_pointers[slot_index].x = x;
mts_state->tracked_pointers[slot_index].y = y;
mts_state->tracked_pointers[slot_index].pressure = pressure;
/* Send events indicating a "pointer down" to the EventHub */
/* Make sure that correct slot is selected. */
if (slot_index != mts_state->current_slot) {
_push_event(EV_ABS, ABS_MT_SLOT, slot_index);
}
_push_event(EV_ABS, ABS_MT_TRACKING_ID, slot_index);
_push_event(EV_ABS, ABS_MT_TOUCH_MAJOR, ++mts_state->touch_major);
_push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
_push_event(EV_ABS, ABS_MT_POSITION_X, x);
_push_event(EV_ABS, ABS_MT_POSITION_Y, y);
_push_event(EV_SYN, SYN_REPORT, 0);
mts_state->current_slot = slot_index;
} else {
D("MTS pointer count is exceeded.");
return;
}
}
/* Handles a "pointer up" event
* Param:
* mts_state - MTS state descriptor.
* slot_index - Pointer's index in the MTS's array of tracked pointers.
*/
static void
_mts_pointer_up(MTSState* mts_state, int slot_index)
{
/* Make sure that correct slot is selected. */
if (slot_index != mts_state->current_slot) {
_push_event(EV_ABS, ABS_MT_SLOT, slot_index);
}
/* Send event indicating "pointer up" to the EventHub. */
_push_event(EV_ABS, ABS_MT_TRACKING_ID, -1);
_push_event(EV_SYN, SYN_REPORT, 0);
/* Update MTS descriptor, removing the tracked pointer. */
mts_state->tracked_pointers[slot_index].tracking_id = MTS_POINTER_UP;
mts_state->tracked_pointers[slot_index].x = 0;
mts_state->tracked_pointers[slot_index].y = 0;
mts_state->tracked_pointers[slot_index].pressure = 0;
/* Since current slot is no longer tracked, make sure we will do a "select"
* next time we send events to the EventHub. */
mts_state->current_slot = -1;
mts_state->tracked_ptr_num--;
assert(mts_state->tracked_ptr_num >= 0);
}
/* Handles a "pointer move" event
* Param:
* mts_state - MTS state descriptor.
* slot_index - Pointer's index in the MTS's array of tracked pointers.
* x, y - New pointer coordinates,
* pressure - Pressure value for the pointer.
*/
static void
_mts_pointer_move(MTSState* mts_state, int slot_index, int x, int y, int pressure)
{
MTSPointerState* ptr_state = &mts_state->tracked_pointers[slot_index];
/* Make sure that coordinates have really changed. */
if (ptr_state->x == x && ptr_state->y == y) {
/* Coordinates didn't change. Bail out. */
return;
}
/* Make sure that the right slot is selected. */
if (slot_index != mts_state->current_slot) {
_push_event(EV_ABS, ABS_MT_SLOT, slot_index);
mts_state->current_slot = slot_index;
}
/* Push the changes down. */
if (ptr_state->pressure != pressure && pressure != 0) {
_push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
ptr_state->pressure = pressure;
}
if (ptr_state->x != x) {
_push_event(EV_ABS, ABS_MT_POSITION_X, x);
ptr_state->x = x;
}
if (ptr_state->y != y) {
_push_event(EV_ABS, ABS_MT_POSITION_Y, y);
ptr_state->y = y;
}
_push_event(EV_SYN, SYN_REPORT, 0);
}
/********************************************************************************
* Multi-touch API
*******************************************************************************/
/* Multi-touch service initialization flag. */
static int _is_mt_initialized = 0;
/* Callback that is invoked when framebuffer update has been transmitted to the
* device. */
static AsyncIOAction
_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status)
{
MTSState* const mts_state = (MTSState*)opaque;
if (status == ASIO_STATE_SUCCEEDED) {
/* Lets see if we have accumulated more changes while transmission has been
* in progress. */
if (mts_state->fb_header.w && mts_state->fb_header.h &&
!mts_state->fb_transfer_in_progress) {
mts_state->fb_transfer_in_progress = 1;
/* Send accumulated updates. */
if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
mts_state->current_fb, _on_fb_sent, mts_state,
mts_state->ydir)) {
mts_state->fb_transfer_in_progress = 0;
}
}
}
return ASIO_ACTION_DONE;
}
/* Common handler for framebuffer updates invoked by both, software, and OpenGLES
* renderers.
*/
static void
_mt_fb_common_update(MTSState* mts_state, int x, int y, int w, int h)
{
if (mts_state->fb_header.w == 0 && mts_state->fb_header.h == 0) {
/* First update after previous one has been transmitted to the device. */
mts_state->fb_header.x = x;
mts_state->fb_header.y = y;
mts_state->fb_header.w = w;
mts_state->fb_header.h = h;
} else {
/*
* Accumulate framebuffer changes in the header.
*/
/* "right" and "bottom" coordinates of the current update. */
int right = mts_state->fb_header.x + mts_state->fb_header.w;
int bottom = mts_state->fb_header.y + mts_state->fb_header.h;
/* "right" and "bottom" coordinates of the new update. */
const int new_right = x + w;
const int new_bottom = y + h;
/* Accumulate changed rectangle coordinates in the header. */
if (mts_state->fb_header.x > x) {
mts_state->fb_header.x = x;
}
if (mts_state->fb_header.y > y) {
mts_state->fb_header.y = y;
}
if (right < new_right) {
right = new_right;
}
if (bottom < new_bottom) {
bottom = new_bottom;
}
mts_state->fb_header.w = right - mts_state->fb_header.x;
mts_state->fb_header.h = bottom - mts_state->fb_header.y;
}
/* We will send updates to the device only after previous transmission is
* completed. */
if (!mts_state->fb_transfer_in_progress) {
mts_state->fb_transfer_in_progress = 1;
if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
mts_state->current_fb, _on_fb_sent, mts_state,
mts_state->ydir)) {
mts_state->fb_transfer_in_progress = 0;
}
}
}
/* A callback invoked on framebuffer updates by software renderer.
* Param:
* opaque - MTSState instance.
* x, y, w, h - Defines an updated rectangle inside the framebuffer.
*/
static void
_mt_fb_update(void* opaque, int x, int y, int w, int h)
{
MTSState* const mts_state = (MTSState*)opaque;
const DisplaySurface* const surface = mts_state->ds->surface;
T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d",
x, y, w, h);
/* TODO: For sofware renderer general framebuffer properties can change on
* the fly. Find a callback that can catch that. For now, just copy FB
* properties over in every FB update. */
mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
mts_state->fb_header.bpl = surface->linesize;
mts_state->fb_header.disp_width = surface->width;
mts_state->fb_header.disp_height = surface->height;
mts_state->current_fb = surface->data;
mts_state->ydir = 1;
_mt_fb_common_update(mts_state, x, y, w, h);
}
void
multitouch_opengles_fb_update(void* context,
int w, int h, int ydir,
int format, int type,
unsigned char* pixels)
{
MTSState* const mts_state = &_MTSState;
/* Make sure MT port is initialized. */
if (!_is_mt_initialized) {
return;
}
T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h);
/* GLES format is always RGBA8888 */
mts_state->fb_header.bpp = 4;
mts_state->fb_header.bpl = 4 * w;
mts_state->fb_header.disp_width = w;
mts_state->fb_header.disp_height = h;
mts_state->current_fb = pixels;
mts_state->ydir = ydir;
/* GLES emulator alwas update the entire framebuffer. */
_mt_fb_common_update(mts_state, 0, 0, w, h);
}
void
multitouch_refresh_screen(void)
{
MTSState* const mts_state = &_MTSState;
/* Make sure MT port is initialized. */
if (!_is_mt_initialized) {
return;
}
/* Lets see if any updates have been received so far. */
if (NULL != mts_state->current_fb) {
_mt_fb_common_update(mts_state, 0, 0, mts_state->fb_header.disp_width,
mts_state->fb_header.disp_height);
}
}
void
multitouch_fb_updated(void)
{
MTSState* const mts_state = &_MTSState;
/* This concludes framebuffer update. */
mts_state->fb_transfer_in_progress = 0;
/* Lets see if we have accumulated more changes while transmission has been
* in progress. */
if (mts_state->fb_header.w && mts_state->fb_header.h) {
mts_state->fb_transfer_in_progress = 1;
/* Send accumulated updates. */
if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
mts_state->current_fb, _on_fb_sent, mts_state,
mts_state->ydir)) {
mts_state->fb_transfer_in_progress = 0;
}
}
}
void
multitouch_init(AndroidMTSPort* mtsp)
{
if (!_is_mt_initialized) {
MTSState* const mts_state = &_MTSState;
DisplayState* const ds = get_displaystate();
DisplayUpdateListener* dul;
int index;
/*
* Initialize the descriptor.
*/
memset(mts_state, 0, sizeof(MTSState));
mts_state->tracked_ptr_num = 0;
mts_state->current_slot = -1;
for (index = 0; index < MTS_POINTERS_NUM; index++) {
mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP;
}
mts_state->mtsp = mtsp;
mts_state->fb_header.header_size = sizeof(MTFrameHeader);
mts_state->fb_transfer_in_progress = 0;
/*
* Set framebuffer update listener.
*/
ANEW0(dul);
dul->opaque = &_MTSState;
dul->dpy_update = _mt_fb_update;
/* Initialize framebuffer information in the screen descriptor. */
mts_state->ds = ds;
mts_state->fb_header.disp_width = ds->surface->width;
mts_state->fb_header.disp_height = ds->surface->height;
mts_state->fb_header.x = mts_state->fb_header.y = 0;
mts_state->fb_header.w = mts_state->fb_header.h = 0;
mts_state->fb_header.bpp = ds->surface->pf.bytes_per_pixel;
mts_state->fb_header.bpl = ds->surface->linesize;
mts_state->fb_transfer_in_progress = 0;
register_displayupdatelistener(ds, dul);
_is_mt_initialized = 1;
}
}
void
multitouch_update_pointer(MTESource source,
int tracking_id,
int x,
int y,
int pressure)
{
MTSState* const mts_state = &_MTSState;
/* Assign a fixed tracking ID to the mouse pointer. */
if (source == MTES_MOUSE) {
tracking_id = MTS_POINTER_MOUSE;
}
/* Find the tracked pointer for the tracking ID. */
const int slot_index = _mtsstate_get_pointer_index(mts_state, tracking_id);
if (slot_index < 0) {
/* This is the first time the pointer is seen. Must be "pressed",
* otherwise it's "hoovering", which we don't support yet. */
if (pressure == 0) {
if (tracking_id != MTS_POINTER_MOUSE) {
D("Unexpected MTS pointer update for tracking id: %d",
tracking_id);
}
return;
}
/* This is a "pointer down" event */
_mts_pointer_down(mts_state, tracking_id, x, y, pressure);
} else if (pressure == 0) {
/* This is a "pointer up" event */
_mts_pointer_up(mts_state, slot_index);
} else {
/* This is a "pointer move" event */
_mts_pointer_move(mts_state, slot_index, x, y, pressure);
}
}
int
multitouch_get_max_slot()
{
return MTS_POINTERS_NUM - 1;
}