--- /dev/null
+#include <e.h>
+#include "e_mod_main.h"
+#include "e_mod_pol_pingpong.h"
+
+#define PONG_WAIT_TIME 2.0
+
+typedef struct _PP_Win
+{
+ E_Client *ec;
+ Ecore_Timer *t_pong_wait;
+ Eina_Bool ping_sent;
+} PP_Win;
+
+static Eina_List *_handlers = NULL;
+static Eina_List *_pp_wins = NULL;
+
+static void _cb_client_mouse_down(void *data, Evas *e, Evas_Object *o, void *event);
+
+static PP_Win *
+_pp_win_add(E_Client *ec)
+{
+ PP_Win *ppw;
+
+ ppw = E_NEW(PP_Win, 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ppw, NULL);
+
+ ppw->ec = ec;
+
+ evas_object_event_callback_add(ec->frame,
+ EVAS_CALLBACK_MOUSE_DOWN,
+ _cb_client_mouse_down,
+ ppw);
+
+ _pp_wins = eina_list_append(_pp_wins, ppw);
+
+ return ppw;
+}
+
+static void
+_pp_win_del(PP_Win *ppw)
+{
+ if (ppw->t_pong_wait)
+ ecore_timer_del(ppw->t_pong_wait);
+
+ evas_object_event_callback_del(ppw->ec->frame,
+ EVAS_CALLBACK_MOUSE_DOWN,
+ _cb_client_mouse_down);
+ E_FREE(ppw);
+}
+
+static PP_Win *
+_pp_win_find(E_Client *ec)
+{
+ PP_Win *ppw = NULL;
+ Eina_List *l;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
+
+ EINA_LIST_FOREACH(_pp_wins, l, ppw)
+ {
+ if (ppw->ec == ec)
+ return ppw;
+ }
+
+ return NULL;
+}
+
+static Eina_Bool
+_cb_pong_check(void *data)
+{
+ PP_Win *ppw = (PP_Win *)data;
+ E_Client* ec = ppw->ec;
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ppw->ping_sent, ECORE_CALLBACK_CANCEL);
+
+ ppw->t_pong_wait = NULL;
+ ppw->ping_sent = EINA_FALSE;
+
+ ELOGF("PP", "PONG CHK | pid:%d name:%s ping_ok:%u",
+ ec, ec->netwm.pid, ec->icccm.name, ec->ping_ok);
+
+ if (ec->ping_ok) return ECORE_CALLBACK_CANCEL;
+
+ /* TODO: kill the unresponsive client here */
+ ;;
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_ping_send(PP_Win *ppw)
+{
+ E_Client *ec = ppw->ec;
+
+ /* if ping is already sent, do nothing */
+ if (ppw->t_pong_wait)
+ {
+ ELOGF("PP", "PING SKIP | pid:%d name:%s",
+ ec, ec->netwm.pid, ec->icccm.name);
+ return EINA_FALSE;
+ }
+
+ ELOGF("PP", "PING SENT | pid:%d name:%s",
+ ec, ec->netwm.pid, ec->icccm.name);
+
+ ec->ping_ok = 0;
+ evas_object_smart_callback_call(ec->frame, "ping", NULL);
+
+ ppw->t_pong_wait = ecore_timer_add(PONG_WAIT_TIME,
+ _cb_pong_check,
+ ppw);
+ ppw->ping_sent = EINA_TRUE;
+
+ return EINA_TRUE;
+}
+
+static void
+_cb_client_mouse_down(void *data,
+ Evas *e EINA_UNUSED,
+ Evas_Object *o EINA_UNUSED,
+ void *event)
+{
+ PP_Win *ppw = (PP_Win *)data;
+ E_Client *ec = ppw->ec;
+ Eina_Bool res;
+
+ if (!ec->visible) return;
+ if (e_client_util_ignored_get(ec)) return;
+ if (!evas_object_visible_get(ec->frame)) return;
+ if (ec->visibility.obscured == E_VISIBILITY_UNKNOWN ||
+ ec->visibility.obscured == E_VISIBILITY_PARTIALLY_OBSCURED) return;
+ if (evas_object_data_get(ec->frame, "comp_skip")) return;
+
+ /* send ping to visible top-level window */
+ res = _ping_send(ppw);
+ if (!res) return;
+
+ /* get a list of tizen remote surface providers which is used in given ec */
+ if (!ec->remote_surface.consumer) return;
+
+ Eina_List *tzrs_provs, *l;
+ E_Client *prov_ec;
+ PP_Win *prov_ppw;
+
+ tzrs_provs = e_comp_wl_remote_surface_providers_get(ec);
+ if (!tzrs_provs) return;
+
+ EINA_LIST_FOREACH(tzrs_provs, l, prov_ec)
+ {
+ /* check remote surface provider */
+ if (!prov_ec->remote_surface.provider) continue;
+ if (prov_ec->comp_data->remote_surface.onscreen_parent != ec) continue;
+ if (prov_ec->visible) continue;
+ if (evas_object_visible_get(prov_ec->frame)) continue;
+ if (prov_ec->comp_data->mapped) continue;
+
+ prov_ppw = _pp_win_find(prov_ec);
+ if (!prov_ppw)
+ prov_ppw = _pp_win_add(prov_ec);
+
+ /* send ping to invisible tizen remote surface provider */
+ _ping_send(prov_ppw);
+ }
+
+ eina_list_free(tzrs_provs);
+}
+
+static Eina_Bool
+_cb_client_show(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event)
+{
+ PP_Win *ppw = NULL;
+ E_Event_Client *ev = (E_Event_Client *)event;
+ E_Client *ec = ev->ec;
+
+ ppw = _pp_win_find(ec);
+ if (ppw) return ECORE_CALLBACK_PASS_ON;
+
+ ppw = _pp_win_add(ec);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ppw, ECORE_CALLBACK_PASS_ON);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_client_hide(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event)
+{
+ PP_Win *ppw = NULL;
+ E_Event_Client *ev = (E_Event_Client *)event;
+ E_Client *ec = ev->ec;
+
+ ppw = _pp_win_find(ec);
+ if (!ppw) return ECORE_CALLBACK_PASS_ON;
+
+ _pp_wins = eina_list_remove(_pp_wins, ppw);
+ _pp_win_del(ppw);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_cb_client_del(void *data EINA_UNUSED,
+ int type EINA_UNUSED,
+ void *event)
+{
+ PP_Win *ppw = NULL;
+ E_Event_Client *ev = (E_Event_Client *)event;
+ E_Client *ec = ev->ec;
+
+ ppw = _pp_win_find(ec);
+ if (!ppw) return ECORE_CALLBACK_PASS_ON;
+
+ _pp_wins = eina_list_remove(_pp_wins, ppw);
+ _pp_win_del(ppw);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+EINTERN void
+e_mod_pol_pingpong_init(void)
+{
+ if (!e_mod_pol_conf_use_pingpoing_policy_get()) return;
+
+ E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_SHOW, _cb_client_show, NULL);
+ E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_HIDE, _cb_client_hide, NULL);
+ E_LIST_HANDLER_APPEND(_handlers, E_EVENT_CLIENT_REMOVE, _cb_client_del, NULL);
+}
+
+EINTERN void
+e_mod_pol_pingpong_shutdown(void)
+{
+ if (!e_mod_pol_conf_use_pingpoing_policy_get()) return;
+
+ E_FREE_LIST(_pp_wins, _pp_win_del);
+ E_FREE_LIST(_handlers, ecore_event_handler_del);
+}