1 /*
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"
18 #include "utils/panic.h"
19 #include "android/hw-events.h"
20 #include "android/charmap.h"
21 #include "android/multitouch-screen.h"
22 #include "android/multitouch-port.h"
23 #include "android/globals.h" /* for android_hw */
24 #include "android/utils/misc.h"
25 #include "android/utils/jpeg-compress.h"
26
27 #define E(...) derror(__VA_ARGS__)
28 #define W(...) dwarning(__VA_ARGS__)
29 #define D(...) VERBOSE_PRINT(mtport,__VA_ARGS__)
30 #define D_ACTIVE VERBOSE_CHECK(mtport)
31
32 /* Query timeout in milliseconds. */
33 #define MTSP_QUERY_TIMEOUT 3000
34 #define MTSP_MAX_MSG 2048
35 #define MTSP_MAX_EVENT 2048
36
37 /* Multi-touch port descriptor. */
38 struct AndroidMTSPort {
39 /* Caller identifier. */
40 void* opaque;
41 /* Connected android device. */
42 AndroidDevice* device;
43 /* Initialized JPEG compressor instance. */
44 AJPEGDesc* jpeg_compressor;
45 /* Connection status: 1 connected, 0 - disconnected. */
46 int is_connected;
47 /* Buffer where to receive multitouch messages. */
48 char mts_msg[MTSP_MAX_MSG];
49 /* Buffer where to receive multitouch events. */
50 char events[MTSP_MAX_EVENT];
51 };
52
53 /* Destroys and frees the descriptor. */
54 static void
_mts_port_free(AndroidMTSPort * mtsp)55 _mts_port_free(AndroidMTSPort* mtsp)
56 {
57 if (mtsp != NULL) {
58 if (mtsp->jpeg_compressor != NULL) {
59 jpeg_compressor_destroy(mtsp->jpeg_compressor);
60 }
61 if (mtsp->device != NULL) {
62 android_device_destroy(mtsp->device);
63 }
64 AFREE(mtsp);
65 }
66 }
67
68 /********************************************************************************
69 * Multi-touch action handlers
70 *******************************************************************************/
71
72 /*
73 * Although there are a lot of similarities in the way the handlers below are
74 * implemented, for the sake of tracing / debugging it's better to have a
75 * separate handler for each distinctive action.
76 */
77
78 /* First pointer down event handler. */
79 static void
_on_action_down(int tracking_id,int x,int y,int pressure)80 _on_action_down(int tracking_id, int x, int y, int pressure)
81 {
82 multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
83 }
84
85 /* Last pointer up event handler. */
86 static void
_on_action_up(int tracking_id)87 _on_action_up(int tracking_id)
88 {
89 multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
90 }
91
92 /* Pointer down event handler. */
93 static void
_on_action_pointer_down(int tracking_id,int x,int y,int pressure)94 _on_action_pointer_down(int tracking_id, int x, int y, int pressure)
95 {
96 multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
97 }
98
99 /* Pointer up event handler. */
100 static void
_on_action_pointer_up(int tracking_id)101 _on_action_pointer_up(int tracking_id)
102 {
103 multitouch_update_pointer(MTES_DEVICE, tracking_id, 0, 0, 0);
104 }
105
106 /* Pointer move event handler. */
107 static void
_on_action_move(int tracking_id,int x,int y,int pressure)108 _on_action_move(int tracking_id, int x, int y, int pressure)
109 {
110 multitouch_update_pointer(MTES_DEVICE, tracking_id, x, y, pressure);
111 }
112
113 /********************************************************************************
114 * Multi-touch event handlers
115 *******************************************************************************/
116
117 /* Handles "pointer move" event. */
118 static void
_on_move(const char * param)119 _on_move(const char* param)
120 {
121 const char* pid = param;
122 D(">>> MOVE: %s", param);
123 while (pid && *pid) {
124 int pid_val, x, y, pressure = 0;
125 if (!get_token_value_int(pid, "pid", &pid_val) &&
126 !get_token_value_int(pid, "x", &x) &&
127 !get_token_value_int(pid, "y", &y)) {
128 get_token_value_int(pid, "pressure", &pressure);
129 _on_action_move(pid_val, x, y, pressure);
130 pid = strstr(pid + 1, "pid");
131 } else {
132 break;
133 }
134 }
135 }
136
137 /* Handles "first pointer down" event. */
138 static void
_on_down(const char * param)139 _on_down(const char* param)
140 {
141 int pid_val, x, y, pressure = 0;
142 D(">>> 1-ST DOWN: %s", param);
143 if (!get_token_value_int(param, "pid", &pid_val) &&
144 !get_token_value_int(param, "x", &x) &&
145 !get_token_value_int(param, "y", &y)) {
146 get_token_value_int(param, "pressure", &pressure);
147 _on_action_down(pid_val, x, y, pressure);
148 } else {
149 W("Invalid parameters '%s' for MTS 'down' event", param);
150 }
151 }
152
153 /* Handles "last pointer up" event. */
154 static void
_on_up(const char * param)155 _on_up(const char* param)
156 {
157 int pid_val;
158 D(">>> LAST UP: %s", param);
159 if (!get_token_value_int(param, "pid", &pid_val)) {
160 _on_action_up(pid_val);
161 } else {
162 W("Invalid parameters '%s' for MTS 'up' event", param);
163 }
164 }
165
166 /* Handles "next pointer down" event. */
167 static void
_on_pdown(const char * param)168 _on_pdown(const char* param)
169 {
170 int pid_val, x, y, pressure = 0;
171 D(">>> DOWN: %s", param);
172 if (!get_token_value_int(param, "pid", &pid_val) &&
173 !get_token_value_int(param, "x", &x) &&
174 !get_token_value_int(param, "y", &y)) {
175 get_token_value_int(param, "pressure", &pressure);
176 _on_action_pointer_down(pid_val, x, y, pressure);
177 } else {
178 W("Invalid parameters '%s' for MTS 'pointer down' event", param);
179 }
180 }
181
182 /* Handles "next pointer up" event. */
183 static void
_on_pup(const char * param)184 _on_pup(const char* param)
185 {
186 int pid_val;
187 D(">>> UP: %s", param);
188 if (!get_token_value_int(param, "pid", &pid_val)) {
189 _on_action_pointer_up(pid_val);
190 } else {
191 W("Invalid parameters '%s' for MTS 'up' event", param);
192 }
193 }
194
195 /********************************************************************************
196 * Device communication callbacks
197 *******************************************************************************/
198
199 /* Main event handler.
200 * This routine is invoked when an event message has been received from the
201 * device.
202 */
203 static void
_on_event_received(void * opaque,AndroidDevice * ad,char * msg,int msgsize)204 _on_event_received(void* opaque, AndroidDevice* ad, char* msg, int msgsize)
205 {
206 char* action;
207 int res;
208 AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
209
210 if (errno) {
211 D("Multi-touch notification has failed: %s", strerror(errno));
212 return;
213 }
214
215 /* Dispatch the event to an appropriate handler. */
216 res = get_token_value_alloc(msg, "action", &action);
217 if (!res) {
218 const char* param = strchr(msg, ' ');
219 if (param) {
220 param++;
221 }
222 if (!strcmp(action, "move")) {
223 _on_move(param);
224 } else if (!strcmp(action, "down")) {
225 _on_down(param);
226 } else if (!strcmp(action, "up")) {
227 _on_up(param);
228 } else if (!strcmp(action, "pdown")) {
229 _on_pdown(param);
230 } else if (!strcmp(action, "pup")) {
231 _on_pup(param);
232 } else {
233 D("Unknown multi-touch event action '%s'", action);
234 }
235 free(action);
236 }
237
238 /* Listen to the next event. */
239 android_device_listen(ad, mtsp->events, sizeof(mtsp->events),
240 _on_event_received);
241 }
242
243 /* A callback that is invoked when android device is connected (i.e. both,
244 * command and event channels have been established).
245 * Param:
246 * opaque - AndroidMTSPort instance.
247 * ad - Android device used by this port.
248 * failure - Connections status.
249 */
250 static void
_on_device_connected(void * opaque,AndroidDevice * ad,int failure)251 _on_device_connected(void* opaque, AndroidDevice* ad, int failure)
252 {
253 if (!failure) {
254 AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
255 mtsp->is_connected = 1;
256 D("Multi-touch emulation has started");
257 android_device_listen(mtsp->device, mtsp->events, sizeof(mtsp->events),
258 _on_event_received);
259 mts_port_start(mtsp);
260 }
261 }
262
263 /* Invoked when an I/O failure occurs on a socket.
264 * Note that this callback will not be invoked on connection failures.
265 * Param:
266 * opaque - AndroidMTSPort instance.
267 * ad - Android device instance
268 * ads - Connection socket where failure has occured.
269 * failure - Contains 'errno' indicating the reason for failure.
270 */
271 static void
_on_io_failure(void * opaque,AndroidDevice * ad,int failure)272 _on_io_failure(void* opaque, AndroidDevice* ad, int failure)
273 {
274 AndroidMTSPort* mtsp = (AndroidMTSPort*)opaque;
275 E("Multi-touch port got disconnected: %s", strerror(failure));
276 mtsp->is_connected = 0;
277 android_device_disconnect(ad);
278
279 /* Try to reconnect again. */
280 android_device_connect_async(ad, _on_device_connected);
281 }
282
283 /********************************************************************************
284 * MTS port API
285 *******************************************************************************/
286
287 AndroidMTSPort*
mts_port_create(void * opaque)288 mts_port_create(void* opaque)
289 {
290 AndroidMTSPort* mtsp;
291 int res;
292
293 ANEW0(mtsp);
294 mtsp->opaque = opaque;
295 mtsp->is_connected = 0;
296
297 /* Initialize default MTS descriptor. */
298 multitouch_init(mtsp);
299
300 /* Create JPEG compressor. Put "$BLOB:%09d\0" + MTFrameHeader header in front
301 * of the compressed data. this way we will have entire query ready to be
302 * transmitted to the device. */
303 mtsp->jpeg_compressor = jpeg_compressor_create(16 + sizeof(MTFrameHeader), 4096);
304
305 mtsp->device = android_device_init(mtsp, AD_MULTITOUCH_PORT, _on_io_failure);
306 if (mtsp->device == NULL) {
307 _mts_port_free(mtsp);
308 return NULL;
309 }
310
311 res = android_device_connect_async(mtsp->device, _on_device_connected);
312 if (res != 0) {
313 mts_port_destroy(mtsp);
314 return NULL;
315 }
316
317 return mtsp;
318 }
319
320 void
mts_port_destroy(AndroidMTSPort * mtsp)321 mts_port_destroy(AndroidMTSPort* mtsp)
322 {
323 _mts_port_free(mtsp);
324 }
325
326 int
mts_port_is_connected(AndroidMTSPort * mtsp)327 mts_port_is_connected(AndroidMTSPort* mtsp)
328 {
329 return mtsp->is_connected;
330 }
331
332 int
mts_port_start(AndroidMTSPort * mtsp)333 mts_port_start(AndroidMTSPort* mtsp)
334 {
335 char qresp[MTSP_MAX_MSG];
336 char query[256];
337 AndroidHwConfig* config = android_hw;
338
339 /* Query the device to start capturing multi-touch events, also providing
340 * the device with width / height of the emulator's screen. This is required
341 * so device can properly adjust multi-touch event coordinates, and display
342 * emulator's framebuffer. */
343 snprintf(query, sizeof(query), "start:%dx%d",
344 config->hw_lcd_width, config->hw_lcd_height);
345 int res = android_device_query(mtsp->device, query, qresp, sizeof(qresp),
346 MTSP_QUERY_TIMEOUT);
347 if (!res) {
348 /* By protocol device should reply with its view dimensions. */
349 if (*qresp) {
350 int width, height;
351 if (sscanf(qresp, "%dx%d", &width, &height) == 2) {
352 multitouch_set_device_screen_size(width, height);
353 D("Multi-touch emulation has started. Device dims: %dx%d",
354 width, height);
355 } else {
356 E("Unexpected reply to MTS 'start' query: %s", qresp);
357 android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
358 MTSP_QUERY_TIMEOUT);
359 res = -1;
360 }
361 } else {
362 E("MTS protocol error: no reply to query 'start'");
363 android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
364 MTSP_QUERY_TIMEOUT);
365 res = -1;
366 }
367 } else {
368 if (errno) {
369 D("Query 'start' failed on I/O: %s", strerror(errno));
370 } else {
371 D("Query 'start' failed on device: %s", qresp);
372 }
373 }
374 return res;
375 }
376
377 int
mts_port_stop(AndroidMTSPort * mtsp)378 mts_port_stop(AndroidMTSPort* mtsp)
379 {
380 char qresp[MTSP_MAX_MSG];
381 const int res =
382 android_device_query(mtsp->device, "stop", qresp, sizeof(qresp),
383 MTSP_QUERY_TIMEOUT);
384 if (res) {
385 if (errno) {
386 D("Query 'stop' failed on I/O: %s", strerror(errno));
387 } else {
388 D("Query 'stop' failed on device: %s", qresp);
389 }
390 }
391
392 return res;
393 }
394
395 /********************************************************************************
396 * Handling framebuffer updates
397 *******************************************************************************/
398
399 /* Compresses a framebuffer region into JPEG image.
400 * Param:
401 * mtsp - Multi-touch port descriptor with initialized JPEG compressor.
402 * fmt Descriptor for framebuffer region to compress.
403 * fb Beginning of the framebuffer.
404 * jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
405 * value 10 provides pretty decent image for the purpose of multi-touch
406 * emulation.
407 */
408 static void
_fb_compress(const AndroidMTSPort * mtsp,const MTFrameHeader * fmt,const uint8_t * fb,int jpeg_quality,int ydir)409 _fb_compress(const AndroidMTSPort* mtsp,
410 const MTFrameHeader* fmt,
411 const uint8_t* fb,
412 int jpeg_quality,
413 int ydir)
414 {
415 jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
416 fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl,
417 fb, jpeg_quality, ydir);
418 }
419
420 int
mts_port_send_frame(AndroidMTSPort * mtsp,MTFrameHeader * fmt,const uint8_t * fb,async_send_cb cb,void * cb_opaque,int ydir)421 mts_port_send_frame(AndroidMTSPort* mtsp,
422 MTFrameHeader* fmt,
423 const uint8_t* fb,
424 async_send_cb cb,
425 void* cb_opaque,
426 int ydir)
427 {
428 char* query;
429 int blob_size, off;
430
431 /* Make sure that port is connected. */
432 if (!mts_port_is_connected(mtsp)) {
433 return -1;
434 }
435
436 /* Compress framebuffer region. 10% quality seems to be sufficient. */
437 fmt->format = MTFB_JPEG;
438 _fb_compress(mtsp, fmt, fb, 10, ydir);
439
440 /* Total size of the blob: header + JPEG image. */
441 blob_size = sizeof(MTFrameHeader) +
442 jpeg_compressor_get_jpeg_size(mtsp->jpeg_compressor);
443
444 /* Query starts at the beginning of the buffer allocated by the compressor's
445 * destination manager. */
446 query = (char*)jpeg_compressor_get_buffer(mtsp->jpeg_compressor);
447
448 /* Build the $BLOB query to transfer to the device. */
449 snprintf(query, jpeg_compressor_get_header_size(mtsp->jpeg_compressor),
450 "$BLOB:%09d", blob_size);
451 off = strlen(query) + 1;
452
453 /* Copy framebuffer update header to the query. */
454 memcpy(query + off, fmt, sizeof(MTFrameHeader));
455
456 /* Zeroing the rectangle in the update header we indicate that it contains
457 * no updates. */
458 fmt->x = fmt->y = fmt->w = fmt->h = 0;
459
460 /* Initiate asynchronous transfer of the updated framebuffer rectangle. */
461 if (android_device_send_async(mtsp->device, query, off + blob_size, 0, cb, cb_opaque)) {
462 D("Unable to send query '%s': %s", query, strerror(errno));
463 return -1;
464 }
465
466 return 0;
467 }
468