aboutsummaryrefslogtreecommitdiffstats
path: root/android/multitouch-screen.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/multitouch-screen.c')
-rw-r--r--android/multitouch-screen.c422
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;
+}