splitscreen: Add new split screen feature 23/290523/10
authorJunseok Kim <juns.kim@samsung.com>
Tue, 28 Mar 2023 06:07:01 +0000 (15:07 +0900)
committerJunseok Kim <juns.kim@samsung.com>
Mon, 10 Apr 2023 09:28:55 +0000 (18:28 +0900)
Split screen allows to use multiple application on a single screen by dividing the screen into logical areas.

Change-Id: I5f2e70074e75952ac8331b655e79495f7293aa79

src/Makefile.am
src/e_mod_main.c
src/splitscreen/e_mod_appinfo_ext.c [new file with mode: 0644]
src/splitscreen/e_mod_appinfo_ext.h [new file with mode: 0644]
src/splitscreen/e_mod_split_screen_manager.c [new file with mode: 0644]
src/splitscreen/e_mod_split_screen_manager.h [new file with mode: 0644]
src/splitscreen/e_mod_split_screen_manager_log.h [new file with mode: 0644]
src/splitscreen/e_mod_split_screen_region.c [new file with mode: 0644]
src/splitscreen/e_mod_split_screen_region.h [new file with mode: 0644]

index bfbfe68..920d4b4 100644 (file)
@@ -1,6 +1,6 @@
 MAINTAINERCLEANFILES = Makefile.in
 
-AM_CPPFLAGS = -I./rotation
+AM_CPPFLAGS = -I./rotation -I./splitscreen
 
 MODULE = e-mod-tizen-wm-policy
 
@@ -23,6 +23,14 @@ endif
 ROT_SRC  += rotation/e_mod_rotation_wl.c \
             rotation/e_mod_rotation_wl.h
 
+SPLITSCREEN_SRC = splitscreen/e_mod_split_screen_manager.c \
+                  splitscreen/e_mod_split_screen_manager.h \
+                  splitscreen/e_mod_split_screen_manager_log.h \
+                  splitscreen/e_mod_split_screen_region.c \
+                  splitscreen/e_mod_split_screen_region.h \
+                  splitscreen/e_mod_appinfo_ext.c \
+                  splitscreen/e_mod_appinfo_ext.h
+
 module_la_SOURCES      = e_mod_config.c \
                          e_mod_main.c \
                          e_mod_main.h \
@@ -30,7 +38,8 @@ module_la_SOURCES      = e_mod_config.c \
                          e_mod_pol_pingpong.h \
                          e_mod_configured_resolution.c \
                          e_mod_configured_resolution.h \
-                         $(ROT_SRC)
+                         $(ROT_SRC) \
+                         $(SPLITSCREEN_SRC)
 
 module_la_LIBADD       =
 module_la_CFLAGS       = @ENLIGHTENMENT_CFLAGS@  @TTRACE_CFLAGS@ @CAPI_SYSTEM_INFO_CFLAGS@
index 339d1c2..af20675 100644 (file)
@@ -2,6 +2,7 @@
 #include "e_mod_rotation.h"
 #include "e_mod_pol_pingpong.h"
 #include "e_mod_configured_resolution.h"
+#include "e_mod_split_screen_manager.h"
 #include <stdlib.h>
 #include <system_info.h>
 
@@ -36,6 +37,7 @@ e_modapi_init(E_Module *m)
    _e_mod_pol_rotation_init();
    e_mod_pol_pingpong_init();
    e_mod_configured_resolution_init();
+   e_mod_split_screen_manager_init();
 
    return mod;
 }
@@ -45,6 +47,7 @@ e_modapi_shutdown(E_Module *m)
 {
    Mod *mod = m->data;
 
+   e_mod_split_screen_manager_shutdown();
    e_mod_configured_resolution_shutdown();
    e_mod_pol_pingpong_shutdown();
    e_mod_pol_rotation_shutdown();
diff --git a/src/splitscreen/e_mod_appinfo_ext.c b/src/splitscreen/e_mod_appinfo_ext.c
new file mode 100644 (file)
index 0000000..f259f61
--- /dev/null
@@ -0,0 +1,32 @@
+#include "e.h"
+#include "e_appinfo.h"
+#include "e_mod_appinfo_ext.h"
+
+/* e_appinfo extension */
+EINTERN pid_t
+e_mod_appinfo_ext_find_pid_by_appid(const char *appid)
+{
+   return e_appinfo_pid_get(e_appinfo_find_with_appid(appid));
+}
+
+EINTERN Eina_List *
+e_mod_appinfo_ext_find_clients_by_appid(const char *appid)
+{
+   E_Client *ec;
+   E_Appinfo *eai = NULL;
+   pid_t pid = -1;
+   Eina_List *ec_list = NULL;
+
+   eai = e_appinfo_find_with_appid(appid);
+   pid = e_appinfo_pid_get(eai);
+
+   E_CLIENT_REVERSE_FOREACH(ec)
+     {
+        if (ec && ec->netwm.pid == pid)
+          {
+             ec_list = eina_list_append(ec_list, ec);
+          }
+     }
+
+   return ec_list;
+}
diff --git a/src/splitscreen/e_mod_appinfo_ext.h b/src/splitscreen/e_mod_appinfo_ext.h
new file mode 100644 (file)
index 0000000..45dd9b6
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef E_MOD_APPINFO_EXT_H
+#define E_MOD_APPINFO_EXT_H
+
+EINTERN pid_t      e_mod_appinfo_ext_find_pid_by_appid(const char *appid);
+EINTERN Eina_List *e_mod_appinfo_ext_find_clients_by_appid(const char *appid);
+
+#endif
diff --git a/src/splitscreen/e_mod_split_screen_manager.c b/src/splitscreen/e_mod_split_screen_manager.c
new file mode 100644 (file)
index 0000000..98bf297
--- /dev/null
@@ -0,0 +1,696 @@
+#include "e.h"
+#include "e_mod_split_screen_manager_log.h"
+#include "e_mod_split_screen_manager.h"
+#include "e_mod_split_screen_region.h"
+#include "e_mod_appinfo_ext.h"
+
+typedef Eina_Bool (*E_Mod_Split_Screen_Manager_Hint_Func)(E_Client *ec, const char *name, const char *value);
+struct _E_Mod_Split_Screen_Manager_Hint_Handler
+{
+   int type;
+   const char *name;
+   E_Mod_Split_Screen_Manager_Hint_Func func;
+};
+
+typedef struct _E_Mod_Split_Screen_Manager_Hint_Handler E_Mod_Split_Screen_Manager_Hint_Handler;
+
+enum _E_Mod_Split_Screen_Manager_Hint_Type
+{
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION = 0,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_LAUNCHER_ROLE_SET,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION_SUB1,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION_SUB2,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_ACTIVATE,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_DEACTIVATE,
+   E_MOD_SPLIT_SCREEN_MANAGER_HINT_MAX,
+};
+
+static Eina_List *_e_mod_split_screen_manager_ec_hook_handlers = NULL;
+static Eina_List *_e_mod_split_screen_manager_ec_event_handlers = NULL;
+static Eina_List *_e_mod_split_screen_manager_desk_area_hook_handlers = NULL;
+static Eina_List *_e_mod_split_screen_manager_zone_hook_handlers = NULL;
+
+static Eina_List *_e_mod_split_screen_region_list = NULL;
+static E_Client *_e_mod_split_screen_launcher_ec = NULL;
+
+/* e_client hooks */
+static void _e_mod_split_screen_manager_cb_hook_aux_hint_change(void *data, E_Client *ec);
+
+/* e_client events */
+static Eina_Bool _e_mod_split_screen_manager_cb_ec_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+static Eina_Bool _e_mod_split_screen_manager_cb_ec_remove(void *data EINA_UNUSED, int type EINA_UNUSED, void *event);
+
+/* Aux hint handlers */
+static Eina_Bool     _e_mod_split_screen_manager_hint_launcher_role_set(E_Client *ec, const char *name, const char *value);
+static Eina_Bool     _e_mod_split_screen_manager_hint_assign_region(E_Client *ec, const char *name, const char *value);
+static Eina_Bool     _e_mod_split_screen_manager_hint_assign_region_sub1(E_Client *ec, const char *name, const char *value);
+static Eina_Bool     _e_mod_split_screen_manager_hint_assign_region_sub2(E_Client *ec, const char *name, const char *value);
+static Eina_Bool     _e_mod_split_screen_manager_hint_activate(E_Client *ec, const char *name, const char *value);
+static Eina_Bool     _e_mod_split_screen_manager_hint_deactivate(E_Client *ec, const char *name, const char *value);
+
+/* e_mod_split_screen_manager internal functions */
+static void          _e_mod_split_screen_manager_client_manager_role_set(E_Client *ec, Eina_Bool set);
+static Eina_Bool     _e_mod_split_screen_activate(E_Zone *zone);
+static Eina_Bool     _e_mod_split_screen_deactivate(E_Zone *zone);
+static void          _e_mod_split_screen_launcher_ec_set(E_Client *ec);
+static E_Client *    _e_mod_split_screen_launcher_ec_get(void);
+static E_Mod_Split_Screen_Region *_e_mod_split_screen_manager_find_region_by_name(const char *name);
+static E_Mod_Split_Screen_Region *_e_mod_split_screen_manager_find_region_by_desk_area(E_Desk_Area *eda);
+static void          _e_mod_split_screen_manager_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid);
+static Eina_Bool     _e_mod_split_screen_manager_ec_assign_to_region_name(E_Client *ec, const char *region_name);
+static Eina_Bool     _e_mod_split_screen_manager_ec_relocate(E_Client *ec);
+static Eina_Bool     _e_mod_split_screen_manager_ec_relocate_to_region(E_Client *ec, E_Mod_Split_Screen_Region *emssr);
+
+/* initializer */
+static Eina_Bool _e_mod_split_screen_manager_handler_init(void);
+static Eina_Bool _e_mod_split_screen_manager_aux_hint_handler_init(void);
+static void      _e_mod_split_screen_manager_handler_shutdown(void);
+static void      _e_mod_split_screen_manager_aux_hint_handler_shutdown(void);
+static Eina_Bool _e_mod_split_screen_manager_region_init(void);
+static void      _e_mod_split_screen_manager_region_shutdown(void);
+
+static const E_Mod_Split_Screen_Manager_Hint_Handler _e_mod_split_screen_manager_hint_handler[] =
+{
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION, "wm.splitscreen.win.assign_region", _e_mod_split_screen_manager_hint_assign_region},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_LAUNCHER_ROLE_SET, "wm.splitscreen.win.launcher.role_set", _e_mod_split_screen_manager_hint_launcher_role_set},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION_SUB1, "wm.splitscreen.win.split_screen_manager.assign_region.sub1", _e_mod_split_screen_manager_hint_assign_region_sub1},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_ASSIGN_REGION_SUB2, "wm.splitscreen.win.split_screen_manager.assign_region.sub2", _e_mod_split_screen_manager_hint_assign_region_sub2},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_ACTIVATE, "wm.splitscreen.win.split_screen_manager.activate", _e_mod_split_screen_manager_hint_activate},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_DEACTIVATE, "wm.splitscreen.win.split_screen_manager.deactivate", _e_mod_split_screen_manager_hint_deactivate},
+     {E_MOD_SPLIT_SCREEN_MANAGER_HINT_MAX, NULL, NULL},
+};
+
+/* Hook handlers */
+static void
+_e_mod_split_screen_manager_cb_hook_aux_hint_change(void *data, E_Client *ec)
+{
+   const char *hint, *val;
+
+   if (!ec)
+     {
+        SSMINF("ec is NULL", NULL);
+        return;
+     }
+
+   for (int i=0 ; i<E_MOD_SPLIT_SCREEN_MANAGER_HINT_MAX ; i++)
+     {
+        hint = _e_mod_split_screen_manager_hint_handler[i].name;
+        val = e_hints_aux_hint_value_get(ec, hint);
+        if (!val) continue;
+
+        _e_mod_split_screen_manager_hint_handler[i].func(ec, hint, val);
+     }
+
+   return;
+}
+
+static void
+_e_mod_split_screen_manager_cb_hook_eval_fetch(void *data, E_Client *ec)
+{
+   if (!ec)
+     {
+        SSMINF("ec is NULL", NULL);
+        return;
+     }
+   // TODO: FIX blinking issue while launching child window
+   _e_mod_split_screen_manager_ec_relocate(ec);
+
+   return;
+}
+
+static void
+_e_mod_split_screen_manager_cb_hook_assign_appid(void *data EINA_UNUSED, E_Desk_Area *eda, void *appid_ptr)
+{
+   E_Mod_Split_Screen_Region *emssr;
+   Eina_Stringshare *appid;
+
+   EINA_SAFETY_ON_NULL_RETURN(eda);
+   EINA_SAFETY_ON_NULL_RETURN(appid_ptr);
+
+   appid = eina_stringshare_add(appid_ptr);
+   emssr = _e_mod_split_screen_manager_find_region_by_desk_area(eda);
+   if (!emssr)
+     {
+        SSMERR("Failed to find region for desk area %p", NULL, eda);
+        return;
+     }
+
+   _e_mod_split_screen_manager_appid_assign(emssr, appid);
+
+   return;
+}
+
+static void
+_e_mod_split_screen_manager_cb_hook_activate(void *data, E_Zone *zone)
+{
+   if (!zone) return;
+   _e_mod_split_screen_activate(zone);
+}
+
+static void
+_e_mod_split_screen_manager_cb_hook_deactivate(void *data, E_Zone *zone)
+{
+   if (!zone) return;
+   _e_mod_split_screen_deactivate(zone);
+}
+
+
+/* e_client events */
+static Eina_Bool
+_e_mod_split_screen_manager_cb_ec_add(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   _e_mod_split_screen_manager_ec_relocate(ev->ec);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_cb_ec_remove(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   E_Event_Client *ev;
+
+   ev = event;
+   if (!ev) return ECORE_CALLBACK_PASS_ON;
+
+   e_mod_split_screen_region_ec_dismiss(ev->ec);
+
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+/* Aux hint handlers */
+static Eina_Bool
+_e_mod_split_screen_manager_hint_launcher_role_set(E_Client *ec, const char *name, const char *value)
+{
+   if (!strncmp(value, "1", 1))
+     {
+        _e_mod_split_screen_manager_client_manager_role_set(ec, EINA_TRUE);
+     }
+   else
+     {
+        _e_mod_split_screen_manager_client_manager_role_set(ec, EINA_FALSE);
+     }
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_hint_assign_region(E_Client *ec, const char *name, const char *value)
+{
+   E_Mod_Split_Screen_Region *emssr;
+   Eina_Stringshare *appid;
+
+   char _region_name[10] = {0,};
+   char _appid[255] = {0,};
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE);
+
+   // hint value format: region_name/appid
+   sscanf(value, "%[^/]/%s", _region_name, _appid);
+   SSMINF("client appid(%s) -> assign to region:%s", ec, _appid, _region_name);
+
+   if ((strlen(_region_name) <= 0) || strlen(_region_name) > 10)
+     {
+        SSMERR("invalid region name:%s", ec, _region_name);
+        return EINA_FALSE;
+     }
+
+   if ((strlen(_appid) <= 0) || (strlen(_appid) > 255))
+     {
+        SSMERR("invalid app id:%s", ec, _appid);
+        return EINA_FALSE;
+     }
+
+   emssr = _e_mod_split_screen_manager_find_region_by_name(_region_name);
+   if (!emssr)
+     {
+        SSMERR("invalid region name:%s", ec, _region_name);
+        return EINA_FALSE;
+     }
+
+   appid = eina_stringshare_add(_appid);
+
+   _e_mod_split_screen_manager_appid_assign(emssr, appid);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_hint_assign_region_sub1(E_Client *ec, const char *name, const char *value)
+{
+   E_Mod_Split_Screen_Region *emssr;
+   Eina_Stringshare *appid;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE);
+
+   if ((strlen(value) <= 0) || strlen(value) > 255)
+     {
+        SSMERR("invalid app id:%s", ec, value);
+        return EINA_FALSE;
+     }
+
+   appid = eina_stringshare_add(value);
+
+   emssr = _e_mod_split_screen_manager_find_region_by_name("sub1");
+   if (!emssr)
+     {
+        SSMERR("invalid region name:%s", ec, "sub1");
+        return EINA_FALSE;
+     }
+
+   _e_mod_split_screen_manager_appid_assign(emssr, appid);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_hint_assign_region_sub2(E_Client *ec, const char *name, const char *value)
+{
+   E_Mod_Split_Screen_Region *emssr;
+   Eina_Stringshare *appid;
+
+   EINA_SAFETY_ON_NULL_RETURN_VAL(value, EINA_FALSE);
+
+   if ((strlen(value) <= 0) || strlen(value) > 255)
+     {
+        SSMERR("invalid app id:%s", ec, value);
+        return EINA_FALSE;
+     }
+
+   appid = eina_stringshare_add(value);
+
+   emssr = _e_mod_split_screen_manager_find_region_by_name("sub2");
+   if (!emssr)
+     {
+        SSMERR("invalid region name:%s", ec, "sub2");
+        return EINA_FALSE;
+     }
+
+   _e_mod_split_screen_manager_appid_assign(emssr, appid);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_hint_activate(E_Client *ec, const char *name, const char *value)
+{
+   return _e_mod_split_screen_activate(e_zone_current_get());
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_hint_deactivate(E_Client *ec, const char *name, const char *value)
+{
+   return _e_mod_split_screen_deactivate(e_zone_current_get());
+}
+
+/* internal functions */
+
+static void
+_e_mod_split_screen_manager_client_manager_role_set(E_Client *ec, Eina_Bool set)
+{
+   if (!ec) return;
+   if (ec->floating == set) return;
+
+   ec->floating = set;
+
+   if (ec->frame)
+     {
+        if (set)
+          {
+             _e_mod_split_screen_launcher_ec_set(ec);
+             ec->floating_saved_layer = ec->layer;
+             e_client_layer_set(ec, E_LAYER_CLIENT_ABOVE);
+             ec->netwm.type = E_WINDOW_TYPE_UTILITY;
+             _e_mod_split_screen_manager_ec_assign_to_region_name(ec, "above");
+          }
+        else
+          {
+             e_client_layer_set(ec, ec->floating_saved_layer);
+             ec->netwm.type = E_WINDOW_TYPE_NORMAL;
+             e_mod_split_screen_region_ec_dismiss(ec);
+          }
+     }
+
+   e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
+   e_comp_object_dirty(ec->frame);
+   e_comp_object_render(ec->frame);
+
+   EC_CHANGED(ec);
+}
+
+static Eina_Bool
+_e_mod_split_screen_activate(E_Zone *zone)
+{
+   E_Desk *desk;
+   E_Client *ec, *launcher;
+   Eina_Bool ret = EINA_FALSE;
+
+   SSMINF("ACTIVATE split screen", NULL);
+
+   desk = e_desk_current_get(zone);
+   ret = e_desk_desk_area_enable(desk);
+
+   // relocate all e clients
+   E_CLIENT_FOREACH(ec)
+     {
+        _e_mod_split_screen_manager_ec_relocate(ec);
+     }
+
+   // launcher window must be above desk group to unobscured by other windows
+   launcher = _e_mod_split_screen_launcher_ec_get();
+   if (launcher)
+     _e_mod_split_screen_manager_ec_assign_to_region_name(launcher, "above");
+
+   return ret;
+}
+
+static Eina_Bool
+_e_mod_split_screen_deactivate(E_Zone *zone)
+{
+   E_Desk *desk;
+   Eina_Bool ret = EINA_FALSE;
+
+   SSMINF("DEACTIVATE split screen", NULL);
+
+   desk = e_desk_current_get(zone);
+   ret = e_desk_desk_area_disable(desk);
+
+   return ret;
+}
+
+static void
+_e_mod_split_screen_launcher_ec_set(E_Client *ec)
+{
+   _e_mod_split_screen_launcher_ec = ec;
+}
+
+static E_Client *
+_e_mod_split_screen_launcher_ec_get(void)
+{
+   return _e_mod_split_screen_launcher_ec;
+}
+
+static E_Mod_Split_Screen_Region *
+_e_mod_split_screen_manager_find_region_by_name(const char *name)
+{
+   E_Mod_Split_Screen_Region *emssr = NULL;
+   Eina_List *l;
+   const char *region_name;
+
+   EINA_LIST_FOREACH(_e_mod_split_screen_region_list, l, emssr)
+     {
+        region_name = e_mod_split_screen_region_name_get(emssr);
+        if (region_name && !strncmp(region_name, name, strlen(region_name))) break;
+     }
+
+   return emssr;
+}
+
+static E_Mod_Split_Screen_Region *
+_e_mod_split_screen_manager_find_region_by_desk_area(E_Desk_Area *eda)
+{
+   E_Mod_Split_Screen_Region *emssr = NULL;
+   Eina_List *l;
+
+   EINA_LIST_FOREACH(_e_mod_split_screen_region_list, l, emssr)
+     {
+        if (e_mod_split_screen_region_desk_area_get(emssr) == eda) break;
+     }
+
+   return emssr;
+}
+
+static void
+_e_mod_split_screen_manager_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid)
+{
+   E_Mod_Split_Screen_Region *emssr_iter;
+   E_Client *ec;
+   Eina_Stringshare *region_name;
+   Eina_List *l;
+
+   // reassign AppID to Split screen region
+   EINA_LIST_FOREACH(_e_mod_split_screen_region_list, l, emssr_iter)
+     {
+        e_mod_split_screen_region_appid_dismiss(emssr_iter, appid);
+     }
+   e_mod_split_screen_region_appid_assign(emssr, appid);
+
+   // assign clients of appID to Split screen region
+   l = e_mod_appinfo_ext_find_clients_by_appid(appid);
+   EINA_LIST_FREE(l, ec)
+     {
+        e_mod_split_screen_region_ec_assign(emssr, ec);
+     }
+
+   region_name = e_mod_split_screen_region_name_get(emssr);
+   SSMINF("from now on, appid %s will be assign to region:%s", NULL, appid, region_name);
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_util_client_special_check(E_Client *ec)
+{
+   if (!ec) return EINA_FALSE;
+
+   if (_e_mod_split_screen_launcher_ec_get() == ec) return EINA_TRUE;
+
+   if ((e_policy_client_is_lockscreen(ec)) ||
+       (e_policy_client_is_keyboard(ec)) ||
+       (e_policy_client_is_keyboard_sub(ec)) ||
+       (e_policy_client_is_noti(ec)) ||
+       (e_policy_client_is_quickpanel(ec)) ||
+       (e_policy_client_is_volume(ec)) ||
+       (e_policy_client_is_cursor(ec)) ||
+       (!e_util_strcmp("wl_pointer-cursor", ec->icccm.window_role)))
+     return EINA_TRUE;
+
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_ec_assign_to_region_name(E_Client *ec, const char *region_name)
+{
+   E_Mod_Split_Screen_Region *emssr = NULL;
+
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+   if (!region_name) return ECORE_CALLBACK_PASS_ON;
+
+   emssr = _e_mod_split_screen_manager_find_region_by_name(region_name);
+   if (!emssr)
+     {
+        SSMERR("invalid region name:%s", ec, region_name);
+        return EINA_FALSE;
+     }
+   return e_mod_split_screen_region_ec_assign(emssr, ec);
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_ec_relocate(E_Client *ec)
+{
+   E_Mod_Split_Screen_Region *emssr = NULL;
+   E_Client *parent_ec = NULL;
+   Eina_Bool res = EINA_FALSE;
+   Eina_List *l;
+
+   if (!ec) return ECORE_CALLBACK_PASS_ON;
+
+   if (_e_mod_split_screen_manager_util_client_special_check(ec))
+     {
+        // special prupose windows.
+        _e_mod_split_screen_manager_ec_assign_to_region_name(ec, "above");
+        return EINA_TRUE;
+     }
+
+   EINA_LIST_FOREACH(_e_mod_split_screen_region_list, l, emssr)
+     {
+        res |= _e_mod_split_screen_manager_ec_relocate_to_region(ec, emssr);
+     }
+
+   if (!res)
+     {
+        // find the parent ec that is in the region
+        for (parent_ec = ec->parent;
+             parent_ec != NULL;
+             parent_ec = parent_ec->parent)
+          {
+             EINA_LIST_FOREACH(_e_mod_split_screen_region_list, l, emssr)
+               {
+                  res |= _e_mod_split_screen_manager_ec_relocate_to_region(parent_ec, emssr);
+               }
+          }
+     }
+
+   return res;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_ec_relocate_to_region(E_Client *ec, E_Mod_Split_Screen_Region *emssr)
+{
+   Eina_Stringshare *appid;
+   Eina_List *appid_list, *l;
+   Eina_Bool res = EINA_FALSE;
+
+   if (!ec || !emssr) return EINA_FALSE;
+
+   appid_list = e_mod_split_screen_region_appid_list_get(emssr);
+
+   EINA_LIST_FOREACH(appid_list, l, appid)
+       if (ec->netwm.pid == e_mod_appinfo_ext_find_pid_by_appid(appid))
+         {
+            Eina_Stringshare *region_name = e_mod_split_screen_region_name_get(emssr);
+            SSMINF("client(%p):%s rearrange to region:%s",
+                   ec, ec, ec ? ec->icccm.name : "NULL", region_name);
+            res |= e_mod_split_screen_region_ec_assign(emssr, ec);
+         }
+
+   return res;
+}
+
+/* split screen initializer */
+
+static Eina_Bool
+_e_mod_split_screen_manager_handler_init(void)
+{
+   E_Desk_Area_Hook *eda_hook;
+   E_Zone_Hook *ez_hook;
+
+   E_LIST_HANDLER_APPEND(_e_mod_split_screen_manager_ec_event_handlers, E_EVENT_CLIENT_ADD, _e_mod_split_screen_manager_cb_ec_add, NULL);
+   E_LIST_HANDLER_APPEND(_e_mod_split_screen_manager_ec_event_handlers, E_EVENT_CLIENT_REMOVE, _e_mod_split_screen_manager_cb_ec_remove, NULL);
+
+   E_LIST_HOOK_APPEND(_e_mod_split_screen_manager_ec_hook_handlers, E_CLIENT_HOOK_EVAL_FETCH, _e_mod_split_screen_manager_cb_hook_eval_fetch, NULL);
+
+   eda_hook = e_desk_area_hook_add(E_DESK_AREA_HOOK_SET_APPID, _e_mod_split_screen_manager_cb_hook_assign_appid, NULL);
+   if (eda_hook) _e_mod_split_screen_manager_desk_area_hook_handlers = eina_list_append(_e_mod_split_screen_manager_desk_area_hook_handlers, eda_hook);
+
+   ez_hook = e_zone_hook_add(E_ZONE_HOOK_SPLISCREEN_ACTIVATE, _e_mod_split_screen_manager_cb_hook_activate, NULL);
+   if (ez_hook) _e_mod_split_screen_manager_zone_hook_handlers = eina_list_append(_e_mod_split_screen_manager_zone_hook_handlers, ez_hook);
+
+   ez_hook = e_zone_hook_add(E_ZONE_HOOK_SPLISCREEN_DEACTIVATE, _e_mod_split_screen_manager_cb_hook_deactivate, NULL);
+   if (ez_hook) _e_mod_split_screen_manager_zone_hook_handlers = eina_list_append(_e_mod_split_screen_manager_zone_hook_handlers, ez_hook);
+
+   if (!_e_mod_split_screen_manager_ec_event_handlers ||
+       !_e_mod_split_screen_manager_ec_hook_handlers ||
+       !_e_mod_split_screen_manager_desk_area_hook_handlers)
+     return EINA_FALSE;
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_aux_hint_handler_init(void)
+{
+   E_LIST_HOOK_APPEND(_e_mod_split_screen_manager_ec_hook_handlers, E_CLIENT_HOOK_AUX_HINT_CHANGE, _e_mod_split_screen_manager_cb_hook_aux_hint_change, NULL);
+
+   if (!_e_mod_split_screen_manager_ec_hook_handlers)
+     return EINA_FALSE;
+
+   for (int i=0 ; i<E_MOD_SPLIT_SCREEN_MANAGER_HINT_MAX ; i++)
+     {
+        e_hints_aux_hint_supported_add(_e_mod_split_screen_manager_hint_handler[i].name);
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_e_mod_split_screen_manager_handler_shutdown(void)
+{
+   E_FREE_LIST(_e_mod_split_screen_manager_ec_event_handlers, ecore_event_handler_del);
+   E_FREE_LIST(_e_mod_split_screen_manager_desk_area_hook_handlers, e_desk_area_hook_del);
+}
+
+static void
+_e_mod_split_screen_manager_aux_hint_handler_shutdown(void)
+{
+   E_FREE_LIST(_e_mod_split_screen_manager_ec_hook_handlers, e_client_hook_del);
+
+   for (int i=0 ; i<E_MOD_SPLIT_SCREEN_MANAGER_HINT_MAX ; i++)
+     {
+        e_hints_aux_hint_supported_del(_e_mod_split_screen_manager_hint_handler[i].name);
+     }
+}
+
+static Eina_Bool
+_e_mod_split_screen_manager_region_init(void)
+{
+   E_Desk *desk;
+   E_Mod_Split_Screen_Region *emssr_sub1, *emssr_sub2, *emssr_above;
+   int desk_x, desk_y, desk_w, desk_h;
+
+   desk = e_desk_current_get(e_zone_current_get());
+   EINA_SAFETY_ON_NULL_RETURN_VAL(desk, EINA_FALSE);
+
+   desk_x = desk->geom.x;
+   desk_y = desk->geom.y;
+   desk_w = desk->geom.w;
+   desk_h = desk->geom.h;
+
+   emssr_sub1 = e_mod_split_screen_region_new("sub1", desk_x, desk_y, desk_w, desk_h / 2);
+   if (!emssr_sub1) return EINA_FALSE;
+
+   _e_mod_split_screen_region_list = eina_list_append(_e_mod_split_screen_region_list, emssr_sub1);
+
+   emssr_sub2 = e_mod_split_screen_region_new("sub2", desk_x, desk_y + desk_h / 2, desk_w, desk_h / 2);
+   if (!emssr_sub2) return EINA_FALSE;
+
+   _e_mod_split_screen_region_list = eina_list_append(_e_mod_split_screen_region_list, emssr_sub2);
+
+   emssr_above = e_mod_split_screen_region_new("above", desk_x, desk_y, desk_w, desk_h);
+   if (!emssr_above) return EINA_FALSE;
+
+   _e_mod_split_screen_region_list = eina_list_append(_e_mod_split_screen_region_list, emssr_above);
+
+   return EINA_TRUE;
+}
+
+static void
+_e_mod_split_screen_manager_region_shutdown(void)
+{
+   E_Mod_Split_Screen_Region *emssr;
+
+   if (!_e_mod_split_screen_region_list) return;
+
+   EINA_LIST_FREE(_e_mod_split_screen_region_list, emssr)
+     {
+        e_mod_split_screen_region_del(emssr);
+     }
+}
+
+EINTERN Eina_Bool
+e_mod_split_screen_manager_init(void)
+{
+   Eina_Bool res;
+
+   SSMDBG("SPLIT_SCREEN_MANAGER module init", NULL);
+
+   e_zone_screen_splitscreen_enable(e_zone_current_get());
+
+   res = _e_mod_split_screen_manager_region_init();
+   EINA_SAFETY_ON_FALSE_GOTO(res, err);
+
+   res = _e_mod_split_screen_manager_handler_init();
+   EINA_SAFETY_ON_FALSE_GOTO(res, err);
+
+   res = _e_mod_split_screen_manager_aux_hint_handler_init();
+   EINA_SAFETY_ON_FALSE_GOTO(res, err);
+
+   return EINA_TRUE;
+
+err:
+   SSMINF("FAILED TO module init!!!", NULL);
+   e_mod_split_screen_manager_shutdown();
+
+   return EINA_FALSE;
+}
+
+EINTERN void
+e_mod_split_screen_manager_shutdown(void)
+{
+   SSMDBG("SPLIT_SCREEN_MANAGER module shutdown", NULL);
+   _e_mod_split_screen_manager_aux_hint_handler_shutdown();
+   _e_mod_split_screen_manager_handler_shutdown();
+   _e_mod_split_screen_manager_region_shutdown();
+}
diff --git a/src/splitscreen/e_mod_split_screen_manager.h b/src/splitscreen/e_mod_split_screen_manager.h
new file mode 100644 (file)
index 0000000..2d2feec
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef E_MOD_SPLIT_SCREEN_MANAGER_H
+#define E_MOD_SPLIT_SCREEN_MANAGER_H
+
+EINTERN Eina_Bool e_mod_split_screen_manager_init(void);
+EINTERN void      e_mod_split_screen_manager_shutdown(void);
+
+#endif
+
diff --git a/src/splitscreen/e_mod_split_screen_manager_log.h b/src/splitscreen/e_mod_split_screen_manager_log.h
new file mode 100644 (file)
index 0000000..20e8b40
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef E_MOD_SPLIT_SCREEN_MANAGER_LOG_H
+#define E_MOD_SPLIT_SCREEN_MANAGER_LOG_H
+
+#  define SSMERR(f, ec, x...)                                         \
+   do                                                                 \
+     {                                                                \
+        if (ec)                                                       \
+          ERR("EWL|%20.20s|w:0x%08zx|ec:%8p|"f,                       \
+              "SPLITSCREEN", (e_client_util_win_get(ec)), (ec), ##x); \
+        else                                                          \
+          ERR("EWL|%20.20s|            |             |"f,             \
+              "SPLITSCREEN", ##x);                                    \
+     }                                                                \
+   while (0)
+
+#  define SSMINF(f, ec, x...)                                         \
+   do                                                                 \
+     {                                                                \
+        if (ec)                                                       \
+          INF("EWL|%20.20s|w:0x%08zx|ec:%8p|"f,                       \
+              "SPLITSCREEN", (e_client_util_win_get(ec)), (ec), ##x); \
+        else                                                          \
+          INF("EWL|%20.20s|            |             |"f,             \
+              "SPLITSCREEN", ##x);                                    \
+     }                                                                \
+   while (0)
+
+#  define SSMDBG(f, ec, x...)                                         \
+   do                                                                 \
+     {                                                                \
+        if (ec)                                                       \
+          DBG("EWL|%20.20s|w:0x%08zx|ec:%8p|"f,                       \
+              "SPLITSCREEN", (e_client_util_win_get(ec)), (ec), ##x); \
+        else                                                          \
+          DBG("EWL|%20.20s|            |             |"f,             \
+              "SPLITSCREEN", ##x);                                    \
+     }                                                                \
+   while (0)
+
+#endif
diff --git a/src/splitscreen/e_mod_split_screen_region.c b/src/splitscreen/e_mod_split_screen_region.c
new file mode 100644 (file)
index 0000000..379e1ea
--- /dev/null
@@ -0,0 +1,218 @@
+#include "e.h"
+#include "e_mod_split_screen_manager_log.h"
+#include "e_mod_split_screen_region.h"
+#include "e_mod_appinfo_ext.h"
+
+struct _E_Mod_Split_Screen_Region
+{
+   E_Zone *zone;
+   E_Desk *desk;
+   E_Desk_Area *eda;
+   Eina_Stringshare *name;
+   Eina_Bool is_del;
+   Eina_List *appid_list;
+   Eina_List *client_list;
+};
+
+/* e_mod_split_screen_region internal functions */
+static E_Mod_Split_Screen_Region * _e_mod_split_screen_region_new(const char *name, int x, int y, int w, int h);
+static void          _e_mod_split_screen_region_del(E_Mod_Split_Screen_Region *emssr);
+static void          _e_mod_split_screen_region_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid);
+static void          _e_mod_split_screen_region_appid_dismiss(E_Mod_Split_Screen_Region *emssr, const char *appid);
+static Eina_Bool     _e_mod_split_screen_region_ec_assign(E_Mod_Split_Screen_Region *emssr, E_Client *ec);
+static Eina_Bool     _e_mod_split_screen_region_ec_dismiss(E_Client *ec);
+
+
+/* internal functions */
+static E_Mod_Split_Screen_Region *
+_e_mod_split_screen_region_new(const char *name, int x, int y, int w, int h)
+{
+   E_Desk *desk;
+   E_Mod_Split_Screen_Region *emssr = NULL;
+   E_Desk_Area *eda = NULL;
+
+   emssr = E_NEW(E_Mod_Split_Screen_Region, 1);
+   if (!emssr) return NULL;
+
+   emssr->zone = e_zone_current_get();
+
+   desk = e_desk_current_get(emssr->zone);
+   EINA_SAFETY_ON_NULL_GOTO(desk, err);
+
+   emssr->desk = desk;
+   emssr->name = eina_stringshare_add(name);
+
+   eda = e_desk_desk_area_add(desk, x, y, w, h, E_DESK_AREA_LAYER_NORMAL);
+   EINA_SAFETY_ON_NULL_GOTO(eda, err);
+
+   e_desk_area_transform_enable_set(eda, EINA_FALSE);
+   eda->name = eina_stringshare_add(name);
+   emssr->eda = eda;
+
+   SSMINF("region added, desk_area:%p, name:%s, id:%d", NULL, emssr->eda, emssr->name, emssr->eda->id);
+
+   return emssr;
+
+err:
+   if (emssr) E_FREE(emssr);
+   if (eda) E_FREE(eda);
+   return NULL;
+}
+
+static void
+_e_mod_split_screen_region_del(E_Mod_Split_Screen_Region *emssr)
+{
+   emssr->is_del = EINA_TRUE;
+   e_desk_desk_area_disable(emssr->desk);
+   E_FREE(emssr);
+}
+
+static void
+_e_mod_split_screen_region_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid)
+{
+   emssr->appid_list = eina_list_append(emssr->appid_list, appid);
+}
+
+static void
+_e_mod_split_screen_region_appid_dismiss(E_Mod_Split_Screen_Region *emssr, const char *appid)
+{
+   Eina_List *l, *ll;
+
+   EINA_LIST_FOREACH_SAFE(emssr->appid_list, l, ll, appid)
+     {
+        if (!e_util_strcmp(appid, appid))
+          {
+             eina_stringshare_del(appid);
+             emssr->appid_list = eina_list_remove_list(emssr->appid_list, ll);
+          }
+     }
+}
+
+static Eina_Bool
+_e_mod_split_screen_region_ec_assign(E_Mod_Split_Screen_Region *emssr, E_Client *ec)
+{
+   E_Client *child_ec;
+   Eina_List *l;
+
+   SSMDBG("set to region: %s", ec, emssr->name);
+
+   if (ec->lock_client_size)
+     {
+        ec->maximized = E_MAXIMIZE_NONE;
+        e_client_maximize(ec, E_MAXIMIZE_EXPAND | E_MAXIMIZE_BOTH);
+     }
+
+   e_client_desk_area_set(ec, emssr->eda);
+
+   // control transient for childs
+   EINA_LIST_FOREACH(ec->transients, l, child_ec)
+     {
+        _e_mod_split_screen_region_ec_assign(emssr, child_ec);
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_mod_split_screen_region_ec_dismiss(E_Client *ec)
+{
+   E_Client *child_ec;
+   Eina_List *l;
+
+   SSMINF("unset ec from regions", ec);
+
+   if (ec->lock_client_size)
+     {
+        ec->maximized = E_MAXIMIZE_NONE;
+        e_client_maximize(ec, E_MAXIMIZE_EXPAND | E_MAXIMIZE_BOTH);
+     }
+
+   e_client_desk_area_set(ec, NULL);
+
+   // control transient for childs
+   EINA_LIST_FOREACH(ec->transients, l, child_ec)
+     {
+        _e_mod_split_screen_region_ec_dismiss(child_ec);
+     }
+
+   return EINA_TRUE;
+}
+
+/* Internal APIs */
+EINTERN E_Mod_Split_Screen_Region *
+e_mod_split_screen_region_new(const char *name, int x, int y, int w, int h)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
+
+   return _e_mod_split_screen_region_new(name, x, y, w, h);
+}
+
+EINTERN void
+e_mod_split_screen_region_del(E_Mod_Split_Screen_Region *emssr)
+{
+   EINA_SAFETY_ON_NULL_RETURN(emssr);
+   _e_mod_split_screen_region_del(emssr);
+}
+
+EINTERN const char *
+e_mod_split_screen_region_name_get(E_Mod_Split_Screen_Region *emssr)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(emssr, NULL);
+   return emssr->name;
+}
+
+EINTERN E_Desk *
+e_mod_split_screen_region_desk_get(E_Mod_Split_Screen_Region *emssr)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(emssr, NULL);
+   return emssr->desk;
+}
+
+EINTERN E_Desk_Area *
+e_mod_split_screen_region_desk_area_get(E_Mod_Split_Screen_Region *emssr)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(emssr, NULL);
+   return emssr->eda;
+}
+
+EINTERN void
+e_mod_split_screen_region_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid)
+{
+   EINA_SAFETY_ON_NULL_RETURN(emssr);
+   EINA_SAFETY_ON_NULL_RETURN(appid);
+
+   _e_mod_split_screen_region_appid_dismiss(emssr, appid);
+   _e_mod_split_screen_region_appid_assign(emssr, appid);
+}
+
+EINTERN void
+e_mod_split_screen_region_appid_dismiss(E_Mod_Split_Screen_Region *emssr, const char *appid)
+{
+   EINA_SAFETY_ON_NULL_RETURN(emssr);
+   EINA_SAFETY_ON_NULL_RETURN(appid);
+
+   _e_mod_split_screen_region_appid_dismiss(emssr, appid);
+}
+
+EINTERN Eina_List *
+e_mod_split_screen_region_appid_list_get(E_Mod_Split_Screen_Region *emssr)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(emssr, NULL);
+   return emssr->appid_list;
+}
+
+EINTERN Eina_Bool
+e_mod_split_screen_region_ec_assign(E_Mod_Split_Screen_Region *emssr, E_Client *ec)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(emssr, EINA_FALSE);
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+
+   return _e_mod_split_screen_region_ec_assign(emssr, ec);
+}
+
+EINTERN Eina_Bool
+e_mod_split_screen_region_ec_dismiss(E_Client *ec)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+
+   return _e_mod_split_screen_region_ec_dismiss(ec);
+}
diff --git a/src/splitscreen/e_mod_split_screen_region.h b/src/splitscreen/e_mod_split_screen_region.h
new file mode 100644 (file)
index 0000000..02021fa
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef E_MOD_SPLIT_SCREEN_REGION_H
+#define E_MOD_SPLIT_SCREEN_REGION_H
+
+typedef struct _E_Mod_Split_Screen_Region E_Mod_Split_Screen_Region;
+
+EINTERN E_Mod_Split_Screen_Region *e_mod_split_screen_region_new(const char *name, int x, int y, int w, int h);
+EINTERN void                       e_mod_split_screen_region_del(E_Mod_Split_Screen_Region *emssr);
+
+EINTERN const char                *e_mod_split_screen_region_name_get(E_Mod_Split_Screen_Region *emssr);
+EINTERN E_Desk                    *e_mod_split_screen_region_desk_get(E_Mod_Split_Screen_Region *emssr);
+EINTERN E_Desk_Area               *e_mod_split_screen_region_desk_area_get(E_Mod_Split_Screen_Region *emssr);
+
+EINTERN void                       e_mod_split_screen_region_appid_assign(E_Mod_Split_Screen_Region *emssr, const char *appid);
+EINTERN void                       e_mod_split_screen_region_appid_dismiss(E_Mod_Split_Screen_Region *emssr, const char *appid);
+EINTERN Eina_List                 *e_mod_split_screen_region_appid_list_get(E_Mod_Split_Screen_Region *emssr);
+EINTERN Eina_Bool                  e_mod_split_screen_region_ec_assign(E_Mod_Split_Screen_Region *emssr, E_Client *ec);
+EINTERN Eina_Bool                  e_mod_split_screen_region_ec_dismiss(E_Client *ec);
+
+#endif
+