Ecore_input: Add "ECORE_EVENT_MOUSE_BUTTON_CANCEL" event
authorJi-Youn Park <jy0703.park@samsung.com>
Tue, 16 Jun 2015 06:33:43 +0000 (15:33 +0900)
committerJi-Youn Park <jy0703.park@samsung.com>
Tue, 16 Jun 2015 06:33:43 +0000 (15:33 +0900)
If ecore get the mouse cancel event(ex: display off, or window stack change) from below, we need to deal with.
Currently, evas also generates up event using evas_event_feed_mouse_cancel function when window hide is called.
if cancel event occurs, ecore also call evas_event_mouse_feed_cancel like window hide situation.
cancel event means all button or touch event should be canceled, but in the future, if we need to deal each cancel event according to touch point,
we need to add api like evas_event_feed_multi_cancel

@feature

src/lib/ecore_input/Ecore_Input.h
src/lib/ecore_input/ecore_input.c
src/lib/ecore_input_evas/Ecore_Input_Evas.h
src/lib/ecore_input_evas/ecore_input_evas.c
src/lib/ecore_x/xlib/ecore_x_events.c
src/lib/ecore_x/xlib/ecore_x_xi2.c

index f944006..1a72e98 100644 (file)
@@ -54,6 +54,7 @@ extern "C" {
    EAPI extern int ECORE_EVENT_MOUSE_IN;
    EAPI extern int ECORE_EVENT_MOUSE_OUT;
    EAPI extern int ECORE_EVENT_AXIS_UPDATE; /**< @since 1.13 */
+   EAPI extern int ECORE_EVENT_MOUSE_BUTTON_CANCEL; /**< @since 1.19 */
 
 #define ECORE_EVENT_MODIFIER_SHIFT      0x0001
 #define ECORE_EVENT_MODIFIER_CTRL       0x0002
@@ -106,7 +107,8 @@ extern "C" {
    typedef enum _Ecore_Event_Press
      {
         ECORE_DOWN,
-        ECORE_UP
+        ECORE_UP,
+        ECORE_CANCEL
      } Ecore_Event_Press;
 
    /**
index ac8b60d..6b52eff 100644 (file)
@@ -23,6 +23,7 @@ EAPI int ECORE_EVENT_MOUSE_WHEEL = 0;
 EAPI int ECORE_EVENT_MOUSE_IN = 0;
 EAPI int ECORE_EVENT_MOUSE_OUT = 0;
 EAPI int ECORE_EVENT_AXIS_UPDATE = 0;
+EAPI int ECORE_EVENT_MOUSE_BUTTON_CANCEL = 0;
 
 static int _ecore_event_init_count = 0;
 
@@ -54,6 +55,7 @@ ecore_event_init(void)
    ECORE_EVENT_MOUSE_IN = ecore_event_type_new();
    ECORE_EVENT_MOUSE_OUT = ecore_event_type_new();
    ECORE_EVENT_AXIS_UPDATE = ecore_event_type_new();
+   ECORE_EVENT_MOUSE_BUTTON_CANCEL = ecore_event_type_new();
 
    return _ecore_event_init_count;
 }
@@ -73,6 +75,7 @@ ecore_event_shutdown(void)
    ECORE_EVENT_MOUSE_IN = 0;
    ECORE_EVENT_MOUSE_OUT = 0;
    ECORE_EVENT_AXIS_UPDATE = 0;
+   ECORE_EVENT_MOUSE_BUTTON_CANCEL = 0;
    eina_log_domain_unregister(_ecore_input_log_dom);
    _ecore_input_log_dom = -1;
    ecore_shutdown();
index 2296048..d17dc7d 100644 (file)
@@ -50,6 +50,7 @@ EAPI Eina_Bool ecore_event_evas_mouse_move(void *data, int type, void *event);
 EAPI Eina_Bool ecore_event_evas_mouse_in(void *data, int type, void *event);
 EAPI Eina_Bool ecore_event_evas_mouse_out(void *data, int type, void *event);
 EAPI Eina_Bool ecore_event_evas_axis_update(void *data, int type, void *event); /**< @since 1.13 */
+EAPI Eina_Bool ecore_event_evas_mouse_button_cancel(void *data, int type, void *event); /**< @since 1.19 */
 
 EAPI void      ecore_event_window_register(Ecore_Window id, void *window, Evas *evas, Ecore_Event_Mouse_Move_Cb move_mouse, Ecore_Event_Multi_Move_Cb move_multi, Ecore_Event_Multi_Down_Cb down_multi, Ecore_Event_Multi_Up_Cb up_multi);
 EAPI void      ecore_event_window_unregister(Ecore_Window id);
index 5de76a8..41ee786 100644 (file)
@@ -29,9 +29,16 @@ typedef enum _Ecore_Input_State {
   ECORE_INPUT_NONE = 0,
   ECORE_INPUT_DOWN,
   ECORE_INPUT_MOVE,
-  ECORE_INPUT_UP
+  ECORE_INPUT_UP,
+  ECORE_INPUT_CANCEL
 } Ecore_Input_State;
 
+typedef enum _Ecore_Input_Action {
+  ECORE_INPUT_CONTINUE = 0,
+  ECORE_INPUT_IGNORE,
+  ECORE_INPUT_FAKE_UP
+} Ecore_Input_Action;
+
 typedef struct _Ecore_Input_Last Ecore_Event_Last;
 struct _Ecore_Input_Last
 {
@@ -46,7 +53,7 @@ struct _Ecore_Input_Last
 };
 
 static int _ecore_event_evas_init_count = 0;
-static Ecore_Event_Handler *ecore_event_evas_handlers[9];
+static Ecore_Event_Handler *ecore_event_evas_handlers[10];
 static Eina_Hash *_window_hash = NULL;
 
 static Eina_List *_last_events = NULL;
@@ -57,6 +64,59 @@ static Eina_Bool _ecore_event_evas_mouse_button(Ecore_Event_Mouse_Button *e,
                                                 Ecore_Event_Press press,
                                                 Eina_Bool faked);
 
+static Ecore_Input_Action
+_ecore_event_last_check(Ecore_Event_Last *eel, Ecore_Event_Press press)
+{
+   switch (eel->state)
+     {
+      case ECORE_INPUT_NONE:
+         /* 1. ECORE_INPUT_NONE => ECORE_UP : impossible
+          * 2. ECORE_INPUT_NONE => ECORE_CANCEL : impossible
+          * 3. ECORE_INPUT_NONE => ECORE_DOWN : ok
+          */
+         return ECORE_INPUT_CONTINUE;
+
+      case ECORE_INPUT_DOWN:
+         /* 1. ECORE_INPUT_DOWN => ECORE_UP : ok
+          * 2. ECORE_INPUT_DOWN => ECORE_CANCEL : ok
+          */
+         if ((press == ECORE_UP) || (press == ECORE_CANCEL))
+           return ECORE_INPUT_CONTINUE;
+
+         /* 3. ECORE_INPUT_DOWN => ECORE_DOWN : emit a faked UP then */
+         INF("Down event occurs twice. device(%d), button(%d)", eel->device, eel->buttons);
+         return ECORE_INPUT_FAKE_UP;
+
+      case ECORE_INPUT_MOVE:
+         /* 1. ECORE_INPUT_MOVE => ECORE_UP : ok
+          *    2. ECORE_INPUT_MOVE => ECORE_CANCEL : ok
+                 */
+         if ((press == ECORE_UP) || (press == ECORE_CANCEL))
+           return ECORE_INPUT_CONTINUE;
+
+         /* 3. ECORE_INPUT_MOVE => ECORE_DOWN : ok
+          * FIXME: handle fake button up and push for more delay here */
+         //TODO: How to deal with down event after move event?
+         INF("Down event occurs after move event. device(%d), button(%d)", eel->device, eel->buttons);
+         return ECORE_INPUT_FAKE_UP;
+
+      case ECORE_INPUT_UP:
+      case ECORE_INPUT_CANCEL:
+          /* 1. ECORE_INPUT_UP     => ECORE_DOWN : ok */
+          /* 2. ECORE_INPUT_CANCEL => ECORE_DOWN : ok */
+         if (press == ECORE_DOWN)
+           return ECORE_INPUT_CONTINUE;
+
+          /* 3. ECORE_INPUT_UP     => ECORE_UP :  ignore */
+          /* 4. ECORE_INPUT_UP     => ECORE_CANCEL : ignore */
+          /* 5. ECORE_INPUT_CANCEL => ECORE_UP : ignore */
+          /* 6. ECORE_INPUT_CANCEL => ECORE_CANCEL : ignore */
+         INF("Up/cancel event occurs after up/cancel event. device(%d), button(%d)", eel->device, eel->buttons);
+         return ECORE_INPUT_IGNORE;
+     }
+  return ECORE_INPUT_IGNORE;
+}
+
 static Ecore_Event_Last *
 _ecore_event_evas_lookup(unsigned int device, unsigned int buttons, Eina_Bool create_new)
 {
@@ -91,6 +151,7 @@ _ecore_event_evas_push_fake(void *data)
      {
       case ECORE_INPUT_NONE:
       case ECORE_INPUT_UP:
+      case ECORE_INPUT_CANCEL:
          /* should not happen */
          break;
       case ECORE_INPUT_DOWN:
@@ -109,48 +170,45 @@ _ecore_event_evas_push_fake(void *data)
    return EINA_FALSE;
 }
 
-static void
+static Eina_Bool
 _ecore_event_evas_push_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press press)
 {
    Ecore_Event_Last *eel;
+   Ecore_Input_Action action;
 
-   if (!_last_events_enable) return;
-
+   //_ecore_event_evas_mouse_button already check press or cancel without history
    eel = _ecore_event_evas_lookup(e->multi.device, e->buttons, EINA_TRUE);
-   if (!eel) return;
+   if (!eel) return EINA_FALSE;
+   INF("dev(%d), button(%d), last_press(%d), press(%d)", e->multi.device, e->buttons, eel->state, press);
 
-   switch (eel->state)
+   action = _ecore_event_last_check(eel, press);
+   INF("action(%d)", action);
+   switch (action)
      {
-      case ECORE_INPUT_NONE:
-         goto fine;
-      case ECORE_INPUT_DOWN:
-         if (press == ECORE_UP)
-           goto fine;
-
-         /* press == ECORE_DOWN => emit a faked UP then */
+      case ECORE_INPUT_FAKE_UP:
          _ecore_event_evas_mouse_button(e, ECORE_UP, EINA_TRUE);
+      case ECORE_INPUT_CONTINUE:
          break;
-      case ECORE_INPUT_MOVE:
-         if (press == ECORE_UP)
-           goto fine;
-
-         /* FIXME: handle fake button up and push for more delay here */
-
-         /* press == ECORE_DOWN */
-         _ecore_event_evas_mouse_button(e, ECORE_DOWN, EINA_TRUE);
-         break;
-      case ECORE_INPUT_UP:
-         if (press == ECORE_DOWN)
-           goto fine;
+      case ECORE_INPUT_IGNORE:
+      default:
+        return EINA_FALSE;
+     }
 
-         /* press == ECORE_UP */
-         _ecore_event_evas_mouse_button(e, ECORE_UP, EINA_TRUE);
-         break;
+   switch (press)
+     {
+      case ECORE_DOWN:
+        eel->state = ECORE_INPUT_DOWN;
+        break;
+      case ECORE_UP:
+        eel->state = ECORE_INPUT_UP;
+        break;
+      default:
+        break;
      }
 
- fine:
-   eel->state = (press == ECORE_DOWN) ? ECORE_INPUT_DOWN : ECORE_INPUT_UP;
-   if (_last_events_timeout)
+   //if up event not occurs from under layers of ecore
+   //up event is generated by ecore
+   if (_last_events_enable && _last_events_timeout)
      {
         if (eel->timer) ecore_timer_del(eel->timer);
         eel->timer = NULL;
@@ -158,7 +216,7 @@ _ecore_event_evas_push_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Pre
           {
              /* Save the Ecore_Event somehow */
              if (!eel->ev) eel->ev = malloc(sizeof (Ecore_Event_Mouse_Button));
-             if (!eel->ev) return;
+             if (!eel->ev) return EINA_FALSE;
              memcpy(eel->ev, e, sizeof (Ecore_Event_Mouse_Button));
              eel->timer = ecore_timer_add(_last_events_timeout, _ecore_event_evas_push_fake, eel);
           }
@@ -168,6 +226,7 @@ _ecore_event_evas_push_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Pre
              eel->ev = NULL;
           }
      }
+   return EINA_TRUE;
 }
 
 static void
@@ -183,7 +242,8 @@ _ecore_event_evas_push_mouse_move(Ecore_Event_Mouse_Move *e)
        {
         case ECORE_INPUT_NONE:
         case ECORE_INPUT_UP:
-           /* none or up and moving, sounds fine to me */
+        case ECORE_INPUT_CANCEL:
+           /* (none, up, or cancel) => move, sounds fine to me */
            break;
         case ECORE_INPUT_DOWN:
         case ECORE_INPUT_MOVE:
@@ -355,6 +415,30 @@ _ecore_event_evas_key(Ecore_Event_Key *e, Ecore_Event_Press press)
 }
 
 static Eina_Bool
+_ecore_event_evas_mouse_button_cancel(Ecore_Event_Mouse_Button *e)
+{
+   Ecore_Input_Window *lookup;
+   Ecore_Event_Last *eel;
+   Eina_List *l;
+
+   lookup = _ecore_event_window_match(e->event_window);
+   if (!lookup) return ECORE_CALLBACK_PASS_ON;
+
+   INF("ButtonEvent cancel, device(%d), button(%d)", e->multi.device, e->buttons);
+   evas_event_feed_mouse_cancel(lookup->evas, e->timestamp, NULL);
+
+   //the number of last event is small, simple check is ok.
+   EINA_LIST_FOREACH(_last_events, l, eel)
+     {
+        Ecore_Input_Action act = _ecore_event_last_check(eel, ECORE_CANCEL);
+        INF("ButtonEvent cancel, dev(%d), button(%d), last_press(%d), action(%d)", eel->device, eel->buttons, eel->state, act);
+        if (act == ECORE_INPUT_CONTINUE) eel->state = ECORE_INPUT_CANCEL;
+     }
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
 _ecore_event_evas_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press press, Eina_Bool faked)
 {
    Ecore_Event_Last *eel;
@@ -366,21 +450,35 @@ _ecore_event_evas_mouse_button(Ecore_Event_Mouse_Button *e, Ecore_Event_Press pr
    if (e->double_click) flags |= EVAS_BUTTON_DOUBLE_CLICK;
    if (e->triple_click) flags |= EVAS_BUTTON_TRIPLE_CLICK;
    INF("\tButtonEvent:ecore_event_evas press(%d), device(%d), button(%d), fake(%d)", press, e->multi.device, e->buttons, faked);
-   if (_last_events_enable)
+
+   //handle all mouse error from under layers of ecore
+   //error handle
+   // 1. ecore up without ecore down
+   // 2. ecore cancel without ecore down
+   if (press != ECORE_DOWN)
      {
-        //error handle: if ecore up without ecore down
-        if (press == ECORE_UP)
+        //ECORE_UP or ECORE_CANCEL
+        eel = _ecore_event_evas_lookup(e->multi.device, e->buttons, EINA_FALSE);
+        if (!eel)
+   
+        if ((!eel) || (eel->state == ECORE_INPUT_UP) || (eel->state == ECORE_INPUT_CANCEL))
           {
-             eel = _ecore_event_evas_lookup(e->multi.device, e->buttons, EINA_FALSE);
-             if ((!eel) || (eel->state == ECORE_INPUT_UP))
-               {
-                  INF("ButtonEvent: up event without down event.");
-                  return ECORE_CALLBACK_PASS_ON;
-               }
+            if (!eel)
+              WRN("ButtonEvent has no history.");
+            else
+              WRN("ButtonEvent has wrong history. Last state=%d", eel->state);
+            return ECORE_CALLBACK_PASS_ON;
           }
      }
 
-   if (!faked) _ecore_event_evas_push_mouse_button(e, press);
+   if (!faked)
+     {
+        Eina_Bool ret = EINA_FALSE;
+        ret = _ecore_event_evas_push_mouse_button(e, press);
+        /* This ButtonEvent is worng */
+        if (!ret) return ECORE_CALLBACK_PASS_ON;
+     }
+
    if (e->multi.device == 0)
      {
         ecore_event_evas_modifier_lock_update(lookup->evas, e->modifiers);
@@ -481,6 +579,12 @@ ecore_event_evas_mouse_button_up(void *data EINA_UNUSED, int type EINA_UNUSED, v
    return _ecore_event_evas_mouse_button((Ecore_Event_Mouse_Button *)event, ECORE_UP, EINA_FALSE);
 }
 
+EAPI Eina_Bool
+ecore_event_evas_mouse_button_cancel(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   return _ecore_event_evas_mouse_button_cancel((Ecore_Event_Mouse_Button *)event);
+}
+
 static Eina_Bool
 _ecore_event_evas_mouse_io(Ecore_Event_Mouse_IO *e, Ecore_Event_IO io)
 {
@@ -609,6 +713,9 @@ ecore_event_evas_init(void)
    ecore_event_evas_handlers[8] = ecore_event_handler_add(ECORE_EVENT_AXIS_UPDATE,
                                                           ecore_event_evas_axis_update,
                                                           NULL);
+   ecore_event_evas_handlers[9] = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_CANCEL,
+                                                          ecore_event_evas_mouse_button_cancel,
+                                                          NULL);
 
    _window_hash = eina_hash_pointer_new(free);
 
index 9864074..0a02b3e 100644 (file)
@@ -452,6 +452,18 @@ _ecore_mouse_button(int event,
 
    if (down_info)
      {
+        //If mouse cancel event occred, should reset down info related with double & triple click
+        if (event == ECORE_EVENT_MOUSE_BUTTON_CANCEL)
+          {
+             down_info->last_win = 0;
+             down_info->last_last_win = 0;
+             down_info->last_event_win = 0;
+             down_info->last_last_event_win = 0;
+             down_info->last_time = 0;
+             down_info->last_last_time = 0;
+             down_info->did_double = EINA_FALSE;
+             down_info->did_triple = EINA_FALSE;
+          }
         if ((event == ECORE_EVENT_MOUSE_BUTTON_DOWN) &&
             down_info->did_triple)
           {
@@ -498,7 +510,7 @@ _ecore_mouse_button(int event,
                        down_info->did_triple = EINA_FALSE;
                     }
                }
-             else
+             else if (event == ECORE_EVENT_MOUSE_BUTTON_UP)
                {
                   if (down_info->did_double)
                     e->double_click = 1;
index cbb709b..2c3acaf 100644 (file)
@@ -468,6 +468,36 @@ _ecore_x_input_multi_handler(XEvent *xevent)
              _ecore_x_input_touch_index_clear(devid,  i);
           }
         break;
+#ifdef XI_TouchCancel
+      case XI_TouchCancel:
+          {
+             XIDeviceEvent *evd = (XIDeviceEvent *)(xevent->xcookie.data);
+             int devid = evd->deviceid;
+             int i = _ecore_x_input_touch_index_get(devid, evd->detail, XI_TouchEnd);
+
+             /* X maybe send several cancel events, but ecore_x only deals with cancel event of first touch
+              * But ecore keeps all Xevent info for future */
+             if ((i != 0) || !(evd->flags & XITouchEmulatingPointer)) return;
+
+             INF("ButtonEvent: cancel time=%u x=%d y=%d devid=%d", (unsigned int)evd->time, (int)evd->event_x, (int)evd->event_y, devid);
+             _ecore_mouse_button(ECORE_EVENT_MOUSE_BUTTON_CANCEL,
+                                 evd->time,
+                                 0,   // state
+                                 0,   // button
+                                 evd->event_x, evd->event_y,
+                                 evd->root_x, evd->root_y,
+                                 evd->event,
+                                (evd->child ? evd->child : evd->event),
+                                 evd->root,
+                                 1,   // same_screen
+                                 i, 1, 1,
+                                 1.0,   // pressure
+                                 0.0,   // angle
+                                 evd->event_x, evd->event_y,
+                                 evd->root_x, evd->root_y);
+          }
+        break;
+#endif
 #endif /* ifdef ECORE_XI2_2 */
       default:
         break;
@@ -791,6 +821,9 @@ _ecore_x_input_touch_devices_grab(Ecore_X_Window grab_win, Eina_Bool grab)
                   XISetMask(mask, XI_TouchUpdate);
                   XISetMask(mask, XI_TouchBegin);
                   XISetMask(mask, XI_TouchEnd);
+#ifdef XI_TouchCancel
+                  XISetMask(mask, XI_TouchCancel);
+#endif
                   update = 1;
                   free(info);
                }