blob: 4cf4fee3d6f00b650cc79d924f0c07bb8168bcbe [file] [log] [blame]
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "qemu-common.h"
David 'Digit' Turnerd4e803c2013-12-14 23:45:50 +010018#include "android/user-events.h"
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -080019#include "android/display-core.h"
20#include "android/hw-events.h"
21#include "android/charmap.h"
22#include "android/globals.h" /* for android_hw */
23#include "android/utils/misc.h"
24#include "android/utils/debug.h"
25#include "android/multitouch-screen.h"
26
27#define E(...) derror(__VA_ARGS__)
28#define W(...) dwarning(__VA_ARGS__)
29#define D(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__)
30#define D_ACTIVE VERBOSE_CHECK(mtscreen)
31
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -070032#define TRACE_ON 1
33
34#if TRACE_ON
35#define T(...) VERBOSE_PRINT(mtscreen,__VA_ARGS__)
36#else
37#define T(...)
38#endif
39
40
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -080041/* Maximum number of pointers, supported by multi-touch emulation. */
42#define MTS_POINTERS_NUM 10
43/* Signals that pointer is not tracked (or is "up"). */
44#define MTS_POINTER_UP -1
45/* Special tracking ID for a mouse pointer. */
46#define MTS_POINTER_MOUSE -2
47
48/* Describes state of a multi-touch pointer */
49typedef struct MTSPointerState {
50 /* Tracking ID assigned to the pointer by an app emulating multi-touch. */
51 int tracking_id;
52 /* X - coordinate of the tracked pointer. */
53 int x;
54 /* Y - coordinate of the tracked pointer. */
55 int y;
56 /* Current pressure value. */
57 int pressure;
58} MTSPointerState;
59
60/* Describes state of an emulated multi-touch screen. */
61typedef struct MTSState {
62 /* Multi-touch port connected to the device. */
63 AndroidMTSPort* mtsp;
64 /* Emulator's display state. */
65 DisplayState* ds;
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -080066 /* Number of tracked pointers. */
67 int tracked_ptr_num;
68 /* Index in the 'tracked_pointers' array of the last pointer for which
69 * ABS_MT_SLOT was sent. -1 indicates that no slot selection has been made
70 * yet. */
71 int current_slot;
72 /* Accumulator for ABS_TOUCH_MAJOR value. */
73 int touch_major;
74 /* Array of multi-touch pointers. */
75 MTSPointerState tracked_pointers[MTS_POINTERS_NUM];
76 /* Header collecting framebuffer information and updates. */
77 MTFrameHeader fb_header;
78 /* Boolean value indicating if framebuffer updates are currently being
79 * transferred to the application running on the device. */
80 int fb_transfer_in_progress;
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -070081 /* Indicates direction in which lines are arranged in the framebuffer. If
82 * this value is negative, lines are arranged in bottom-up format (i.e. the
83 * bottom line is at the beginning of the buffer). */
84 int ydir;
85 /* Current framebuffer pointer. */
86 uint8_t* current_fb;
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -080087} MTSState;
88
89/* Default multi-touch screen descriptor */
Vladimir Chtchetkine6df71192012-05-15 07:59:51 -070090static MTSState _MTSState = { 0 };
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -080091
92/* Pushes event to the event device. */
93static void
94_push_event(int type, int code, int value)
95{
96 user_event_generic(type, code, value);
97}
98
99/* Gets an index in the MTS's tracking pointers array MTS for the given
100 * tracking id.
101 * Return:
102 * Index of a matching entry in the MTS's tracking pointers array, or -1 if
103 * matching entry was not found.
104 */
105static int
106_mtsstate_get_pointer_index(const MTSState* mts_state, int tracking_id)
107{
108 int index;
109 for (index = 0; index < MTS_POINTERS_NUM; index++) {
110 if (mts_state->tracked_pointers[index].tracking_id == tracking_id) {
111 return index;
112 }
113 }
114 return -1;
115}
116
117/* Gets an index of the first untracking pointer in the MTS's tracking pointers
118 * array.
119 * Return:
120 * An index of the first untracking pointer, or -1 if all pointers are tracked.
121 */
122static int
123_mtsstate_get_available_pointer_index(const MTSState* mts_state)
124{
125 return _mtsstate_get_pointer_index(mts_state, MTS_POINTER_UP);
126}
127
128/* Handles a "pointer down" event
129 * Param:
130 * mts_state - MTS state descriptor.
131 * tracking_id - Tracking ID of the "downed" pointer.
132 * x, y - "Downed" pointer coordinates,
133 * pressure - Pressure value for the pointer.
134 */
135static void
136_mts_pointer_down(MTSState* mts_state, int tracking_id, int x, int y, int pressure)
137{
138 /* Get first available slot for the new pointer. */
139 const int slot_index = _mtsstate_get_available_pointer_index(mts_state);
140
141 /* Make sure there is a place for the pointer. */
142 if (slot_index >= 0) {
143 /* Initialize pointer's entry. */
144 mts_state->tracked_ptr_num++;
145 mts_state->tracked_pointers[slot_index].tracking_id = tracking_id;
146 mts_state->tracked_pointers[slot_index].x = x;
147 mts_state->tracked_pointers[slot_index].y = y;
148 mts_state->tracked_pointers[slot_index].pressure = pressure;
149
150 /* Send events indicating a "pointer down" to the EventHub */
151 /* Make sure that correct slot is selected. */
152 if (slot_index != mts_state->current_slot) {
153 _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
154 }
155 _push_event(EV_ABS, ABS_MT_TRACKING_ID, slot_index);
156 _push_event(EV_ABS, ABS_MT_TOUCH_MAJOR, ++mts_state->touch_major);
157 _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
158 _push_event(EV_ABS, ABS_MT_POSITION_X, x);
159 _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
160 _push_event(EV_SYN, SYN_REPORT, 0);
161 mts_state->current_slot = slot_index;
162 } else {
163 D("MTS pointer count is exceeded.");
164 return;
165 }
166}
167
168/* Handles a "pointer up" event
169 * Param:
170 * mts_state - MTS state descriptor.
171 * slot_index - Pointer's index in the MTS's array of tracked pointers.
172 */
173static void
174_mts_pointer_up(MTSState* mts_state, int slot_index)
175{
176 /* Make sure that correct slot is selected. */
177 if (slot_index != mts_state->current_slot) {
178 _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
179 }
180
181 /* Send event indicating "pointer up" to the EventHub. */
182 _push_event(EV_ABS, ABS_MT_TRACKING_ID, -1);
183 _push_event(EV_SYN, SYN_REPORT, 0);
184
185 /* Update MTS descriptor, removing the tracked pointer. */
186 mts_state->tracked_pointers[slot_index].tracking_id = MTS_POINTER_UP;
187 mts_state->tracked_pointers[slot_index].x = 0;
188 mts_state->tracked_pointers[slot_index].y = 0;
189 mts_state->tracked_pointers[slot_index].pressure = 0;
190
191 /* Since current slot is no longer tracked, make sure we will do a "select"
192 * next time we send events to the EventHub. */
193 mts_state->current_slot = -1;
194 mts_state->tracked_ptr_num--;
195 assert(mts_state->tracked_ptr_num >= 0);
196}
197
198/* Handles a "pointer move" event
199 * Param:
200 * mts_state - MTS state descriptor.
201 * slot_index - Pointer's index in the MTS's array of tracked pointers.
202 * x, y - New pointer coordinates,
203 * pressure - Pressure value for the pointer.
204 */
205static void
206_mts_pointer_move(MTSState* mts_state, int slot_index, int x, int y, int pressure)
207{
208 MTSPointerState* ptr_state = &mts_state->tracked_pointers[slot_index];
209
210 /* Make sure that coordinates have really changed. */
211 if (ptr_state->x == x && ptr_state->y == y) {
212 /* Coordinates didn't change. Bail out. */
213 return;
214 }
215
216 /* Make sure that the right slot is selected. */
217 if (slot_index != mts_state->current_slot) {
218 _push_event(EV_ABS, ABS_MT_SLOT, slot_index);
219 mts_state->current_slot = slot_index;
220 }
221
222 /* Push the changes down. */
223 if (ptr_state->pressure != pressure && pressure != 0) {
224 _push_event(EV_ABS, ABS_MT_PRESSURE, pressure);
225 ptr_state->pressure = pressure;
226 }
227 if (ptr_state->x != x) {
228 _push_event(EV_ABS, ABS_MT_POSITION_X, x);
229 ptr_state->x = x;
230 }
231 if (ptr_state->y != y) {
232 _push_event(EV_ABS, ABS_MT_POSITION_Y, y);
233 ptr_state->y = y;
234 }
235 _push_event(EV_SYN, SYN_REPORT, 0);
236}
237
238/********************************************************************************
239 * Multi-touch API
240 *******************************************************************************/
241
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700242/* Multi-touch service initialization flag. */
243static int _is_mt_initialized = 0;
244
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800245/* Callback that is invoked when framebuffer update has been transmitted to the
246 * device. */
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700247static AsyncIOAction
248_on_fb_sent(void* opaque, SDKCtlDirectPacket* packet, AsyncIOState status)
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800249{
250 MTSState* const mts_state = (MTSState*)opaque;
251
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700252 if (status == ASIO_STATE_SUCCEEDED) {
253 /* Lets see if we have accumulated more changes while transmission has been
254 * in progress. */
Vladimir Chtchetkine1129b0b2012-05-15 09:01:52 -0700255 if (mts_state->fb_header.w && mts_state->fb_header.h &&
256 !mts_state->fb_transfer_in_progress) {
257 mts_state->fb_transfer_in_progress = 1;
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700258 /* Send accumulated updates. */
259 if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
260 mts_state->current_fb, _on_fb_sent, mts_state,
261 mts_state->ydir)) {
262 mts_state->fb_transfer_in_progress = 0;
263 }
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800264 }
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800265 }
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700266
267 return ASIO_ACTION_DONE;
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800268}
269
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700270/* Common handler for framebuffer updates invoked by both, software, and OpenGLES
271 * renderers.
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800272 */
273static void
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700274_mt_fb_common_update(MTSState* mts_state, int x, int y, int w, int h)
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800275{
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800276 if (mts_state->fb_header.w == 0 && mts_state->fb_header.h == 0) {
277 /* First update after previous one has been transmitted to the device. */
278 mts_state->fb_header.x = x;
279 mts_state->fb_header.y = y;
280 mts_state->fb_header.w = w;
281 mts_state->fb_header.h = h;
282 } else {
283 /*
284 * Accumulate framebuffer changes in the header.
285 */
286
287 /* "right" and "bottom" coordinates of the current update. */
288 int right = mts_state->fb_header.x + mts_state->fb_header.w;
289 int bottom = mts_state->fb_header.y + mts_state->fb_header.h;
290
291 /* "right" and "bottom" coordinates of the new update. */
292 const int new_right = x + w;
293 const int new_bottom = y + h;
294
295 /* Accumulate changed rectangle coordinates in the header. */
296 if (mts_state->fb_header.x > x) {
297 mts_state->fb_header.x = x;
298 }
299 if (mts_state->fb_header.y > y) {
300 mts_state->fb_header.y = y;
301 }
302 if (right < new_right) {
303 right = new_right;
304 }
305 if (bottom < new_bottom) {
306 bottom = new_bottom;
307 }
308 mts_state->fb_header.w = right - mts_state->fb_header.x;
309 mts_state->fb_header.h = bottom - mts_state->fb_header.y;
310 }
311
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800312 /* We will send updates to the device only after previous transmission is
313 * completed. */
314 if (!mts_state->fb_transfer_in_progress) {
315 mts_state->fb_transfer_in_progress = 1;
316 if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700317 mts_state->current_fb, _on_fb_sent, mts_state,
318 mts_state->ydir)) {
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800319 mts_state->fb_transfer_in_progress = 0;
320 }
321 }
322}
323
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700324/* A callback invoked on framebuffer updates by software renderer.
325 * Param:
326 * opaque - MTSState instance.
327 * x, y, w, h - Defines an updated rectangle inside the framebuffer.
328 */
329static void
330_mt_fb_update(void* opaque, int x, int y, int w, int h)
331{
332 MTSState* const mts_state = (MTSState*)opaque;
333 const DisplaySurface* const surface = mts_state->ds->surface;
334
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700335 T("Multi-touch: Software renderer framebuffer update: %d:%d -> %dx%d",
336 x, y, w, h);
337
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700338 /* TODO: For sofware renderer general framebuffer properties can change on
339 * the fly. Find a callback that can catch that. For now, just copy FB
340 * properties over in every FB update. */
341 mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
342 mts_state->fb_header.bpl = surface->linesize;
343 mts_state->fb_header.disp_width = surface->width;
344 mts_state->fb_header.disp_height = surface->height;
345 mts_state->current_fb = surface->data;
346 mts_state->ydir = 1;
347
348 _mt_fb_common_update(mts_state, x, y, w, h);
349}
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700350
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700351void
352multitouch_opengles_fb_update(void* context,
353 int w, int h, int ydir,
354 int format, int type,
355 unsigned char* pixels)
356{
357 MTSState* const mts_state = &_MTSState;
358
359 /* Make sure MT port is initialized. */
360 if (!_is_mt_initialized) {
361 return;
362 }
363
Vladimir Chtchetkine7136b052012-04-10 13:39:24 -0700364 T("Multi-touch: openGLES framebuffer update: 0:0 -> %dx%d", w, h);
365
Vladimir Chtchetkine1a820e92012-04-11 13:22:48 -0700366 /* GLES format is always RGBA8888 */
367 mts_state->fb_header.bpp = 4;
368 mts_state->fb_header.bpl = 4 * w;
369 mts_state->fb_header.disp_width = w;
370 mts_state->fb_header.disp_height = h;
371 mts_state->current_fb = pixels;
372 mts_state->ydir = ydir;
373
374 /* GLES emulator alwas update the entire framebuffer. */
375 _mt_fb_common_update(mts_state, 0, 0, w, h);
376}
377
Vladimir Chtchetkine1129b0b2012-05-15 09:01:52 -0700378void
379multitouch_refresh_screen(void)
Vladimir Chtchetkine6df71192012-05-15 07:59:51 -0700380{
381 MTSState* const mts_state = &_MTSState;
382
383 /* Make sure MT port is initialized. */
384 if (!_is_mt_initialized) {
385 return;
386 }
387
388 /* Lets see if any updates have been received so far. */
389 if (NULL != mts_state->current_fb) {
390 _mt_fb_common_update(mts_state, 0, 0, mts_state->fb_header.disp_width,
391 mts_state->fb_header.disp_height);
392 }
393}
394
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800395void
Vladimir Chtchetkine1129b0b2012-05-15 09:01:52 -0700396multitouch_fb_updated(void)
397{
398 MTSState* const mts_state = &_MTSState;
399
400 /* This concludes framebuffer update. */
401 mts_state->fb_transfer_in_progress = 0;
402
403 /* Lets see if we have accumulated more changes while transmission has been
404 * in progress. */
405 if (mts_state->fb_header.w && mts_state->fb_header.h) {
406 mts_state->fb_transfer_in_progress = 1;
407 /* Send accumulated updates. */
408 if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
409 mts_state->current_fb, _on_fb_sent, mts_state,
410 mts_state->ydir)) {
411 mts_state->fb_transfer_in_progress = 0;
412 }
413 }
414}
415
416void
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800417multitouch_init(AndroidMTSPort* mtsp)
418{
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800419 if (!_is_mt_initialized) {
420 MTSState* const mts_state = &_MTSState;
421 DisplayState* const ds = get_displaystate();
422 DisplayUpdateListener* dul;
423 int index;
424
425 /*
426 * Initialize the descriptor.
427 */
428
429 memset(mts_state, 0, sizeof(MTSState));
430 mts_state->tracked_ptr_num = 0;
431 mts_state->current_slot = -1;
432 for (index = 0; index < MTS_POINTERS_NUM; index++) {
433 mts_state->tracked_pointers[index].tracking_id = MTS_POINTER_UP;
434 }
Vladimir Chtchetkine8dd31e82012-02-15 17:16:04 -0800435 mts_state->mtsp = mtsp;
436 mts_state->fb_header.header_size = sizeof(MTFrameHeader);
437 mts_state->fb_transfer_in_progress = 0;
438
439 /*
440 * Set framebuffer update listener.
441 */
442
443 ANEW0(dul);
444 dul->opaque = &_MTSState;
445 dul->dpy_update = _mt_fb_update;
446
447 /* Initialize framebuffer information in the screen descriptor. */
448 mts_state->ds = ds;
449 mts_state->fb_header.disp_width = ds->surface->width;
450 mts_state->fb_header.disp_height = ds->surface->height;
451 mts_state->fb_header.x = mts_state->fb_header.y = 0;
452 mts_state->fb_header.w = mts_state->fb_header.h = 0;
453 mts_state->fb_header.bpp = ds->surface->pf.bytes_per_pixel;
454 mts_state->fb_header.bpl = ds->surface->linesize;
455 mts_state->fb_transfer_in_progress = 0;
456
457 register_displayupdatelistener(ds, dul);
458
459 _is_mt_initialized = 1;
460 }
461}
462
463void
464multitouch_update_pointer(MTESource source,
465 int tracking_id,
466 int x,
467 int y,
468 int pressure)
469{
470 MTSState* const mts_state = &_MTSState;
471
472 /* Assign a fixed tracking ID to the mouse pointer. */
473 if (source == MTES_MOUSE) {
474 tracking_id = MTS_POINTER_MOUSE;
475 }
476
477 /* Find the tracked pointer for the tracking ID. */
478 const int slot_index = _mtsstate_get_pointer_index(mts_state, tracking_id);
479 if (slot_index < 0) {
480 /* This is the first time the pointer is seen. Must be "pressed",
481 * otherwise it's "hoovering", which we don't support yet. */
482 if (pressure == 0) {
483 if (tracking_id != MTS_POINTER_MOUSE) {
484 D("Unexpected MTS pointer update for tracking id: %d",
485 tracking_id);
486 }
487 return;
488 }
489
490 /* This is a "pointer down" event */
491 _mts_pointer_down(mts_state, tracking_id, x, y, pressure);
492 } else if (pressure == 0) {
493 /* This is a "pointer up" event */
494 _mts_pointer_up(mts_state, slot_index);
495 } else {
496 /* This is a "pointer move" event */
497 _mts_pointer_move(mts_state, slot_index, x, y, pressure);
498 }
499}
500
501int
502multitouch_get_max_slot()
503{
504 return MTS_POINTERS_NUM - 1;
505}