diff options
Diffstat (limited to 'android/multitouch-screen.c')
-rw-r--r-- | android/multitouch-screen.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c new file mode 100644 index 0000000..e1a9a8a --- /dev/null +++ b/android/multitouch-screen.c @@ -0,0 +1,422 @@ +/* + * 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 "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) + +/* 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; + /* Screen width of the device that emulates multi-touch. */ + int device_width; + /* Screen height of the device that emulates multi-touch. */ + int device_height; + /* 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; +} MTSState; + +/* Default multi-touch screen descriptor */ +static MTSState _MTSState; + +/* 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 + *******************************************************************************/ + +/* Callback that is invoked when framebuffer update has been transmitted to the + * device. */ +static void +_on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent) +{ + MTSState* const mts_state = (MTSState*)opaque; + + /* 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) { + /* Send accumulated updates. */ + if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header, + mts_state->ds->surface->data, _on_fb_sent, + mts_state)) { + mts_state->fb_transfer_in_progress = 0; + } + } else { + /* Framebuffer transfer is completed, and no more updates are pending. */ + mts_state->fb_transfer_in_progress = 0; + } +} + +/* A callback invoked on framebuffer updates. + * 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; + + 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; + } + + /* TODO: Looks like 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; + + /* 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, + surface->data, _on_fb_sent, mts_state)) { + mts_state->fb_transfer_in_progress = 0; + } + } +} + +void +multitouch_init(AndroidMTSPort* mtsp) +{ + /* Multi-touch service initialization flag. */ + static int _is_mt_initialized = 0; + + 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->device_width = android_hw->hw_lcd_width; + mts_state->device_height = android_hw->hw_lcd_height; + 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; +} + +void +multitouch_set_device_screen_size(int width, int height) +{ + MTSState* const mts_state = &_MTSState; + + mts_state->device_width = width; + mts_state->device_height = height; +} |