Merged with devel
[platform/core/uifw/e17-extra-modules.git] / screen-reader / src / e_mod_main.c
1 #include <Elementary.h>
2 #include <vconf.h>
3 #include "e.h"
4 #include "e_mod_main.h"
5 #define HISTORY_MAX 8
6 #define DEBUG_INFO 0
7
8 #if DEBUG_INFO
9   #define INFO(cov, txt) \
10     evas_object_text_text_set(cov->text, txt); \
11     INF("%s", txt)
12 #else
13   #define INFO(cov, txt) INF("%s -> %x", txt, target_win)
14 #endif
15
16 #define MOUSE_BUTTON_DOWN 0
17 #define MOUSE_MOVE 1
18 #define MOUSE_BUTTON_UP 2
19
20 typedef struct
21 {
22    E_Zone         *zone;
23    Ecore_X_Window  win;
24    Ecore_X_Window  down_win;
25    Ecore_Timer    *timer;
26    Ecore_Timer    *double_down_timer;
27    Ecore_Timer    *tap_timer;
28    Evas_Object    *info;
29    Evas_Object    *text;
30    int             x, y, dx, dy, mx, my;
31    int             mouse_history[HISTORY_MAX];
32    unsigned int    dt;
33    unsigned int    n_taps;
34    Eina_Inarray   *two_finger_move;
35    Eina_Inlist    *history;
36
37    Ecore_X_Atom    atom_control_panel_open;
38    Ecore_X_Atom    atom_app_tray_open;
39
40    Eina_Bool       longpressed : 1;
41    Eina_Bool       two_finger_down : 1;
42    Eina_Bool       three_finger_down : 1;
43    Eina_Bool       mouse_double_down : 1;
44    Eina_Bool       lock_screen : 1;
45 } Cover;
46
47 typedef struct
48 {
49    EINA_INLIST;
50    int             device;
51 } Multi;
52
53 static int g_enable = 0;
54 static Ecore_X_Window target_win = 0;
55 static Ecore_X_Window unfocused_win = 0;
56
57 static Eina_List *covers = NULL;
58 static Eina_List *handlers = NULL;
59 static Ecore_Event_Handler *property_handler = NULL;
60
61 static void _move_module_enable_set(int enable);
62
63 static void
64 _mouse_in_win_get(Cover *cov, int x, int y)
65 {
66    E_Border *bd;
67    Eina_List *l;
68    Ecore_X_Window *skip;
69    Ecore_X_Window win = 0;
70    Cover *cov2;
71    int i;
72
73    skip = alloca(sizeof(Ecore_X_Window) * eina_list_count(covers));
74    i = 0;
75    EINA_LIST_FOREACH(covers, l, cov2)
76      {
77         skip[i] = cov2->win;
78         i++;
79      }
80    win = ecore_x_window_shadow_tree_at_xy_with_skip_get
81      (cov->zone->container->manager->root, x, y, skip, i);
82
83    if (win != target_win)
84      {
85         target_win = win;
86
87         bd = e_border_focused_get();
88         if (bd && (bd->client.win != target_win))
89           unfocused_win = target_win;
90         else
91           unfocused_win = 0;
92      }
93 }
94
95 static unsigned int
96 _win_angle_get(Ecore_X_Window win)
97 {
98    Ecore_X_Window root;
99
100    if (!win) return 0;
101
102    int ret;
103    int count;
104    int angle = 0;
105    unsigned char *prop_data = NULL;
106
107    root = ecore_x_window_root_get(win);
108    ret = ecore_x_window_prop_property_get(root,
109        ECORE_X_ATOM_E_ILLUME_ROTATE_ROOT_ANGLE,
110                          ECORE_X_ATOM_CARDINAL,
111                         32, &prop_data, &count);
112
113    if (ret && prop_data)
114       memcpy (&angle, prop_data, sizeof (int));
115
116    if (prop_data) free (prop_data);
117
118    return angle;
119 }
120
121 static void
122 _cov_data_reset(Cover *cov)
123 {
124    cov->n_taps = 0;
125    cov->longpressed = EINA_FALSE;
126    cov->two_finger_down = EINA_FALSE;
127    cov->two_finger_move = EINA_FALSE;
128    cov->mouse_double_down = EINA_FALSE;
129    cov->three_finger_down = EINA_FALSE;
130    cov->lock_screen = EINA_FALSE;
131
132    if (cov->timer)
133      {
134         ecore_timer_del(cov->timer);
135         cov->timer = NULL;
136      }
137
138    if (cov->double_down_timer)
139      {
140         ecore_timer_del(cov->double_down_timer);
141         cov->double_down_timer = NULL;
142      }
143
144    if (cov->tap_timer)
145      {
146         ecore_timer_del(cov->tap_timer);
147         cov->tap_timer = NULL;
148      }
149 }
150
151 static void
152 _screen_reader_support_check()
153 {
154    int ret;
155    unsigned int val;
156    Eina_List *l;
157    Cover *cov;
158
159    ret = ecore_x_window_prop_card32_get
160       (target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL, &val, 1);
161
162    if ((ret >= 0) && (val == 2))
163      {
164         /* hide input window */
165         EINA_LIST_FOREACH(covers, l, cov)
166           {
167              ecore_x_window_hide(cov->win);
168              _cov_data_reset(cov);
169           }
170
171         _move_module_enable_set(EINA_FALSE);
172      }
173    else
174      {
175         /* show input window */
176         EINA_LIST_FOREACH(covers, l, cov)
177           {
178              ecore_x_window_show(cov->win);
179           }
180
181         _move_module_enable_set(EINA_TRUE);
182      }
183 }
184
185 static void
186 _target_window_find()
187 {
188    Ecore_X_Window win;
189    E_Border *bd;
190    unsigned int val;
191    int ret;
192
193    /* find proper target window to send a meesage */
194    Eina_List *borders, *l;
195
196    win = 0;
197    borders = e_border_client_list();
198    EINA_LIST_REVERSE_FOREACH(borders, l, bd)
199      {
200         if (!bd->visible) continue;
201         if (bd->client.netwm.type == ECORE_X_WINDOW_TYPE_NORMAL) break;
202
203         ret = ecore_x_window_prop_card32_get
204            (bd->client.win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL, &val, 1);
205
206        if ((ret >= 0) && (val == 1))
207          {
208             win = bd->client.win;
209             break;
210          }
211      }
212
213    /* there would be an unfocused window which does not have 'val == 1'
214       such as an window of virtual keyboard. if the window is selected by
215       _mouse_in_win_get(); previously with the unfocused window, the target
216       window should be the unfocused window */
217    if (win) target_win = win;
218    else
219      {
220         if (unfocused_win) target_win = unfocused_win;
221         else
222           {
223              bd = e_border_focused_get();
224              if (bd) target_win = bd->client.win;
225           }
226      }
227 }
228
229 static void
230 _lock_screen_check(Cover *cov)
231 {
232    Ecore_X_Window win;
233    E_Border *bd;
234    const char *name = NULL;
235    const char *clas = NULL;
236
237    Eina_List *borders, *l;
238
239    cov->lock_screen = EINA_FALSE;
240    win = 0;
241    borders = e_border_client_list();
242    EINA_LIST_REVERSE_FOREACH(borders, l, bd)
243      {
244         if (!bd) continue;
245         if (!bd->visible) continue;
246
247         name = bd->client.icccm.name;
248         clas = bd->client.icccm.class;
249
250         if (clas == NULL || name == NULL) continue;
251         if (strncmp(clas,"LOCK_SCREEN",strlen("LOCK_SCREEN"))!= 0) continue;
252         if (strncmp(name,"LOCK_SCREEN",strlen("LOCK_SCREEN"))!= 0) continue;
253
254         INF("lock screen is detected");
255         cov->lock_screen = EINA_TRUE;
256
257         break;
258      }
259 }
260
261 static void
262 _app_tray_open(Cover *cov)
263 {
264    Ecore_X_Window win;
265    E_Border *bd;
266    const char *name = NULL;
267    const char *clas = NULL;
268
269    Eina_List *borders, *l;
270
271    win = 0;
272    borders = e_border_client_list();
273    EINA_LIST_REVERSE_FOREACH(borders, l, bd)
274      {
275         if (!bd) continue;
276         if (!bd->visible) continue;
277
278         /* UTILITY type such as keyboard window could come first, before NORMAL
279            type such as app tray, quickpanel window comes */
280         if (bd->client.netwm.type == ECORE_X_WINDOW_TYPE_UTILITY) continue;
281         if (bd->client.netwm.type != ECORE_X_WINDOW_TYPE_NORMAL) break;
282
283         name = bd->client.icccm.name;
284         clas = bd->client.icccm.class;
285
286         if (clas == NULL || name == NULL) continue;
287         if (strncmp(clas,"MINIAPP_TRAY",strlen("MINIAPP_TRAY"))!= 0) continue;
288         if (strncmp(name,"MINIAPP_TRAY",strlen("MINIAPP_TRAY"))!= 0) continue;
289
290         /* open mini app tray */
291         INF("open app tray");
292         ecore_x_client_message32_send(bd->client.win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
293                                       ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
294                                       bd->client.win,
295                                       cov->atom_app_tray_open,
296                                       0, 0, 0);
297         break;
298      }
299 }
300
301 static void
302 _quickpanel_open(void)
303 {
304    Ecore_X_Window win;
305    E_Border *bd;
306    const char *name = NULL;
307    const char *clas = NULL;
308
309    Eina_List *borders, *l;
310
311    win = 0;
312    borders = e_border_client_list();
313    EINA_LIST_REVERSE_FOREACH(borders, l, bd)
314      {
315         if (!bd) continue;
316         if (!bd->visible) continue;
317
318         /* UTILITY type such as keyboard window could come first, before NORMAL
319            type such as app tray, quickpanel window comes */
320         if (bd->client.netwm.type == ECORE_X_WINDOW_TYPE_UTILITY) continue;
321         if (bd->client.netwm.type != ECORE_X_WINDOW_TYPE_NORMAL) break;
322
323         name = bd->client.icccm.name;
324         clas = bd->client.icccm.class;
325
326         if (clas == NULL || name == NULL) continue;
327         if (strncmp(clas,"QUICKPANEL",strlen("QUICKPANEL"))!= 0) continue;
328         if (strncmp(name,"QUICKPANEL",strlen("QUICKPANEL"))!= 0) continue;
329
330         /* open quickpanel */
331         INF("open quickpanel");
332         ecore_x_e_illume_quickpanel_state_send
333           (ecore_x_e_illume_zone_get(bd->client.win),
334            ECORE_X_ILLUME_QUICKPANEL_STATE_ON);
335
336         /* set unfocused window to quickpanel (unfocused window), otherwise
337           target window would set to focused window in _target_window_find(); */
338         target_win = bd->client.win;
339         unfocused_win = bd->client.win;
340         break;
341      }
342 }
343
344 static void
345 _coordinate_calibrate(Ecore_X_Window win, int *x, int *y)
346 {
347    int tx, ty, w, h;
348    unsigned int angle;
349
350    if (!x) return;
351    if (!y) return;
352
353    angle = _win_angle_get(win);
354    ecore_x_window_geometry_get(win, NULL, NULL, &w, &h);
355
356    tx = *x;
357    ty = *y;
358
359    switch (angle)
360      {
361       case 90:
362         *x = h - ty;
363         *y = tx;
364         break;
365
366       case 180:
367         *x = w - tx;
368         *y = h - ty;
369         break;
370
371       case 270:
372         *x = ty;
373         *y = w - tx;
374         break;
375
376       default:
377         break;
378      }
379 }
380
381 static void
382 _mouse_win_fake_tap(Cover *cov, Ecore_Event_Mouse_Button *ev)
383 {
384    int x, y;
385
386    /* find target window to send message */
387    _mouse_in_win_get(cov, ev->root.x, ev->root.y);
388
389    ecore_x_pointer_xy_get(target_win, &x, &y);
390    ecore_x_mouse_in_send(target_win, x, y);
391    ecore_x_mouse_move_send(target_win, x, y);
392    ecore_x_mouse_down_send(target_win, x, y, 1);
393    ecore_x_mouse_up_send(target_win, x, y, 1);
394    ecore_x_mouse_out_send(target_win, x, y);
395 }
396
397 static void
398 _message_control_panel_open_send(Cover *cov)
399 {
400    ecore_x_client_message32_send(target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
401                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
402                                  target_win,
403                                  cov->atom_control_panel_open,
404                                  0, 0, 0);
405 }
406
407 static void
408 _message_back_send(Cover *cov)
409 {
410    ecore_x_client_message32_send(target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
411                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
412                                  target_win,
413                                  ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_BACK,
414                                  0, 0, 0);
415 }
416
417 static void
418 _message_scroll_send(Cover *cov, int type)
419 {
420    int x, y;
421    Ecore_X_Atom atom;
422
423    atom = ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_SCROLL;
424    if (cov->lock_screen) atom = ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_MOUSE;
425
426    ecore_x_pointer_xy_get(target_win, &x, &y);
427    _coordinate_calibrate(target_win, &x, &y);
428
429    ecore_x_client_message32_send(target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
430                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
431                                  target_win,
432                                  atom,
433                                  type, x, y);
434 }
435
436 static void
437 _message_mouse_send(Cover *cov, int type)
438 {
439    int x, y;
440
441    ecore_x_pointer_xy_get(cov->down_win, &x, &y);
442    _coordinate_calibrate(cov->down_win, &x, &y);
443
444    ecore_x_client_message32_send(cov->down_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
445                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
446                                  cov->down_win,
447                                  ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_MOUSE,
448                                  type, x, y);
449 }
450
451 static void
452 _message_read_send(Cover *cov)
453 {
454    int x, y;
455
456    /* find target window to send message */
457    _mouse_in_win_get(cov, cov->x, cov->y);
458
459    ecore_x_pointer_xy_get(target_win, &x, &y);
460    _coordinate_calibrate(target_win, &x, &y);
461
462    ecore_x_client_message32_send(target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
463                                  ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
464                                  target_win,
465                                  ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ,
466                                  x, y, 0);
467 }
468
469 static Eina_Bool
470 _mouse_longpress(void *data)
471 {
472    Cover *cov = data;
473    int distance = 40;
474    int dx, dy;
475
476    cov->timer = NULL;
477    dx = cov->x - cov->dx;
478    dy = cov->y - cov->dy;
479    if (((dx * dx) + (dy * dy)) < (distance * distance))
480      {
481         cov->longpressed = EINA_TRUE;
482         INFO(cov, "longpress");
483
484         if (!cov->mouse_double_down) _message_read_send(cov);
485         else
486           {
487              /* send message to start longpress,
488                 keep previous target window to send mouse-up event */
489              cov->down_win = target_win;
490
491              _message_mouse_send(cov, MOUSE_BUTTON_DOWN);
492           }
493      }
494    return EINA_FALSE;
495 }
496
497 static Eina_Bool
498 _mouse_double_down(void *data)
499 {
500    Cover *cov = data;
501    ecore_timer_del(cov->double_down_timer);
502    cov->double_down_timer = NULL;
503    return EINA_FALSE;
504 }
505
506 static void
507 _mouse_double_down_timeout(Cover *cov)
508 {
509    double long_time = 0.5;
510    double short_time = 0.3;
511    int distance = 40;
512    int dx, dy;
513
514    dx = cov->x - cov->dx;
515    dy = cov->y - cov->dy;
516
517    if ((cov->double_down_timer) &&
518        (((dx * dx) + (dy * dy)) < (distance * distance)))
519      {
520         /* start double tap and move from here */
521         cov->mouse_double_down = EINA_TRUE;
522
523         if (cov->timer)
524           {
525              ecore_timer_del(cov->timer);
526              cov->timer = NULL;
527           }
528         /* check longpress after double down */
529         cov->timer = ecore_timer_add(long_time, _mouse_longpress, cov);
530      }
531
532    if (cov->double_down_timer)
533      {
534         ecore_timer_del(cov->double_down_timer);
535         cov->double_down_timer = NULL;
536         return;
537      }
538
539    cov->double_down_timer = ecore_timer_add(short_time, _mouse_double_down, cov);
540 }
541
542 static Eina_Bool
543 _mouse_tap(void *data)
544 {
545    Cover *cov = data;
546    cov->tap_timer = NULL;
547
548    _message_read_send(cov);
549
550    return EINA_FALSE;
551 }
552
553 static void
554 _mouse_down(Cover *cov, Ecore_Event_Mouse_Button *ev)
555 {
556    double longtime = 0.5;
557
558    cov->dx = ev->x;
559    cov->dy = ev->y;
560    cov->mx = ev->x;
561    cov->my = ev->y;
562    cov->x = ev->x;
563    cov->y = ev->y;
564    cov->dt = ev->timestamp;
565    cov->longpressed = EINA_FALSE;
566    cov->timer = ecore_timer_add(longtime, _mouse_longpress, cov);
567    cov->down_win = 0;
568
569    if (cov->tap_timer)
570      {
571         ecore_timer_del(cov->tap_timer);
572         cov->tap_timer = NULL;
573      }
574
575    /* check mouse double down - not two fingers, refer to double click */
576    _mouse_double_down_timeout(cov);
577 }
578
579 static void
580 _circle_draw_check(Cover *cov)
581 {
582    Ecore_Event_Mouse_Move *ev, *t_ev, *b_ev, *l_ev, *r_ev;
583    Evas_Coord_Point m_tb, m_lr;
584    int count = 0;
585    int offset, i;
586    int min_x, min_y, max_x, max_y;
587    int left = 0, right = 0, top = 0, bottom = 0;
588    int distance;
589
590    count = eina_inarray_count(cov->two_finger_move);
591    if (count < 10 || count > 60) goto inarray_free;
592
593    offset = count / 8;
594
595    i = 0;
596    EINA_INARRAY_FOREACH(cov->two_finger_move, ev)
597      {
598         if (i == 0)
599           {
600              min_x = ev->x;
601              max_x = ev->x;
602              min_y = ev->y;
603              max_y = ev->y;
604           }
605
606         if (ev->x < min_x)
607           {
608              min_x = ev->x;
609              left = i;
610           }
611
612         if (ev->y < min_y)
613           {
614              min_y = ev->y;
615              bottom = i;
616           }
617
618         if (ev->x > max_x)
619           {
620              max_x = ev->x;
621              right = i;
622           }
623
624         if (ev->y > max_y)
625           {
626              max_y = ev->y;
627              top = i;
628           }
629
630         i++;
631      }
632
633    t_ev = eina_inarray_nth(cov->two_finger_move, top);
634    b_ev = eina_inarray_nth(cov->two_finger_move, bottom);
635    m_tb.x = (t_ev->x + b_ev->x) / 2;
636    m_tb.y = (t_ev->y + b_ev->y) / 2;
637
638
639    l_ev = eina_inarray_nth(cov->two_finger_move, left);
640    r_ev = eina_inarray_nth(cov->two_finger_move, right);
641    m_lr.x = (l_ev->x + r_ev->x) / 2;
642    m_lr.y = (l_ev->y + r_ev->y) / 2;
643
644    distance = (int) sqrt(((m_tb.x - m_lr.x) * (m_tb.x - m_lr.x)) + ((m_tb.y - m_lr.y) * (m_tb.y - m_lr.y)));
645
646    i = 0;
647    if (top > left) i++;
648    if (left > bottom) i++;
649    if (bottom > right) i++;
650    if (right > top) i++;
651
652    if ((i >= 3) && (distance < 60))
653      {
654         INFO(cov, "two finger circle draw");
655         _message_back_send(cov);
656      }
657
658 inarray_free:
659    eina_inarray_free(cov->two_finger_move);
660 }
661
662 static void
663 _mouse_up(Cover *cov, Ecore_Event_Mouse_Button *ev)
664 {
665    double timeout = 0.15;
666    double double_tap_timeout = 0.25;
667    int distance = 40;
668    int dx, dy;
669    int angle = 0;
670
671    if (cov->three_finger_down)
672      {
673         cov->three_finger_down = EINA_FALSE;
674
675         dx = ev->x - cov->dx;
676         dy = ev->y - cov->dy;
677
678         if (((dx * dx) + (dy * dy)) > (4 * distance * distance)
679             && ((ev->timestamp - cov->dt) < (timeout * 3000)))
680           {
681              /* get root window rotation */
682              angle = _win_angle_get(target_win);
683
684              if (abs(dx) > abs(dy)) /* left or right */
685                {
686                   if (dx > 0) /* right */
687                     {
688                        INFO(cov, "three finger swipe right");
689                        switch (angle)
690                          {
691                           case 270:
692                             _app_tray_open(cov);
693                           break;
694
695                           case 90:
696                             _quickpanel_open();
697                           break;
698                          }
699
700                     }
701                   else /* left */
702                     {
703                        INFO(cov, "three finger swipe left");
704                        switch (angle)
705                          {
706                           case 270:
707                             _quickpanel_open();
708                           break;
709
710                           case 90:
711                             _app_tray_open(cov);
712                           break;
713
714                          }
715                     }
716                }
717              else /* up or down */
718                {
719                   if (dy > 0) /* down */
720                     {
721                        INFO(cov, "three finger swipe down");
722                        switch (angle)
723                          {
724                           case 180:
725                           default:
726                             _quickpanel_open();
727                           break;
728                          }
729                     }
730                   else /* up */
731                     {
732                        INFO(cov, "three finger swipe up");
733                        switch (angle)
734                          {
735                           case 180:
736                           default:
737                             _app_tray_open(cov);
738                           break;
739                          }
740                     }
741                }
742           }
743         goto end;
744      }
745
746    /* for two finger panning */
747    if (cov->two_finger_down)
748      {
749         cov->two_finger_down = EINA_FALSE;
750
751         _message_scroll_send(cov, MOUSE_BUTTON_UP);
752         cov->lock_screen = EINA_FALSE;
753
754         /* to check 2 finger mouse move */
755         if (cov->two_finger_move) _circle_draw_check(cov);
756
757         dx = ev->x - cov->dx;
758         dy = ev->y - cov->dy;
759
760         if (((dx * dx) + (dy * dy)) < (distance * distance))
761           {
762              if (ev->double_click)
763                {
764                   INFO(cov, "two finger double click");
765                   _message_control_panel_open_send(cov);
766                }
767           }
768         goto end;
769      }
770
771    if (cov->mouse_double_down)
772      {
773         /* reset double down and moving: action up/down */
774         cov->mouse_double_down = EINA_FALSE;
775
776         if (cov->longpressed)
777           {
778              /* mouse up after longpress */
779              _message_mouse_send(cov, MOUSE_BUTTON_UP);
780           }
781      }
782
783    /* delete timer which is used for checking longpress */
784    if (cov->timer)
785      {
786         ecore_timer_del(cov->timer);
787         cov->timer = NULL;
788      }
789
790    if (cov->longpressed)
791      {
792         cov->longpressed = EINA_FALSE;
793         return;
794      }
795
796    dx = ev->x - cov->dx;
797    dy = ev->y - cov->dy;
798    if (((dx * dx) + (dy * dy)) < (distance * distance))
799      {
800         if (ev->double_click)
801           {
802              INFO(cov, "double_click");
803              ecore_x_e_illume_access_action_activate_send(target_win);
804           }
805         else
806           {
807              cov->tap_timer = ecore_timer_add(double_tap_timeout,
808                                          _mouse_tap, cov);
809           }
810      }
811    else if (((dx * dx) + (dy * dy)) > (4 * distance * distance)
812             && ((ev->timestamp - cov->dt) < (timeout * 2000)))
813      {
814         /* get root window rotation */
815         angle = _win_angle_get(target_win);
816
817         if (abs(dx) > abs(dy)) /* left or right */
818           {
819              if (dx > 0) /* right */
820                {
821                   INFO(cov, "single flick right");
822                   switch (angle)
823                     {
824                      case 270:
825                        ecore_x_e_illume_access_action_up_send(target_win);
826                      break;
827
828                      case 90:
829                        ecore_x_e_illume_access_action_down_send(target_win);
830                      break;
831
832                      case 180:
833                      default:
834                        ecore_x_e_illume_access_action_read_next_send
835                                                         (target_win);
836                      break;
837                     }
838
839                }
840              else /* left */
841                {
842                   INFO(cov, "single flick left");
843                   switch (angle)
844                     {
845                      case 270:
846                        ecore_x_e_illume_access_action_down_send(target_win);
847                      break;
848
849                      case 90:
850                        ecore_x_e_illume_access_action_up_send(target_win);
851                      break;
852
853                      case 180:
854                      default:
855                        ecore_x_e_illume_access_action_read_prev_send
856                                                         (target_win);
857                      break;
858                     }
859                }
860           }
861         else /* up or down */
862           {
863              if (dy > 0) /* down */
864                {
865                   INFO(cov, "single flick down");
866                   switch (angle)
867                     {
868                      case 90:
869                        ecore_x_e_illume_access_action_read_prev_send
870                                                         (target_win);
871                      break;
872
873                      case 270:
874                        ecore_x_e_illume_access_action_read_next_send
875                                                         (target_win);
876                      break;
877
878                      case 180:
879                      default:
880                        ecore_x_e_illume_access_action_down_send(target_win);
881                      break;
882                     }
883                }
884              else /* up */
885                {
886                   INFO(cov, "single flick up");
887                   switch (angle)
888                     {
889                      case 90:
890                        ecore_x_e_illume_access_action_read_next_send
891                                                         (target_win);
892                      break;
893
894                      case 270:
895                        ecore_x_e_illume_access_action_read_prev_send
896                                                         (target_win);
897                      break;
898
899                      case 180:
900                      default:
901                        ecore_x_e_illume_access_action_up_send(target_win);
902                      break;
903                     }
904                }
905           }
906      }
907
908 end:
909    cov->longpressed = EINA_FALSE;
910 }
911
912 static void
913 _mouse_move(Cover *cov __UNUSED__, Ecore_Event_Mouse_Move *ev __UNUSED__)
914 {
915    //FIXME: why here, after long press you cannot go below..
916    //if (!cov->down) return;
917
918    //FIXME: one finger cannot come here
919    //_record_mouse_history(cov, ev);
920    if (cov->two_finger_move) eina_inarray_push(cov->two_finger_move, ev);
921
922    _message_scroll_send(cov, MOUSE_MOVE);
923 }
924
925 static void
926 _mouse_wheel(Cover *cov __UNUSED__, Ecore_Event_Mouse_Wheel *ev __UNUSED__)
927 {
928    if (ev->z == -1) /* up */
929      {
930 #if ECORE_VERSION_MAJOR >= 1
931 # if ECORE_VERSION_MINOR >= 8
932         ecore_x_e_illume_access_action_up_send(target_win);
933 # endif
934 #endif
935      }
936    else if (ev->z == 1) /* down */
937      {
938 #if ECORE_VERSION_MAJOR >= 1
939 # if ECORE_VERSION_MINOR >= 8
940         ecore_x_e_illume_access_action_down_send(target_win);
941 # endif
942 #endif
943      }
944 }
945
946 static Eina_Bool
947 _cb_mouse_down(void    *data __UNUSED__,
948                int      type __UNUSED__,
949                void    *event)
950 {
951    Ecore_Event_Mouse_Button *ev = event;
952    Eina_List *l;
953    Cover *cov;
954
955    EINA_LIST_FOREACH(covers, l, cov)
956      {
957         /* sometimes the mouse down event has improper multi.device value */
958         cov->n_taps++;
959
960         if (ev->window == cov->win)
961           {
962              //XXX change specific number
963              if (ev->multi.device == 0)
964                {
965                   _target_window_find();
966                   _mouse_down(cov, ev);
967                }
968
969              else if (cov->n_taps == 2 &&
970                       !(cov->two_finger_down) &&
971                       !(cov->longpressed))
972                {
973                   /* prevent longpress client message by two finger */
974                   if (cov->timer)
975                     {
976                        ecore_timer_del(cov->timer);
977                        cov->timer = NULL;
978                     }
979
980                   cov->two_finger_down = EINA_TRUE;
981
982                   _lock_screen_check(cov);
983                   _message_scroll_send(cov, MOUSE_BUTTON_DOWN);
984
985                   /* to check 2 finger mouse move */
986                   cov->two_finger_move = eina_inarray_new(sizeof(Ecore_Event_Mouse_Move), 0);
987                }
988
989              else if (cov->n_taps == 3 &&
990                       !(cov->three_finger_down) &&
991                       !(cov->longpressed))
992                {
993                   cov->three_finger_down = EINA_TRUE;
994
995                   if (cov->two_finger_down)
996                     {
997                        cov->two_finger_down = EINA_FALSE;
998
999                        _message_scroll_send(cov, MOUSE_BUTTON_UP);
1000                        cov->lock_screen = EINA_FALSE;
1001
1002                        eina_inarray_free(cov->two_finger_move);
1003                     }
1004                }
1005              return ECORE_CALLBACK_PASS_ON;
1006           }
1007      }
1008    return ECORE_CALLBACK_PASS_ON;
1009 }
1010
1011 static Eina_Bool
1012 _cb_mouse_up(void    *data __UNUSED__,
1013              int      type __UNUSED__,
1014              void    *event)
1015 {
1016    Ecore_Event_Mouse_Button *ev = event;
1017    Eina_List *l;
1018    Cover *cov;
1019
1020    EINA_LIST_FOREACH(covers, l, cov)
1021      {
1022         cov->n_taps--;
1023
1024         if (ev->window == cov->win)
1025           {
1026              /* the first finger: 1, from the second finger: 0 */
1027              if (ev->buttons == 1) _mouse_up(cov, ev);
1028
1029              return ECORE_CALLBACK_PASS_ON;
1030           }
1031      }
1032    return ECORE_CALLBACK_PASS_ON;
1033 }
1034
1035 static Eina_Bool
1036 _cb_mouse_move(void    *data __UNUSED__,
1037                int      type __UNUSED__,
1038                void    *event)
1039 {
1040    Ecore_Event_Mouse_Move *ev = event;
1041    Eina_List *l;
1042    Cover *cov;
1043
1044    EINA_LIST_FOREACH(covers, l, cov)
1045      {
1046         cov->x = ev->x;
1047         cov->y = ev->y;
1048
1049         if (ev->window == cov->win)
1050           {
1051              //if (ev->multi.device == multi_device[0] || ev->multi.device == multi_device[1])
1052              if (cov->two_finger_down && cov->n_taps == 2)
1053                _mouse_move(cov, ev);
1054              else if (cov->longpressed && /* client message for moving is available only after long press is detected */
1055                       !(cov->mouse_double_down) && /* mouse move after double down should not send read message */
1056                       !(cov->two_finger_down) && ev->multi.device == 0)
1057                {
1058                   INFO(cov, "read");
1059                   _message_read_send(cov);
1060                }
1061              else if (cov->mouse_double_down && /* client message for moving is available only after long press is detected */
1062                       !(cov->two_finger_down) && ev->multi.device == 0)
1063                {
1064                   if (cov->longpressed)
1065                     {
1066                        /* send message to notify move after longpress */
1067                         _message_mouse_send(cov, MOUSE_MOVE);
1068                     }
1069                }
1070
1071              return ECORE_CALLBACK_PASS_ON;
1072           }
1073      }
1074    return ECORE_CALLBACK_PASS_ON;
1075 }
1076
1077 static Eina_Bool
1078 _cb_mouse_wheel(void    *data __UNUSED__,
1079                 int      type __UNUSED__,
1080                 void    *event)
1081 {
1082    Ecore_Event_Mouse_Wheel *ev = event;
1083    Eina_List *l;
1084    Cover *cov;
1085
1086    EINA_LIST_FOREACH(covers, l, cov)
1087      {
1088         if (ev->window == cov->win)
1089           {
1090              _mouse_wheel(cov, ev);
1091              return ECORE_CALLBACK_PASS_ON;
1092           }
1093      }
1094    return ECORE_CALLBACK_PASS_ON;
1095 }
1096
1097 static Cover *
1098 _cover_new(E_Zone *zone)
1099 {
1100    Cover *cov;
1101
1102    cov = E_NEW(Cover, 1);
1103    if (!cov) return NULL;
1104    cov->zone = zone;
1105
1106 #if DEBUG_INFO
1107    Ecore_Evas *ee;
1108    ee = ecore_evas_new(NULL,
1109                        zone->container->x + zone->x,
1110                        zone->container->y + zone->y,
1111                        zone->w, zone->h,
1112                        NULL);
1113    ecore_evas_alpha_set(ee, EINA_TRUE);
1114    cov->win = (Ecore_X_Window)ecore_evas_window_get(ee);
1115
1116    /* create infomation */
1117    Evas *e;
1118    e = ecore_evas_get(ee);
1119    cov->info = evas_object_rectangle_add(e);
1120    evas_object_color_set(cov->info, 255, 255, 255, 100);
1121    evas_object_move(cov->info, zone->container->x + zone->x, zone->container->y + zone->y);
1122    evas_object_resize(cov->info, zone->w, 30);
1123    evas_object_show(cov->info);
1124
1125    cov->text = evas_object_text_add(e);
1126    evas_object_text_style_set(cov->text, EVAS_TEXT_STYLE_PLAIN);
1127    evas_object_text_font_set(cov->text, "DejaVu", 14);
1128    evas_object_text_text_set(cov->text, "screen-reader module");
1129
1130    evas_object_color_set(cov->text, 0, 0, 0, 255);
1131    evas_object_resize(cov->text, (zone->w / 8), 20);
1132    evas_object_move(cov->text, zone->container->x + zone->x + 5, zone->container->y + zone->y + 5);
1133    evas_object_show(cov->text);
1134
1135 #else
1136    cov->win = ecore_x_window_input_new(zone->container->manager->root,
1137                                        zone->container->x + zone->x,
1138                                        zone->container->y + zone->y,
1139                                        zone->w, zone->h);
1140 #endif
1141
1142    ecore_x_input_multi_select(cov->win);
1143
1144    ecore_x_icccm_title_set(cov->win, "screen-reader-input");
1145    ecore_x_netwm_name_set(cov->win, "screen-reader-input");
1146
1147    ecore_x_window_ignore_set(cov->win, 1);
1148    ecore_x_window_configure(cov->win,
1149                             ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING |
1150                             ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE,
1151                             0, 0, 0, 0, 0,
1152                             zone->container->layers[8].win,
1153                             ECORE_X_WINDOW_STACK_ABOVE);
1154    ecore_x_window_show(cov->win);
1155    ecore_x_window_raise(cov->win);
1156
1157    return cov;
1158 }
1159
1160 static void
1161 _covers_init(void)
1162 {
1163    Eina_List *l, *l2, *l3;
1164    E_Manager *man;
1165
1166    EINA_LIST_FOREACH(e_manager_list(), l, man)
1167      {
1168         E_Container *con;
1169         EINA_LIST_FOREACH(man->containers, l2, con)
1170           {
1171              E_Zone *zone;
1172              EINA_LIST_FOREACH(con->zones, l3, zone)
1173                {
1174                   Cover *cov = _cover_new(zone);
1175                   if (cov)
1176                     {
1177                        covers = eina_list_append(covers, cov);
1178                        cov->n_taps = 0;
1179
1180                        cov->atom_control_panel_open = ecore_x_atom_get("_E_MOD_SCREEN_READER_ACTION_CONTROL_PANEL_OPEN_");
1181                        cov->atom_app_tray_open = ecore_x_atom_get("_E_MOD_SCREEN_READER_ACTION_APP_TRAY_OPEN_");
1182                     }
1183                }
1184           }
1185      }
1186 }
1187
1188 static void
1189 _covers_shutdown(void)
1190 {
1191    Cover *cov;
1192
1193    EINA_LIST_FREE(covers, cov)
1194      {
1195         ecore_x_window_ignore_set(cov->win, 0);
1196         ecore_x_window_free(cov->win);
1197         evas_object_del(cov->info);
1198         evas_object_del(cov->text);
1199
1200         if (cov->timer)
1201           {
1202              ecore_timer_del(cov->timer);
1203              cov->timer = NULL;
1204           }
1205
1206         if (cov->double_down_timer)
1207           {
1208              ecore_timer_del(cov->double_down_timer);
1209              cov->double_down_timer = NULL;
1210           }
1211
1212         if (cov->tap_timer)
1213           {
1214              ecore_timer_del(cov->tap_timer);
1215              cov->tap_timer = NULL;
1216           }
1217
1218         free(cov);
1219      }
1220 }
1221
1222 static Eina_Bool
1223 _cb_zone_add(void    *data __UNUSED__,
1224              int      type __UNUSED__,
1225              void    *event __UNUSED__)
1226 {
1227    _covers_shutdown();
1228    _covers_init();
1229    return ECORE_CALLBACK_PASS_ON;
1230 }
1231
1232 static Eina_Bool
1233 _cb_zone_del(void    *data __UNUSED__,
1234              int      type __UNUSED__,
1235              void    *event __UNUSED__)
1236 {
1237    _covers_shutdown();
1238    _covers_init();
1239    return ECORE_CALLBACK_PASS_ON;
1240 }
1241
1242 static Eina_Bool
1243 _cb_zone_move_resize(void    *data __UNUSED__,
1244                      int      type __UNUSED__,
1245                      void    *event __UNUSED__)
1246 {
1247    _covers_shutdown();
1248    _covers_init();
1249    return ECORE_CALLBACK_PASS_ON;
1250 }
1251
1252 static void
1253 _events_init(void)
1254 {
1255    handlers = eina_list_append
1256      (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN,
1257                                         _cb_mouse_down, NULL));
1258    handlers = eina_list_append
1259      (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP,
1260                                         _cb_mouse_up, NULL));
1261    handlers = eina_list_append
1262      (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE,
1263                                         _cb_mouse_move, NULL));
1264    handlers = eina_list_append
1265      (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_WHEEL,
1266                                         _cb_mouse_wheel, NULL));
1267    handlers = eina_list_append
1268      (handlers, ecore_event_handler_add(E_EVENT_ZONE_ADD,
1269                                         _cb_zone_add, NULL));
1270    handlers = eina_list_append
1271      (handlers, ecore_event_handler_add(E_EVENT_ZONE_DEL,
1272                                         _cb_zone_del, NULL));
1273    handlers = eina_list_append
1274      (handlers, ecore_event_handler_add(E_EVENT_ZONE_MOVE_RESIZE,
1275                                         _cb_zone_move_resize, NULL));
1276 }
1277
1278 static void
1279 _events_shutdown(void)
1280 {
1281    E_FREE_LIST(handlers, ecore_event_handler_del);
1282 }
1283
1284 static Eina_Bool
1285 _cb_property_change(void *data __UNUSED__,
1286                    int   type __UNUSED__,
1287                    void *ev)
1288 {
1289    E_Border *bd;
1290    Ecore_X_Event_Window_Property *event = ev;
1291
1292    if (!g_enable) return ECORE_CALLBACK_PASS_ON;
1293
1294    if (event->atom == ECORE_X_ATOM_NET_ACTIVE_WINDOW)
1295      {
1296         bd = e_border_focused_get();
1297         if (bd)
1298           {
1299              /* if there was an unfocused window, the target window is changed
1300                 in the _target_window_find();. so reset unfocused window here */
1301              if (unfocused_win) unfocused_win = 0;
1302
1303              target_win = bd->client.win;
1304              _screen_reader_support_check();
1305           }
1306      }
1307
1308    if (event->atom == ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL &&
1309        event->win == target_win)
1310      {
1311         _screen_reader_support_check();
1312      }
1313
1314    return ECORE_CALLBACK_PASS_ON;
1315 }
1316
1317 static void
1318 _move_module_enable_set(int enable)
1319 {
1320    E_Manager *man = e_manager_current_get();
1321
1322    if (!man) ERR("cannot get current manager");
1323
1324    if (enable)
1325      {
1326         INF("send enaable message");
1327         e_msg_send("screen-reader", "enable", 0, E_OBJECT(man), NULL, NULL, NULL);
1328      }
1329    else
1330      {
1331         INF("send disaable message");
1332         e_msg_send("screen-reader", "disable", 0, E_OBJECT(man), NULL, NULL, NULL);
1333      }
1334 }
1335
1336 static void
1337 _vconf_cb_accessibility_tts_change(keynode_t *node,
1338                                    void      *data)
1339 {
1340    int enable = 0;
1341
1342    if (!node) return;
1343
1344    enable = vconf_keynode_get_bool(node);
1345    if (g_enable == enable) return;
1346
1347    g_enable = enable;
1348    if (enable)
1349      {
1350         INF("[screen reader module] module enable");
1351         _covers_shutdown();
1352         _covers_init();
1353         _events_shutdown();
1354         _events_init();
1355
1356         /* send a message to the move module */
1357         _move_module_enable_set(enable);
1358      }
1359    else
1360      {
1361         INF("[screen reader module] module disable");
1362         _covers_shutdown();
1363         _events_shutdown();
1364
1365         /* send a message to the move module */
1366         _move_module_enable_set(enable);
1367      }
1368
1369    elm_config_access_set(enable);
1370    elm_config_all_flush();
1371    elm_config_save();
1372 }
1373 /***************************************************************************/
1374 /* module setup */
1375 EAPI E_Module_Api e_modapi =
1376 {
1377    E_MODULE_API_VERSION, "Screen Reader"
1378 };
1379
1380 EAPI void *
1381 e_modapi_init(E_Module *m)
1382 {
1383    vconf_notify_key_changed(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS,
1384                             _vconf_cb_accessibility_tts_change,
1385                             NULL);
1386
1387    ecore_x_event_mask_set(ecore_x_window_root_first_get(),
1388                           ECORE_X_EVENT_MASK_WINDOW_CONFIGURE);
1389    ecore_x_event_mask_set(ecore_x_window_root_first_get(),
1390                           ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
1391    property_handler = ecore_event_handler_add
1392                        (ECORE_X_EVENT_WINDOW_PROPERTY, _cb_property_change, NULL);
1393
1394    if (vconf_get_bool(VCONFKEY_SETAPPL_ACCESSIBILITY_TTS, &g_enable) != 0)
1395      INF("vconf get failed\n");
1396
1397    if (g_enable)
1398      {
1399         _covers_shutdown();
1400         _covers_init();
1401         _events_shutdown();
1402         _events_init();
1403
1404         /* send a message to the move module */
1405         _move_module_enable_set(g_enable);
1406      }
1407    else
1408      {
1409         _covers_shutdown();
1410         _events_shutdown();
1411
1412         /* send a message to the move module */
1413         _move_module_enable_set(g_enable);
1414      }
1415
1416    elm_config_access_set(g_enable);
1417    elm_config_all_flush();
1418    elm_config_save();
1419
1420    return m;
1421 }
1422
1423 EAPI int
1424 e_modapi_shutdown(E_Module *m __UNUSED__)
1425 {
1426    INF("[screen-reader module] module shutdown");
1427    if (property_handler) ecore_event_handler_del(property_handler);
1428
1429    _covers_shutdown();
1430    _events_shutdown();
1431
1432    return 1;
1433 }
1434
1435 EAPI int
1436 e_modapi_save(E_Module *m __UNUSED__)
1437 {
1438    return 1;
1439 }