• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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