e_mod_rotation_wl: fix infinite loop
[platform/core/uifw/e-mod-tizen-wm-policy.git] / src / rotation / e_mod_rotation_wl.c
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * This file is a modified version of BSD licensed file and
5  * licensed under the Flora License, Version 1.1 (the License);
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://floralicense.org/license/
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an AS IS BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * Please, see the COPYING file for the original copyright owner and
18  * license.
19  */
20 #include "e_mod_main.h"
21 #include "e_mod_rotation.h"
22 #include "e_mod_rotation_wl.h"
23 #include "e_mod_rotation_private.h"
24 #include <wayland-server.h>
25 #include "tizen-policy-ext-server-protocol.h"
26
27 #ifdef HAVE_AUTO_ROTATION
28 #include "e_mod_sensord.h"
29 #endif
30
31 #define TIZEN_ROTATION_ANGLE_TO_INT(angle) ((angle == TIZEN_ROTATION_ANGLE_0) ? 0 :        \
32                                             (angle == TIZEN_ROTATION_ANGLE_90) ? 90 :      \
33                                             (angle == TIZEN_ROTATION_ANGLE_180) ? 180 :    \
34                                             (angle == TIZEN_ROTATION_ANGLE_270) ? 270 : -1)
35
36 #define INT_TO_TIZEN_ROTATION_ANGLE(angle) ((angle == 0) ? TIZEN_ROTATION_ANGLE_0 :        \
37                                             (angle == 90) ? TIZEN_ROTATION_ANGLE_90 :      \
38                                             (angle == 180) ? TIZEN_ROTATION_ANGLE_180 :    \
39                                             (angle == 270) ? TIZEN_ROTATION_ANGLE_270 : TIZEN_ROTATION_ANGLE_NONE)
40
41 typedef struct _Policy_Ext_Rotation Policy_Ext_Rotation;
42 typedef struct _E_Client_Rotation E_Client_Rotation;
43
44 struct _Policy_Ext_Rotation
45 {
46    E_Client *ec;
47    E_Vis_Grab *show_grab;
48    uint32_t available_angles, preferred_angle;
49    enum tizen_rotation_angle cur_angle, prev_angle;
50    Eina_List *rotation_list;
51    Eina_Bool angle_change_done;
52    Eina_Bool wait_update;
53    Eina_Bool hint_fetch;
54    uint32_t serial;
55    Ecore_Timer *wait_update_done_timer;
56
57    /* WORKAROUND for the client used manual rotation */
58    struct
59    {
60       Eina_Bool    use;
61       int          count;
62       Ecore_Timer *timer;
63    } wait_update_pending;
64 };
65
66 struct _E_Client_Rotation
67 {
68    Eina_List     *list;
69    Eina_List     *async_list;
70    Eina_List     *force_update_list;
71    Ecore_Timer   *done_timer;
72    Eina_Bool      screen_lock;
73    Eina_Bool      fetch;
74
75    struct
76    {
77       Eina_Bool    state;
78       E_Zone      *zone;
79    } cancel;
80 };
81
82 /* local subsystem variables */
83 static E_Client_Rotation rot =
84 {
85    NULL,
86    NULL,
87    NULL,
88    NULL,
89    EINA_FALSE,
90    EINA_FALSE,
91    {
92       EINA_FALSE,
93       NULL,
94    }
95 };
96 static Eina_Hash *rot_hash = NULL;
97 static Eina_List *rot_cbs = NULL;
98 static Eina_List *rot_ec_hooks = NULL;
99 static Eina_List *rot_obj_hooks = NULL;
100 static Eina_List *rot_pixmap_hooks = NULL;
101 static Eina_List *wl_hooks = NULL;
102 static Eina_List *pol_vis_hooks = NULL;
103 static Ecore_Idle_Enterer *rot_idle_enterer = NULL;
104 static E_Client *fg_ec = NULL;
105 static struct wl_global *rot_global;
106
107 /* local subsystem functions */
108 static Policy_Ext_Rotation* _policy_ext_rotation_get(E_Client *ec);
109
110 /* local subsystem wayland rotation protocol related functions */
111 static void _e_tizen_rotation_destroy_cb(struct wl_client *client, struct wl_resource *resource);
112 static void _e_tizen_rotation_set_available_angles_cb(struct wl_client *client, struct wl_resource *resource, uint32_t angles);
113 static void _e_tizen_rotation_set_preferred_angle_cb(struct wl_client *client, struct wl_resource *resource, uint32_t angle);
114 static void _e_tizen_rotation_ack_angle_change_cb(struct wl_client *client, struct wl_resource *resource, uint32_t serial);
115 static void _e_tizen_rotation_set_geometry_hint_cb(struct wl_client *client, struct wl_resource *resource, uint32_t angle, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
116 static void _e_tizen_rotation_destroy(struct wl_resource *resource);
117 static void _e_tizen_rotation_send_angle_change(E_Client *ec, int angle);
118 static void _e_tizen_policy_ext_get_rotation_cb(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface);
119 static void _e_tizen_policy_ext_active_angle_cb(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface);
120 static void _e_tizen_policy_ext_bind_cb(struct wl_client *client, void *data, uint32_t version, uint32_t id);
121
122 /* local subsystem wayland rotation protocol related variables */
123 static const struct tizen_rotation_interface _e_tizen_rotation_interface =
124 {
125    _e_tizen_rotation_destroy_cb,
126    _e_tizen_rotation_set_available_angles_cb,
127    _e_tizen_rotation_set_preferred_angle_cb,
128    _e_tizen_rotation_ack_angle_change_cb,
129    _e_tizen_rotation_set_geometry_hint_cb,
130 };
131 static const struct tizen_policy_ext_interface _e_tizen_policy_ext_interface =
132 {
133    _e_tizen_policy_ext_get_rotation_cb,
134    _e_tizen_policy_ext_active_angle_cb,
135 };
136
137 /* local subsystem e_client_rotation related functions */
138 static void      _e_client_rotation_list_remove(E_Client *ec);
139 static Eina_Bool _e_client_rotation_zone_set(E_Zone *zone, E_Client *include_ec, E_Client *exclude_ec);
140 static void      _e_client_rotation_change_done(void);
141 static Eina_Bool _e_client_rotation_change_done_timeout(void *data);
142 static void      _e_client_rotation_change_message_send(E_Client *ec);
143 static int       _e_client_rotation_curr_next_get(const E_Client *ec);
144 static void      _e_client_event_client_rotation_change_begin_send(E_Client *ec);
145 static void      _e_client_event_client_rotation_change_begin_free(void *data, void *ev);
146 static void      _e_client_event_client_rotation_change_cancel_send(E_Client *ec);
147 static void      _e_client_event_client_rotation_change_cancel_free(void *data, void *ev);
148 static void      _e_client_event_client_rotation_change_end_free(void *data, void *ev);
149 static void      _e_client_event_client_rotation_geometry_set_send(E_Client *ec, unsigned int angle, int x, int y, int w, int h);
150 static void      _e_client_event_client_rotation_geometry_set_free(void *data, void *ev);
151 static void      _e_client_rotation_wait_update_clear(E_Client *ec);
152
153 /* local subsystem e_zone_rotation related functions */
154 static void      _e_zone_event_rotation_change_begin_free(void *data, void *ev);
155 static void      _e_zone_event_rotation_change_end_free(void *data, void *ev);
156 static void      _e_zone_event_rotation_change_cancel_free(void *data, void *ev);
157
158 /* e_client_roation functions */
159 static Eina_Bool  e_client_rotation_is_progress(E_Client *ec);
160 static int        e_client_rotation_curr_angle_get(E_Client *ec);
161 static int        e_client_rotation_next_angle_get(E_Client *ec);
162 static Eina_Bool  e_client_rotation_is_available(E_Client *ec, int ang);
163 static Eina_Bool  e_client_rotation_set(E_Client *ec, int rotation);
164 static void       e_client_rotation_change_request(E_Client *ec, int rotation);
165
166 /* e_zone_roation functions */
167 static void       e_zone_rotation_update_done(E_Zone *zone);
168 static void       e_zone_rotation_update_cancel(E_Zone *zone);
169 static void       e_zone_rotation_sub_set(E_Zone *zone, int rotation) EINA_UNUSED;
170
171 /* e_client event, hook, intercept callbacks */
172 static Eina_Bool _rot_cb_zone_rotation_change_begin(void *data EINA_UNUSED, int ev_type EINA_UNUSED, E_Event_Zone_Rotation_Change_Begin *ev);
173 static void      _rot_hook_new_client(void *d EINA_UNUSED, E_Client *ec);
174 static void      _rot_hook_client_del(void *d EINA_UNUSED, E_Client *ec);
175 static void      _rot_hook_eval_end(void *d EINA_UNUSED, E_Client *ec);
176 static void      _rot_hook_eval_fetch(void *d EINA_UNUSED, E_Client *ec);
177 static Eina_Bool _rot_intercept_hook_show_helper(void *d EINA_UNUSED, E_Client *ec);
178 static Eina_Bool _rot_intercept_hook_hide(void *d EINA_UNUSED, E_Client *ec);
179 static Eina_Bool _rot_cb_idle_enterer(void *data EINA_UNUSED);
180
181 static Eina_Bool
182 _browser_check(E_Client *ec)
183 {
184    const char *name = e_client_util_name_get(ec);
185    if (!name) return EINA_FALSE;
186    if (!strncmp(name, "browser", 7)) return EINA_TRUE;
187    return EINA_FALSE;
188 }
189
190 static Eina_Bool
191 _camera_check(E_Client *ec)
192 {
193    const char *name = e_client_util_name_get(ec);
194    if (!name) return EINA_FALSE;
195    if (!strncmp(name, "camera-app", 10)) return EINA_TRUE;
196    if (!strncmp(name, "com.samsung.camera-app-lite", 27)) return EINA_TRUE;
197    return EINA_FALSE;
198 }
199
200 static void
201 _unlock_rot_for_fg_app(E_Zone *zone)
202 {
203    ELOGF("ROTATION", "UNLOCK for app-hint", NULL);
204
205    e_zone_rotation_unblock_type_set(zone,
206                                     E_ZONE_ROT_UNBLOCK_TYPE_APP_HINT,
207                                     "camera",
208                                     EINA_TRUE);
209
210    e_zone_rotation_set(zone, e_mod_sensord_cur_angle_get());
211 }
212
213 static void
214 _try_lock_rot_for_fg_app(E_Zone *zone)
215 {
216    ELOGF("ROTATION", "LOCK for app-hint", NULL);
217
218    if (zone->rot.block.sys_auto_rot)
219      e_zone_rotation_set(zone, 0);
220
221    e_zone_rotation_unblock_type_set(zone,
222                                     E_ZONE_ROT_UNBLOCK_TYPE_APP_HINT,
223                                     "camera",
224                                     EINA_FALSE);
225 }
226
227 static Eina_Bool
228 _no_active_lockscreen_check(E_Client *ec)
229 {
230    E_Client *ec2 = NULL;
231    E_Zone *zone;
232    int x, y, w, h;
233
234    if (!ec) return EINA_FALSE;
235
236    zone = e_comp_zone_find_by_ec(ec);
237    EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
238
239    /* NB: to set fg_ec to lockscreen, return true if given ec is lockscreen itself. */
240    if (e_policy_client_is_lockscreen(ec)) return EINA_TRUE;
241
242    /* bottom to top */
243    for (ec2 = ec; ec2; ec2 = e_client_above_get(ec2))
244      {
245         if (e_object_is_del(E_OBJECT(ec2))) continue;
246         if (e_policy_client_is_lockscreen(ec2))
247           {
248              e_client_geometry_get(ec2, &x, &y, &w, &h);
249              if (E_CONTAINS(zone->x, zone->y, zone->w, zone->h, x, y, w, h))
250                {
251                   /* lockscreen is shown on top of given ec. */
252                   return EINA_FALSE;
253                }
254              break;
255           }
256      }
257
258    /* there is no lockscreen on top of given ec. */
259    return EINA_TRUE;
260 }
261
262 static Eina_Bool
263 _rot_client_check_will_visible(E_Client *ec)
264 {
265    E_Client *above = NULL;
266    Eina_Bool will_visible = EINA_TRUE;
267
268    int ec_x, ec_y, ec_w, ec_h;
269    int above_x, above_y, above_w, above_h;
270
271    ec_x = ec->x;
272    ec_y = ec->y;
273    ec_w = ec->w;
274    ec_h = ec->h;
275    e_client_geometry_get(ec, &ec_x, &ec_y, &ec_w, &ec_h);
276
277    for (above = e_client_above_get(ec); above; above = e_client_above_get(above))
278      {
279         if (e_client_util_ignored_get(above)) continue;
280
281         above_x = above->x;
282         above_y = above->y;
283         above_w = above->w;
284         above_h = above->h;
285         e_client_geometry_get(above, &above_x, &above_y, &above_w, &above_h);
286         if (!E_CONTAINS(above_x, above_y, above_w, above_h, ec_x, ec_y, ec_w, ec_h)) continue;
287
288         if (above->visibility.obscured == E_VISIBILITY_UNOBSCURED)
289           {
290              if (!above->argb)
291                {
292                   will_visible = EINA_FALSE;
293                   break;
294                }
295              else
296                {
297                   if (above->visibility.opaque > 0)
298                     {
299                        will_visible = EINA_FALSE;
300                        break;
301                     }
302                }
303           }
304      }
305
306    return will_visible;
307 }
308
309 static void
310 _rot_client_cb_vis_prepare_foreground(void *data, Evas_Object *obj, void *event_info)
311 {
312    Policy_Ext_Rotation *rot;
313    E_Client *ec;
314    E_Zone *zone;
315
316    rot = data;
317    ec = rot->ec;
318
319    zone = e_comp_zone_find_by_ec(ec);
320    EINA_SAFETY_ON_NULL_RETURN(zone);
321
322    EDBG(ec, "Update Foreground Client '%s'(%p)", ec->icccm.name, ec);
323    if (e_policy_visibility_client_is_activity(ec))
324      {
325         EDBG(ec, "Check ec %x to set fg_ec", e_client_util_win_get(ec));
326         if (_no_active_lockscreen_check(ec))
327           {
328              if (ec->exp_iconify.not_raise)
329                {
330                   if (_rot_client_check_will_visible(ec))
331                     {
332                        EDBG(ec, "Set the fg_ec to %x", e_client_util_win_get(ec));
333                        fg_ec = ec;
334                     }
335                }
336              else
337                {
338                   EDBG(ec, "Set the fg_ec to %x", e_client_util_win_get(ec));
339                   fg_ec = ec;
340                }
341
342              if (_camera_check(ec))
343                _unlock_rot_for_fg_app(zone);
344              else
345                _try_lock_rot_for_fg_app(zone);
346           }
347      }
348
349    _e_client_rotation_zone_set(zone, ec, NULL);
350    if (ec->changes.rotation)
351      {
352         EDBG(ec, "Postpone foreground: ang %d", ec->e.state.rot.ang.next);
353         e_pixmap_image_clear(ec->pixmap, 1);
354         rot->show_grab = e_policy_visibility_client_grab_get(ec, __func__);
355         /* to be invoked 'eval_end' */
356         EC_CHANGED(ec);
357      }
358 }
359
360 static void
361 _policy_ext_rotation_free(void *data)
362 {
363    Policy_Ext_Rotation *rot;
364    E_Client *ec;
365
366    rot = data;
367    ec = rot->ec;
368
369    if (ec->frame)
370      {
371         evas_object_smart_callback_del(ec->frame,
372                                        "e,visibility,prepare,foreground",
373                                        _rot_client_cb_vis_prepare_foreground);
374      }
375
376    free(rot);
377 }
378
379 /* local subsystem functions */
380 static Policy_Ext_Rotation*
381 _policy_ext_rotation_get(E_Client *ec)
382 {
383    Policy_Ext_Rotation *rot;
384
385    EINA_SAFETY_ON_NULL_RETURN_VAL(rot_hash, NULL);
386
387    rot = eina_hash_find(rot_hash, &ec);
388    if (!rot)
389      {
390         rot = E_NEW(Policy_Ext_Rotation, 1);
391         EINA_SAFETY_ON_NULL_RETURN_VAL(rot, NULL);
392
393         rot->ec = ec;
394         rot->angle_change_done = EINA_TRUE;
395         eina_hash_add(rot_hash, &ec, rot);
396         evas_object_smart_callback_add(ec->frame,
397                                        "e,visibility,prepare,foreground",
398                                        _rot_client_cb_vis_prepare_foreground,
399                                        rot);
400      }
401
402    return rot;
403 }
404
405 static void
406 _e_tizen_rotation_destroy_cb(struct wl_client *client EINA_UNUSED,
407                              struct wl_resource *resource)
408 {
409    wl_resource_destroy(resource);
410 }
411
412 static void
413 _e_tizen_rotation_set_available_angles_cb(struct wl_client *client,
414                                           struct wl_resource *resource,
415                                           uint32_t angles)
416 {
417    E_Client *ec;
418    Policy_Ext_Rotation *rot;
419
420    rot = wl_resource_get_user_data(resource);
421    EINA_SAFETY_ON_NULL_RETURN(rot);
422
423    ec = rot->ec;
424    if (!ec)
425      return;
426
427    rot->available_angles = angles;
428
429    ec->e.fetch.rot.available_rots = 1;
430    EC_CHANGED(ec);
431 }
432
433 static void
434 _e_tizen_rotation_set_preferred_angle_cb(struct wl_client *client,
435                                          struct wl_resource *resource,
436                                          uint32_t angle)
437 {
438    Policy_Ext_Rotation *rot;
439    E_Client *ec;
440
441    rot = wl_resource_get_user_data(resource);
442    EINA_SAFETY_ON_NULL_RETURN(rot);
443
444    ec = rot->ec;
445    if (!ec)
446      return;
447
448    ELOGF("ROTATION", "Request to set Preferred angle:%d", ec, TIZEN_ROTATION_ANGLE_TO_INT(angle));
449    if ((angle != TIZEN_ROTATION_ANGLE_NONE) &&
450        (!e_mod_pol_rotation_is_conf_enable_angle(TIZEN_ROTATION_ANGLE_TO_INT(angle))))
451      {
452         ELOGF("ROTATION", "Preferred angle(%d) is not allowed. CONF disabled",
453               ec, TIZEN_ROTATION_ANGLE_TO_INT(angle));
454         return;
455      }
456
457    rot->preferred_angle = angle;
458
459    if (TIZEN_ROTATION_ANGLE_TO_INT(angle) == e_client_rotation_curr_angle_get(ec))
460      {
461         ec->e.state.rot.preferred_rot = TIZEN_ROTATION_ANGLE_TO_INT(angle);
462         EDBG(ec, "preferred angle is same as current angle (%d). don't need to fetch.", ec->e.state.rot.preferred_rot);
463         return;
464      }
465
466    /* for clients supporting portrait mode in floating state*/
467    if ((rot->preferred_angle) &&
468        !(rot->preferred_angle & TIZEN_ROTATION_ANGLE_90) &&
469        !(rot->preferred_angle & TIZEN_ROTATION_ANGLE_270))
470      {
471         enum tizen_rotation_angle tz_angle = 0;
472         uint32_t serial;
473         Eina_List *l;
474         struct wl_resource *res;
475
476         if ((ec->floating) && (ec->comp_data) && ((ec->comp_data->shell.window.w != ec->w) || (ec->comp_data->shell.window.h != ec->h)))
477           {
478              if (rot->preferred_angle & TIZEN_ROTATION_ANGLE_0)
479                tz_angle = TIZEN_ROTATION_ANGLE_0;
480              else if (rot->preferred_angle & TIZEN_ROTATION_ANGLE_180)
481                tz_angle = TIZEN_ROTATION_ANGLE_180;
482              else
483                {
484                   ERR("What's this impossible angle?? : %d", rot->preferred_angle);
485                }
486           }
487
488         if (tz_angle)
489           {
490              serial = wl_display_next_serial(e_comp_wl->wl.disp);
491
492              rot->angle_change_done = EINA_FALSE;
493              rot->prev_angle = rot->cur_angle;
494              rot->cur_angle = tz_angle;
495              rot->serial = serial;
496
497              EINA_LIST_FOREACH(rot->rotation_list, l, res)
498                {
499                   int ver = wl_resource_get_version(res);
500
501                   if ((ver >= 2) && ((tz_angle == TIZEN_ROTATION_ANGLE_0) || (tz_angle == TIZEN_ROTATION_ANGLE_180)))
502                     {
503                        int window_w, window_h;
504                        if (!ec->comp_data) continue;
505                        window_w = ec->comp_data->shell.window.w;
506                        window_h = ec->comp_data->shell.window.h;
507                        EDBG(ec, "Send Change Rotation: angle %d for rendering preparation of portrait mode in floating w/h:%dx%d, shell.w/h:%dx%d, Resize:%dx%d -> %dx%d",
508                             tz_angle, ec->w, ec->h, window_w, window_h, window_w, window_h, window_h, window_w);
509                        tizen_rotation_send_angle_change_with_resize(res, tz_angle, serial, window_h, window_w);
510
511                        ec->e.state.rot.preferred_rot = TIZEN_ROTATION_ANGLE_TO_INT(angle);
512                        return;
513                     }
514                }
515           }
516      }
517
518    ec->e.fetch.rot.preferred_rot = 1;
519    EC_CHANGED(ec);
520 }
521
522 static Eina_Bool
523 _wait_update_done_timer_cb(void *data)
524 {
525    Policy_Ext_Rotation *rot;
526    E_Client *ec;
527
528    rot = (Policy_Ext_Rotation *)data;
529    if (!rot) return ECORE_CALLBACK_CANCEL;
530
531    rot->wait_update_done_timer = NULL;
532
533    ec = rot->ec;
534    if (ec)
535      {
536         if (!e_object_is_del(E_OBJECT(ec)))
537           {
538              EINF(ec, "Rotation Timeout... Force update rotation");
539              _e_client_rotation_wait_update_clear(ec);
540           }
541      }
542
543    return ECORE_CALLBACK_CANCEL;
544 }
545
546 static void
547 _remove_rotation_wait_update_done_timer(Policy_Ext_Rotation *rot)
548 {
549    if (!rot) return;
550
551    if (rot->wait_update_done_timer)
552      {
553         ecore_timer_del(rot->wait_update_done_timer);
554         rot->wait_update_done_timer = NULL;
555      }
556 }
557
558 static void
559 _create_rotation_wait_update_done_timer(Policy_Ext_Rotation *rot)
560 {
561    if (!rot) return;
562
563    _remove_rotation_wait_update_done_timer(rot);
564
565    rot->wait_update_done_timer = ecore_timer_add(5.0f, _wait_update_done_timer_cb, rot);
566 }
567
568 static void
569 _e_tizen_rotation_angle_change(Policy_Ext_Rotation *rot)
570 {
571    E_Client *ec;
572
573    if (!rot) return;
574    if (!rot->ec) return;
575    if (rot->angle_change_done) return;
576
577    ec = rot->ec;
578
579    ec->e.state.rot.ang.prev = ec->e.state.rot.ang.curr;
580    ec->e.state.rot.ang.curr = TIZEN_ROTATION_ANGLE_TO_INT(rot->cur_angle);
581
582    if (TIZEN_ROTATION_ANGLE_TO_INT(rot->cur_angle) == ec->e.state.rot.ang.next)
583      {
584         ec->e.state.rot.ang.next = -1;
585
586         if (ec->e.state.rot.ang.reserve != -1)
587           {
588              _e_client_rotation_list_remove(ec);
589
590              e_client_rotation_set(ec, ec->e.state.rot.ang.reserve);
591              ec->e.state.rot.ang.reserve = -1;
592           }
593         else
594           {
595              /* it means that we need the next buffer commit after rotation ack event.
596               * once we get next buffer commit, policy module attempts to remove
597               * this ec from rotation_list_remove. and then, rotation process is finished.
598               */
599              rot->wait_update = EINA_TRUE;
600
601              /* WORKAROUND
602               *
603               * Until now, we have assumed that the buffer commit event after the rotation
604               * ack event informs us of completion of rotation. However this assumption could
605               * be wrong because the rotation ack event is dispatched by main thread of client,
606               * whereas the buffer commit event could be dispatched by other thread.
607               *
608               * The ideal solution for resolving this issue is introduction of same protocol
609               * serial number between the rotation ack event and buffer commit event. But this
610               * approach needs much of changes for EFL, E20 and DDK. Thus I have added workaround
611               * code for quick resolving this.
612               *
613               * We have to extend EFL, E20 and DDK to support this later.
614               */
615              if (_browser_check(ec))
616                {
617                   if (rot->wait_update_pending.timer)
618                     ecore_timer_del(rot->wait_update_pending.timer);
619
620                   rot->wait_update_pending.timer = NULL;
621                   rot->wait_update_pending.use = EINA_TRUE;
622                   rot->wait_update_pending.count = 2;
623                }
624           }
625      }
626
627    // check angle change serial
628    rot->angle_change_done = EINA_TRUE;
629 }
630
631 static void
632 _e_tizen_rotation_ack_angle_change_cb(struct wl_client *client,
633                                       struct wl_resource *resource,
634                                       uint32_t serial)
635 {
636    E_Client *ec;
637    Policy_Ext_Rotation *rot;
638    E_Zone *zone;
639
640    rot = wl_resource_get_user_data(resource);
641    EINA_SAFETY_ON_NULL_RETURN(rot);
642
643    ec = rot->ec;
644    if (!ec) return;
645
646    EDBG(ec, "Rotation Done: prev %d cur %d serial %u",
647         ec->e.state.rot.ang.curr,
648         TIZEN_ROTATION_ANGLE_TO_INT(rot->cur_angle),
649         serial);
650
651    zone = e_comp_zone_find_by_ec(ec);
652    EINA_SAFETY_ON_NULL_RETURN(zone);
653
654    if ((serial != 0) && (rot->serial == serial)) // rotation success
655      {
656         if (rot->angle_change_done)
657           {
658              WRN("Rotation Zone Set: Rotation Done(fail case): %s(%p) Already received change_done for this serial:%u",
659                  ec->icccm.name ? ec->icccm.name : "", ec, serial);
660              return;
661           }
662
663         _e_tizen_rotation_angle_change(rot);
664      }
665    else // rotation fail
666      {
667         WRN("Rotation Zone Set: Rotation Done(fail case): %s(%p) Not matched serial %u != %u",
668             ec->icccm.name ? ec->icccm.name : "", ec, rot->serial, serial);
669
670         _e_client_rotation_zone_set(zone, ec, NULL);
671      }
672
673    // check angle change serial
674    rot->angle_change_done = EINA_TRUE;
675 }
676
677 static void
678 _e_tizen_rotation_set_geometry_hint_cb(struct wl_client *client,
679                                       struct wl_resource *resource,
680                                       uint32_t tz_angle,
681                                       uint32_t x, uint32_t y, uint32_t w, uint32_t h)
682 {
683    E_Client *ec;
684    Policy_Ext_Rotation *rot;
685    int i = -1;
686    int angle = 0;
687
688    rot = wl_resource_get_user_data(resource);
689    EINA_SAFETY_ON_NULL_RETURN(rot);
690
691    ec = rot->ec;
692    if (!ec)
693      return;
694
695    switch (tz_angle)
696      {
697         case TIZEN_ROTATION_ANGLE_0:
698           i = 0;
699           angle = 0;
700           break;
701         case TIZEN_ROTATION_ANGLE_90:
702           i = 1;
703           angle = 90;
704           break;
705         case TIZEN_ROTATION_ANGLE_180:
706           i = 2;
707           angle = 180;
708           break;
709         case TIZEN_ROTATION_ANGLE_270:
710           i = 3;
711           angle = 270;
712           break;
713         default:
714           return;
715      }
716
717    ec->e.state.rot.geom[i].x = x;
718    ec->e.state.rot.geom[i].y = y;
719    ec->e.state.rot.geom[i].w = w;
720    ec->e.state.rot.geom[i].h = h;
721
722    e_policy_hook_call(E_POLICY_HOOK_CLIENT_ROTATION_GEOMETRY_SET, ec);
723    _e_client_event_client_rotation_geometry_set_send(ec, angle, x, y, w, h);
724 }
725
726 static void
727 _e_tizen_rotation_destroy(struct wl_resource *resource)
728 {
729    Policy_Ext_Rotation *ext_rot;
730
731    ext_rot = wl_resource_get_user_data(resource);
732    EINA_SAFETY_ON_NULL_RETURN(ext_rot);
733
734    ext_rot->rotation_list = eina_list_remove(ext_rot->rotation_list, resource);
735
736    /* if there's no connected client of tizen_rotation */
737    if (!ext_rot->rotation_list)
738      {
739         _e_client_rotation_list_remove(ext_rot->ec);
740         if (rot.async_list) rot.async_list = eina_list_remove(rot.async_list, ext_rot->ec);
741
742         ext_rot->angle_change_done = EINA_TRUE;
743      }
744 }
745
746 static void
747 _e_tizen_policy_ext_get_rotation_cb(struct wl_client *client,
748                                     struct wl_resource *resource,
749                                     uint32_t id,
750                                     struct wl_resource *surface)
751 {
752    int version = wl_resource_get_version(resource); // resource is tizen_policy_ext resource
753    struct wl_resource *res;
754    E_Client *ec;
755    Policy_Ext_Rotation *rot;
756
757    ec = e_client_from_surface_resource(surface);
758    EINA_SAFETY_ON_NULL_RETURN(ec);
759
760    // Add rotation info
761    rot = _policy_ext_rotation_get(ec);
762    EINA_SAFETY_ON_NULL_RETURN(rot);
763
764    res = wl_resource_create(client, &tizen_rotation_interface, version, id);
765    if (res == NULL)
766      {
767         wl_client_post_no_memory(client);
768         return;
769      }
770
771    rot->rotation_list = eina_list_append(rot->rotation_list, res);
772
773    wl_resource_set_implementation(res, &_e_tizen_rotation_interface,
774                                   rot, _e_tizen_rotation_destroy);
775
776    ec->e.fetch.rot.support = 1;
777    EC_CHANGED(ec);
778 }
779
780 static void
781 _e_tizen_policy_ext_active_angle_cb(struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface)
782 {
783    int angle;
784    E_Zone *zone;
785    E_Client *ec;
786    E_Client *focused_ec;
787
788    if (!surface)
789      zone = e_zone_current_get();
790    else
791      {
792         ec = e_client_from_surface_resource(surface);
793         if (ec)
794           zone = e_comp_zone_find_by_ec(ec);
795         else
796           zone = e_zone_current_get();
797      }
798
799    if (!zone)
800      angle = -1;
801    else
802      {
803         focused_ec = e_client_focused_get();
804         if (!focused_ec)
805           angle = zone->rot.curr;
806         else
807           angle = focused_ec->e.state.rot.ang.curr;
808      }
809
810    tizen_policy_ext_send_active_angle(resource, angle);
811 }
812
813 static void
814 _e_tizen_policy_ext_bind_cb(struct wl_client *client, void *data, uint32_t version, uint32_t id)
815 {
816    struct wl_resource *res;
817
818    if (!(res = wl_resource_create(client, &tizen_policy_ext_interface, version, id)))
819      {
820         ERR("Could not create scaler resource: %m");
821         wl_client_post_no_memory(client);
822         return;
823      }
824
825    wl_resource_set_implementation(res, &_e_tizen_policy_ext_interface, NULL, NULL);
826 }
827
828 static Eina_Bool
829 _rot_eval_fetch_preferred_send_angle_change(Policy_Ext_Rotation *rot)
830 {
831    E_Client *ec;
832
833    EINA_SAFETY_ON_NULL_RETURN_VAL(rot, EINA_FALSE);
834
835    ec = rot->ec;
836    if (!ec)
837      return EINA_FALSE;
838
839    if (!rot->preferred_angle)
840      return EINA_FALSE;
841
842    /* for clients supporting landscape mode only */
843    if (!(rot->preferred_angle & TIZEN_ROTATION_ANGLE_0) &&
844        !(rot->preferred_angle & TIZEN_ROTATION_ANGLE_180))
845      {
846         enum tizen_rotation_angle tz_angle = 0;
847         uint32_t serial;
848         Eina_List *l;
849         struct wl_resource *res;
850
851         if (rot->preferred_angle & TIZEN_ROTATION_ANGLE_90)
852           tz_angle = TIZEN_ROTATION_ANGLE_90;
853         else if (rot->preferred_angle & TIZEN_ROTATION_ANGLE_270)
854           tz_angle = TIZEN_ROTATION_ANGLE_270;
855         else
856           {
857              ERR("What's this impossible angle?? : %d", rot->preferred_angle);
858           }
859
860         if (tz_angle)
861           {
862              serial = wl_display_next_serial(e_comp_wl->wl.disp);
863
864              rot->angle_change_done = EINA_FALSE;
865              rot->prev_angle = rot->cur_angle;
866              rot->cur_angle = tz_angle;
867              rot->serial = serial;
868
869              EDBG(ec, "Send Change Rotation: angle %d for rendering preparation of landscape mode only app. mapped:%d serial:%u",
870                   tz_angle, ec->first_mapped, serial);
871
872              ec->e.state.rot.ang.next = TIZEN_ROTATION_ANGLE_TO_INT(tz_angle);
873
874              EINA_LIST_FOREACH(rot->rotation_list, l, res)
875                {
876                   tizen_rotation_send_angle_change(res, tz_angle, serial);
877                }
878              return EINA_TRUE;
879           }
880      }
881
882    return EINA_FALSE;
883 }
884
885 static Eina_Bool
886 _rot_eval_fetch_available_send_angle_change(Policy_Ext_Rotation *rot)
887 {
888    enum tizen_rotation_angle cur_tz_angle;
889    E_Client *ec;
890
891    EINA_SAFETY_ON_NULL_RETURN_VAL(rot, EINA_FALSE);
892
893    ec = rot->ec;
894    if (!ec)
895      return EINA_FALSE;
896
897    if (!rot->available_angles)
898      return EINA_FALSE;
899
900    cur_tz_angle = INT_TO_TIZEN_ROTATION_ANGLE(ec->e.state.rot.ang.curr);
901    if (rot->available_angles & cur_tz_angle)
902      {
903         EDBG(ec, "Ignore it. current angle(%d) is one of available angles.", cur_tz_angle);
904         return EINA_FALSE;
905      }
906
907    /* for clients supporting landscape mode only */
908    if (!(rot->available_angles & TIZEN_ROTATION_ANGLE_0) &&
909        !(rot->available_angles & TIZEN_ROTATION_ANGLE_180))
910      {
911         enum tizen_rotation_angle tz_angle = 0, zone_tz_angle = 0;
912         uint32_t serial;
913         Eina_List *l;
914         struct wl_resource *res;
915         int ang_zone = 0;
916         E_Zone *zone;
917
918         zone = e_comp_zone_find_by_ec(ec);
919         if (zone)
920           {
921              if (zone->rot.unknown_state)
922                ang_zone = zone->rot.act;
923              else
924                ang_zone = zone->rot.curr;
925
926              zone_tz_angle = INT_TO_TIZEN_ROTATION_ANGLE(ang_zone);
927           }
928
929         if (rot->available_angles & zone_tz_angle)
930           tz_angle = zone_tz_angle;
931         else if (rot->available_angles & TIZEN_ROTATION_ANGLE_90)
932           tz_angle = TIZEN_ROTATION_ANGLE_90;
933         else if (rot->available_angles & TIZEN_ROTATION_ANGLE_270)
934           tz_angle = TIZEN_ROTATION_ANGLE_270;
935         else
936           {
937              ERR("What's this impossible angle?? : %d", rot->available_angles);
938           }
939
940         if (tz_angle)
941           {
942              /* if the client requests a window rotation with the same value as the current angle, just ignore it. */
943              if ((ec->first_mapped) &&
944                  (ec->e.state.rot.ang.curr == TIZEN_ROTATION_ANGLE_TO_INT(tz_angle)))
945                {
946                   EDBG(ec, "Ignore it. given angle %d is same as the current angle for landscape only app.",
947                        tz_angle);
948                   return EINA_FALSE;
949                }
950
951              serial = wl_display_next_serial(e_comp_wl->wl.disp);
952
953              rot->angle_change_done = EINA_FALSE;
954              rot->prev_angle = rot->cur_angle;
955              rot->cur_angle = tz_angle;
956              rot->serial = serial;
957
958              EDBG(ec, "Send Change Rotation: angle %d for redering preparation of landscape only app. mapped:%d serial:%u",
959                   tz_angle, ec->first_mapped, serial);
960
961              ec->e.state.rot.ang.next = TIZEN_ROTATION_ANGLE_TO_INT(tz_angle);
962
963              EINA_LIST_FOREACH(rot->rotation_list, l, res)
964                {
965                   tizen_rotation_send_angle_change(res, tz_angle, serial);
966                }
967              return EINA_TRUE;
968           }
969      }
970    return EINA_FALSE;
971 }
972
973 static Eina_Bool
974 _e_client_rotation_swap_check(E_Client *ec)
975 {
976    // Check whether the width and height of ec are swapped or not.
977    // If you want to make your swap policy, then you can modify this function.
978
979    E_Zone *zone;
980
981    if (!e_config->wm_win_rotation_swap_size)
982      return EINA_FALSE;
983
984    int x, y, w, h;
985
986    if (e_policy_client_is_keyboard(ec) ||
987        e_policy_client_is_keyboard_sub(ec))
988      return EINA_FALSE;
989
990    zone = e_comp_zone_find_by_ec(ec);
991    EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
992
993    e_client_geometry_get(ec, &x, &y, &w, &h);
994    if ((x != zone->x) ||
995        (y != zone->y) ||
996        (w != zone->w) ||
997        (h != zone->h))
998      return EINA_TRUE;
999
1000    return EINA_FALSE;
1001 }
1002
1003 static void
1004 _e_tizen_rotation_send_angle_change(E_Client *ec, int angle)
1005 {
1006    Eina_List *l;
1007    Policy_Ext_Rotation *rot;
1008    uint32_t serial;
1009    struct wl_resource *resource;
1010    enum tizen_rotation_angle tz_angle = TIZEN_ROTATION_ANGLE_0;
1011    int ver;
1012    int rot_diff;
1013
1014    EINA_SAFETY_ON_NULL_RETURN(ec);
1015    EINA_SAFETY_ON_NULL_RETURN(rot_hash);
1016
1017    rot_diff = ec->e.state.rot.ang.curr - angle;
1018    if (rot_diff < 0) rot_diff = -rot_diff;
1019
1020    rot = eina_hash_find(rot_hash, &ec);
1021    if (!rot) return;
1022
1023    switch (angle)
1024      {
1025         case 0:
1026           tz_angle = TIZEN_ROTATION_ANGLE_0;
1027           break;
1028         case 90:
1029           tz_angle = TIZEN_ROTATION_ANGLE_90;
1030           break;
1031         case 180:
1032           tz_angle = TIZEN_ROTATION_ANGLE_180;
1033           break;
1034         case 270:
1035           tz_angle = TIZEN_ROTATION_ANGLE_270;
1036           break;
1037         default:
1038           break;
1039      }
1040
1041    serial = wl_display_next_serial(e_comp_wl->wl.disp);
1042
1043    rot->angle_change_done = EINA_FALSE;
1044    rot->prev_angle = rot->cur_angle;
1045    rot->cur_angle = tz_angle;
1046    rot->serial = serial;
1047
1048    EINA_LIST_FOREACH(rot->rotation_list, l, resource)
1049      {
1050         ver = wl_resource_get_version(resource); // resource is type of tizen_rotation_interface
1051
1052         Eina_Bool need_swap = EINA_FALSE;
1053         need_swap = _e_client_rotation_swap_check(ec);
1054
1055         if ((ver >= 2) && (need_swap))
1056           {
1057              if (rot_diff == 180)
1058                {
1059                   EDBG(ec, "Send Change Rotation: angle %d mapped:%d serial:%u Resize:%dx%d",
1060                        tz_angle, ec->first_mapped, serial, ec->w, ec->h);
1061
1062                   tizen_rotation_send_angle_change_with_resize(resource, tz_angle, serial, ec->w, ec->h);
1063                }
1064              else
1065                {
1066                   EDBG(ec, "Send Change Rotation: angle %d mapped:%d serial:%u Resize:%dx%d -> %dx%d",
1067                        tz_angle, ec->first_mapped, serial, ec->w, ec->h, ec->h, ec->w);
1068
1069                   tizen_rotation_send_angle_change_with_resize(resource, tz_angle, serial, ec->h, ec->w);
1070                }
1071           }
1072         else
1073           {
1074              EDBG(ec, "Send Change Rotation: angle %d mapped:%d serial:%u",
1075                   tz_angle, ec->first_mapped, serial);
1076
1077              tizen_rotation_send_angle_change(resource, tz_angle, serial);
1078           }
1079      }
1080 }
1081
1082 /* local subsystem e_client_rotation related functions */
1083 static void
1084 _e_client_rotation_list_remove(E_Client *ec)
1085 {
1086    E_Event_Client_Rotation_Change_End *ev = NULL;
1087    Eina_Bool found = EINA_FALSE;
1088    E_Zone *zone;
1089
1090    zone = e_comp_zone_find_by_ec(ec);
1091    EINA_SAFETY_ON_NULL_RETURN(zone);
1092
1093    if (eina_list_data_find(rot.list, ec) == ec)
1094      {
1095         found = EINA_TRUE;
1096         rot.list = eina_list_remove(rot.list, ec);
1097      }
1098
1099    if (ec->e.state.rot.wait_for_done)
1100      {
1101         ec->e.state.rot.wait_for_done = 0;
1102
1103         /* if we make the e_client event in the _e_client_free function,
1104          * then we may meet a crash problem, only work this at least e_client_hide.
1105          */
1106         if (!e_object_is_del(E_OBJECT(ec)))
1107           {
1108              ev = E_NEW(E_Event_Client_Rotation_Change_End, 1);
1109              if (ev)
1110                {
1111                   ev->ec = ec;
1112                   e_object_ref(E_OBJECT(ec));
1113                   EDBG(ec, "Rotation Event: Client Rotation END");
1114                   ecore_event_add(E_EVENT_CLIENT_ROTATION_CHANGE_END,
1115                                   ev,
1116                                   _e_client_event_client_rotation_change_end_free,
1117                                   NULL);
1118                }
1119           }
1120
1121         if ((found) &&
1122             (eina_list_count(rot.list) == 0))
1123           {
1124              _e_client_rotation_change_done();
1125           }
1126         /* handling pending zone rotation job */
1127         else if ((eina_list_count(rot.list) == 0) &&
1128                  (zone->rot.wait_for_done) &&
1129                  (zone->rot.pending))
1130           {
1131              e_zone_rotation_update_done(zone);
1132           }
1133      }
1134    ec->e.state.rot.ang.next = -1;
1135    ec->changes.rotation = 0;
1136 }
1137
1138 static Eina_Bool
1139 _e_client_rotation_angle_is_allowed(E_Client *ec, int angle)
1140 {
1141    if ((!_camera_check(ec)) &&
1142        (!e_mod_pol_rotation_is_conf_enable_angle(angle)))
1143      {
1144         EDBG(ec, "CHECK dependent '%s'(%p) ang:%d rot.type:%d dependent:%d",
1145              ec->icccm.name, ec, angle,
1146              ec->e.state.rot.type,
1147              E_CLIENT_ROTATION_TYPE_DEPENDENT);
1148
1149         /* check whether the window is 32bit and has the dependent rotation type.
1150         * if true, then it can be rotated according to below normal window
1151         * even if current zone angle is not allowed by configuration value.
1152         */
1153         if (!((ec->argb) && (e_client_rotation_is_available(ec, angle))))
1154           return EINA_FALSE;
1155      }
1156    return EINA_TRUE;
1157 }
1158
1159 static Eina_Bool
1160 _e_client_rotation_check_ec_size_and_type(E_Zone *zone, E_Client *ec)
1161 {
1162    int ec_x, ec_y, ec_w, ec_h;
1163
1164    if (!zone) return EINA_FALSE;
1165    if (!ec) return EINA_FALSE;
1166
1167    ec_x = ec->x;
1168    ec_y = ec->y;
1169    ec_w = ec->w;
1170    ec_h = ec->h;
1171
1172    e_client_geometry_get(ec, &ec_x, &ec_y, &ec_w, &ec_h);
1173
1174    if ((ec_x == zone->x) && (ec_y == zone->y) &&
1175        (ec_w == zone->w) && (ec_h == zone->h) &&
1176        (ec->e.state.rot.type == E_CLIENT_ROTATION_TYPE_NORMAL))
1177      {
1178         return EINA_TRUE;
1179      }
1180
1181    return EINA_FALSE;
1182 }
1183
1184 /* TODO need to optimize */
1185 static Eina_Bool
1186 _e_client_rotation_zone_set(E_Zone *zone, E_Client *include_ec, E_Client *exclude_ec)
1187 {
1188    E_Client *ec, *bg_ec = NULL;
1189    Eina_List *target_list = NULL, *l, *ll;
1190    int angle, cur_angle, top_win_angle;
1191    Eina_Bool can_rotate = EINA_TRUE, ret = EINA_FALSE;
1192
1193    TRACE_DS_BEGIN(CLIENT ROTATION ZONE SET);
1194
1195    if (zone->rot.unknown_state)
1196      cur_angle = zone->rot.act;
1197    else
1198      cur_angle = zone->rot.curr;
1199
1200    top_win_angle = -1;
1201
1202    DBG("<<< Try to set zone rotation | fg_ec '%s'(win:%x, ec:%p) cur_angle:%d zone(unknown_state:%d act:%d curr:%d)",
1203        fg_ec ? fg_ec->icccm.name : "",
1204        e_client_util_win_get(fg_ec),
1205        fg_ec,
1206        cur_angle,
1207        zone->rot.unknown_state,
1208        zone->rot.act,
1209        zone->rot.curr);
1210
1211    E_CLIENT_REVERSE_FOREACH(ec)
1212      {
1213         if (zone != e_comp_zone_find_by_ec(ec)) continue;
1214         if (ec == exclude_ec) continue;
1215         if ((ec != include_ec) && (!evas_object_visible_get(ec->frame))) continue;
1216         if (e_object_is_del(E_OBJECT(ec))) continue;
1217         if (e_client_util_ignored_get(ec)) continue;
1218         if (evas_object_data_get(ec->frame, "comp_skip")) continue;
1219         if ((ec->comp_data) && (ec->comp_data->sub.data)) continue;
1220         if (!e_util_strcmp("wl_pointer-cursor", ec->icccm.window_role))
1221           {
1222              e_client_cursor_map_apply(ec, cur_angle, ec->x, ec->y);
1223              continue;
1224           }
1225
1226         if (!fg_ec)
1227           {
1228              if ((!bg_ec) ||
1229                  ((include_ec == ec) &&
1230                   (evas_object_layer_get(ec->frame) >= evas_object_layer_get(bg_ec->frame))))
1231                {
1232                   if (_e_client_rotation_angle_is_allowed(ec, cur_angle))
1233                     {
1234                        EDBG(ec, "Append to rotation target list");
1235                        target_list = eina_list_append(target_list, ec);
1236                     }
1237                }
1238              else
1239                {
1240                   e_client_rotation_set(ec, cur_angle);
1241                   continue;
1242                }
1243
1244              if (_e_client_rotation_check_ec_size_and_type(zone, ec))
1245                {
1246                   if (!ec->argb)
1247                     {
1248                        EDBG(ec, "Found Topmost Fullscreen Window");
1249                        bg_ec = ec;
1250                     }
1251                   else
1252                     {
1253                        if ((ec->visibility.opaque > 0) &&
1254                            (!ec->parent))
1255                          {
1256                             EDBG(ec, "Found Topmost Fullscreen Window");
1257                             bg_ec = ec;
1258                          }
1259                     }
1260                }
1261           }
1262         else
1263           {
1264              ELOGF("ROTATION", "   fg_ec:%x, bg_ec:%p CHECK %s(%p) parent:%p vis:%d argb:%d opaque:%d",
1265                    ec, e_client_util_win_get(fg_ec), bg_ec, ec->icccm.name, ec, ec->parent,
1266                    ec->visibility.obscured, ec->argb, ec->visibility.opaque);
1267              /* if already found background client,
1268               * that means this client is placed under background client */
1269              if (bg_ec)
1270                {
1271                   /* if this client don't have parent, rotate */
1272                   if (!ec->parent)
1273                     {
1274                        /* 1. rotate window only if auto-rotation is enabled.
1275                         * it can show wrong rotated window if we don't check auto-rot value.
1276                         *
1277                         * assuming that auto-rotation is disabled and window A is 0 degree.
1278                         * in this case, when we attempt to run the camera-app which can be
1279                         * always rotated, window A is changed to 270 degrees before showing
1280                         * camera-app window. to prevent this we should check whether auto
1281                         * rotation is enabled.
1282                         *
1283                         * 2. also need to check this ec is visible before rotating it.
1284                         *
1285                         * e.g. camera -> image-viewer launching case
1286                         *
1287                         *  image-viewer: bg_ec is set to image-viewer ec at the previous phase of this loop
1288                         *  ec: camera
1289                         *
1290                         *  if we decide to rotate camera ec which is obscured by image-viewer window,
1291                         *  then camera app will not send rotation done event. thus it occurrs rotation time
1292                         *  out error. to resolve this issue we should exclude obscured camera ec from
1293                         *  rotation list.
1294                         *
1295                         * 2-1. Even if the ec is obscured by above, the ec can rotate if the ec is uniconic state.
1296                         *
1297                         * e.g. fully obscured by 32bit opaque window, or parent window that obscured by their child.
1298                         */
1299                        if ((!zone->rot.block.sys_auto_rot) &&
1300                            (e_mod_pol_rotation_is_conf_enable_angle(cur_angle)) &&
1301                            (ec->comp_data && ec->comp_data->mapped) &&
1302                            ((ec->visibility.obscured != E_VISIBILITY_FULLY_OBSCURED) || !ec->iconic))
1303                          {
1304                             ELOGF("ROTATION", "Do rotation of below ec under bg_ec %s(%p)",
1305                                   NULL, ec->icccm.name, ec);
1306
1307                             e_client_rotation_set(ec, cur_angle);
1308                          }
1309                     }
1310                   continue;
1311                }
1312              /*
1313               * activity clients placed above background client should not be
1314               * rotated. that's because this is deactivated sometime later by
1315               * visibility's deiconify rendering logic.
1316               */
1317              else if ((fg_ec != ec) && (include_ec != ec) &&
1318                       _e_client_rotation_check_ec_size_and_type(zone, ec))
1319                {
1320                   if (!ec->argb)
1321                     continue;
1322                   else
1323                     {
1324                        if ((ec->visibility.opaque > 0) &&
1325                            (!ec->parent))
1326                          continue;
1327                     }
1328                }
1329
1330              if (_e_client_rotation_angle_is_allowed(ec, cur_angle))
1331                {
1332                   EDBG(ec, "Append Rotation List '%s'(%p)", ec->icccm.name, ec);
1333                   target_list = eina_list_append(target_list, ec);
1334                }
1335              if (ec == fg_ec)
1336                bg_ec = ec;
1337           }
1338      }
1339
1340    angle = cur_angle;
1341
1342    if (!target_list || (target_list && eina_list_count(target_list) == 0))
1343      {
1344         DBG("Failed to set rotation with zone: target_list is empty. angle: %d", angle);
1345         if (fg_ec)
1346           angle = _e_client_rotation_curr_next_get(fg_ec);
1347         can_rotate = EINA_FALSE;
1348      }
1349
1350    EINA_LIST_FOREACH(target_list, l, ec)
1351      {
1352         if (!e_client_rotation_is_available(ec, angle))
1353           {
1354              EDBG(ec, "Failed to set rotation with zone: not able to rotate given angle %d", angle);
1355              angle = _e_client_rotation_curr_next_get(ec);
1356              can_rotate = EINA_FALSE;
1357              break;
1358           }
1359         else
1360           {
1361              if (top_win_angle == -1)
1362                {
1363                   if (e_policy_client_is_keyboard(ec) ||
1364                       e_policy_client_is_keyboard_sub(ec))
1365                     continue;
1366
1367                   EDBG(ec, "Set top_win_angle: %d", angle);
1368                   top_win_angle = angle;
1369                }
1370           }
1371      }
1372
1373    if (!can_rotate)
1374      {
1375         can_rotate = EINA_TRUE;
1376         EINA_LIST_FOREACH(target_list, l, ec)
1377           {
1378              if (!e_client_rotation_is_available(ec, angle))
1379                {
1380                   EDBG(ec, "Failed to set with exist client: not able to rotate given angle %d", angle);
1381                   can_rotate = EINA_FALSE;
1382                   break;
1383                }
1384              else
1385                {
1386                   if (top_win_angle == -1)
1387                     {
1388                        if (e_policy_client_is_keyboard(ec) ||
1389                            e_policy_client_is_keyboard_sub(ec))
1390                          continue;
1391
1392                        EDBG(ec, "Set top_win_angle: %d", angle);
1393                        top_win_angle = angle;
1394                     }
1395                }
1396           }
1397      }
1398    else
1399      {
1400         DBG("Set rotation of zone according to angle of zone: %d", angle);
1401         goto do_rotate;
1402      }
1403
1404    if (!can_rotate)
1405      {
1406         /* support more than one client with fixed angle value */
1407         if (eina_list_count(target_list) > 0)
1408           {
1409              Eina_Bool res;
1410              int i, ang;
1411
1412              if (top_win_angle == -1)
1413                {
1414                   EINA_LIST_FOREACH_SAFE(target_list, l, ll, ec)
1415                     {
1416                        DBG("Find top most window angle");
1417                        // 1. check preferred
1418                        if (ec->e.state.rot.preferred_rot != -1)
1419                          {
1420                             EDBG(ec, "Set top_win_angle(preferred angle): %d", ec->e.state.rot.preferred_rot);
1421                             top_win_angle = ec->e.state.rot.preferred_rot;
1422                             break;
1423                          }
1424
1425                        // 2. check available angle
1426                        for (i = 0; i < 4; i++)
1427                          {
1428                             ang = (cur_angle + (i * 90)) % 360;
1429                             if (e_client_rotation_is_available(ec, ang))
1430                               {
1431                                  EDBG(ec, "Set top_win_angle: %d", ang);
1432                                  top_win_angle = ang;
1433                                  break;
1434                               }
1435                          }
1436
1437                        if (top_win_angle == -1)
1438                          {
1439                             // 3. couldn't find available angle
1440                             EDBG(ec, "Cannot find any available angle. Set top_win_angle to 0");
1441                             top_win_angle = 0;
1442                          }
1443
1444                        break;
1445                     }
1446                }
1447
1448              angle = top_win_angle;
1449
1450              EINA_LIST_FOREACH_SAFE(target_list, l, ll, ec)
1451                {
1452                   EDBG(ec, "Attempt to rotate client with given angle %d", top_win_angle);
1453                   if (e_client_rotation_is_available(ec, top_win_angle))
1454                     {
1455                        res = e_client_rotation_set(ec, top_win_angle);
1456                        if (!res) ret = EINA_FALSE;
1457                     }
1458                   else
1459                     {
1460                        EDBG(ec, "Failed to set given angle %d, Keep current angle %d", top_win_angle, ec->e.state.rot.ang.curr);
1461                     }
1462                }
1463
1464              EINA_LIST_FOREACH(rot.force_update_list, l, ec)
1465                {
1466                   if (!eina_list_data_find(target_list, ec))
1467                     {
1468                        EDBG(ec, "Rotate ec of force_update_list '%s'(%p)", ec->icccm.name, ec);
1469                        res = e_client_rotation_set(ec, top_win_angle);
1470                        if (!res) ret = EINA_FALSE;
1471                     }
1472                }
1473           }
1474
1475         goto end;
1476      }
1477    else
1478      {
1479         DBG("Set rotation of zone according to angle of exist clients: %d", angle);
1480         goto do_rotate;
1481      }
1482
1483 do_rotate:
1484    EINA_LIST_FOREACH(rot.force_update_list, l, ec)
1485      {
1486         if (!eina_list_data_find(target_list, ec))
1487           {
1488              EDBG(ec, "Append Rotation List from force_update_list '%s'(%p)", ec->icccm.name, ec);
1489              target_list = eina_list_append(target_list, ec);
1490           }
1491      }
1492
1493    EINA_LIST_FOREACH(target_list, l, ec)
1494       ret = e_client_rotation_set(ec, angle);
1495
1496 end:
1497    if (target_list)
1498      eina_list_free(target_list);
1499
1500    zone->rot.act = angle;
1501
1502    TRACE_DS_END();
1503
1504    ELOGF("ROTATION", "zone active angle %d", NULL, zone->rot.act);
1505    DBG("End to set zone rotation: %s >>>", ret ? "Change" : "Stay");
1506    return ret;
1507 }
1508
1509 static void
1510 _e_client_rotation_change_done(void)
1511 {
1512    Policy_Ext_Rotation *er;
1513    E_Client *ec;
1514
1515    if (rot.done_timer)
1516      {
1517         DBG("Remove rotation Timer by changing done");
1518         ecore_timer_del(rot.done_timer);
1519      }
1520    rot.done_timer = NULL;
1521
1522    EINA_LIST_FREE(rot.list, ec)
1523      {
1524         if (ec->e.state.rot.pending_show)
1525           {
1526              ec->e.state.rot.pending_show = 0;
1527              evas_object_show(ec->frame); // e_client_show(ec);
1528              e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
1529           }
1530         er = eina_hash_find(rot_hash, &ec);
1531         if ((er) && (er->show_grab))
1532           E_FREE_FUNC(er->show_grab, e_policy_visibility_client_grab_release);
1533         ec->e.state.rot.ang.next = -1;
1534         ec->e.state.rot.wait_for_done = 0;
1535      }
1536
1537    EINA_LIST_FREE(rot.async_list, ec)
1538      {
1539         _e_client_rotation_change_message_send(ec);
1540      }
1541
1542    rot.list = NULL;
1543    rot.async_list = NULL;
1544
1545    if (rot.screen_lock)
1546      {
1547         // do call comp_wl's screen unlock
1548         ELOGF("ROTATION", "RENDERING resume", NULL);
1549         e_comp_canvas_norender_pop();
1550         rot.screen_lock = EINA_FALSE;
1551      }
1552    e_zone_rotation_update_done(e_zone_current_get());
1553 }
1554
1555 static Eina_Bool
1556 _e_client_rotation_change_done_timeout(void *data)
1557 {
1558    E_Client *ec = (E_Client *)data;
1559
1560    if ((ec) && (!e_object_is_del(E_OBJECT(ec))))
1561      WRN("Timeout ROTATION_DONE %s(%p)",
1562          ec->icccm.name ? ec->icccm.name : "", ec);
1563    else
1564      WRN("Timeout ROTATION_DONE (%p)", ec);
1565
1566    _e_client_rotation_change_done();
1567    return ECORE_CALLBACK_CANCEL;
1568 }
1569
1570 static void
1571 _e_client_rotation_change_message_send(E_Client *ec)
1572 {
1573    int rotation;
1574
1575    if (!ec) return;
1576
1577    rotation = ec->e.state.rot.ang.next;
1578    if (rotation == -1) return;
1579    if (ec->e.state.rot.wait_for_done) return;
1580
1581    e_client_rotation_change_request(ec, rotation);
1582 }
1583
1584 static int
1585 _e_client_rotation_curr_next_get(const E_Client *ec)
1586 {
1587    if (!ec) return -1;
1588
1589    return ((ec->e.state.rot.ang.next == -1) ?
1590            ec->e.state.rot.ang.curr : ec->e.state.rot.ang.next);
1591 }
1592
1593 static void
1594 _e_client_event_client_rotation_change_begin_send(E_Client *ec)
1595 {
1596    E_Event_Client_Rotation_Change_Begin *ev = NULL;
1597    ev = E_NEW(E_Event_Client_Rotation_Change_Begin, 1);
1598    if (ev)
1599      {
1600         ev->ec = ec;
1601         e_object_ref(E_OBJECT(ec));
1602         EDBG(ec, "Rotation Event: Client Rotation BEGIN");
1603         ecore_event_add(E_EVENT_CLIENT_ROTATION_CHANGE_BEGIN,
1604                         ev,
1605                         _e_client_event_client_rotation_change_begin_free,
1606                         NULL);
1607      }
1608 }
1609
1610 static void
1611 _e_client_event_client_rotation_change_cancel_send(E_Client *ec)
1612 {
1613    E_Event_Client_Rotation_Change_Cancel *ev = NULL;
1614    ev = E_NEW(E_Event_Client_Rotation_Change_Cancel, 1);
1615    if (ev)
1616      {
1617         ev->ec = ec;
1618         e_object_ref(E_OBJECT(ec));
1619         EDBG(ec, "Rotation Event: Client Rotation CANCEL");
1620         ecore_event_add(E_EVENT_CLIENT_ROTATION_CHANGE_CANCEL,
1621                         ev,
1622                         _e_client_event_client_rotation_change_cancel_free,
1623                         NULL);
1624      }
1625 }
1626
1627 static void
1628 _e_client_event_client_rotation_change_begin_free(void *data __UNUSED__,
1629                                                   void      *ev)
1630 {
1631    E_Event_Client_Rotation_Change_Begin *e;
1632    e = ev;
1633    e_object_unref(E_OBJECT(e->ec));
1634    E_FREE(e);
1635 }
1636
1637 static void
1638 _e_client_event_client_rotation_change_cancel_free(void *data __UNUSED__,
1639                                                    void      *ev)
1640 {
1641    E_Event_Client_Rotation_Change_Cancel *e;
1642    e = ev;
1643    e_object_unref(E_OBJECT(e->ec));
1644    E_FREE(e);
1645 }
1646
1647 static void
1648 _e_client_event_client_rotation_change_end_free(void *data __UNUSED__,
1649                                                 void      *ev)
1650 {
1651    E_Event_Client_Rotation_Change_End *e;
1652    e = ev;
1653    e_object_unref(E_OBJECT(e->ec));
1654    E_FREE(e);
1655 }
1656
1657 static void
1658 _e_client_event_client_rotation_geometry_set_send(E_Client *ec, unsigned int angle, int x, int y, int w, int h)
1659 {
1660    E_Event_Client_Rotation_Geometry_Set *ev = NULL;
1661    ev = E_NEW(E_Event_Client_Rotation_Geometry_Set, 1);
1662    if (ev)
1663      {
1664         ev->ec = ec;
1665         ev->angle = angle;
1666         ev->x = x;
1667         ev->y = y;
1668         ev->w = w;
1669         ev->h = h;
1670         e_object_ref(E_OBJECT(ec));
1671         ecore_event_add(E_EVENT_CLIENT_ROTATION_GEOMETRY_SET,
1672                         ev,
1673                         _e_client_event_client_rotation_geometry_set_free,
1674                         NULL);
1675      }
1676 }
1677
1678 static void
1679 _e_client_event_client_rotation_geometry_set_free(void *data __UNUSED__,
1680                                                   void *ev)
1681 {
1682    E_Event_Client_Rotation_Geometry_Set *e;
1683    e = ev;
1684    e_object_unref(E_OBJECT(e->ec));
1685    E_FREE(e);
1686 }
1687
1688 static void
1689 _e_zone_event_rotation_change_begin_free(void *data __UNUSED__,
1690                                          void      *ev)
1691 {
1692    E_Event_Zone_Rotation_Change_Begin *e = ev;
1693    e_object_unref(E_OBJECT(e->zone));
1694    E_FREE(e);
1695 }
1696
1697 static void
1698 _e_zone_event_rotation_change_end_free(void *data __UNUSED__,
1699                                        void      *ev)
1700 {
1701    E_Event_Zone_Rotation_Change_End *e = ev;
1702    e_object_unref(E_OBJECT(e->zone));
1703    E_FREE(e);
1704 }
1705
1706 static void
1707 _e_zone_event_rotation_change_cancel_free(void *data __UNUSED__,
1708                                           void      *ev)
1709 {
1710    E_Event_Zone_Rotation_Change_Cancel *e = ev;
1711    e_object_unref(E_OBJECT(e->zone));
1712    E_FREE(e);
1713 }
1714
1715 /* e_client_roation functions */
1716 /**
1717  * @describe
1718  *  Get current rotoation state.
1719  * @param      ec             e_client
1720  * @return     EINA_FALSE     the state that does not rotating.
1721  *             EINA_TRUE      the state that rotating.
1722  */
1723 static Eina_Bool
1724 e_client_rotation_is_progress(E_Client *ec)
1725 {
1726    if (!ec) return EINA_FALSE;
1727
1728    if (ec->e.state.rot.ang.next == -1)
1729      return EINA_FALSE;
1730    else
1731      return EINA_TRUE;
1732 }
1733
1734 /**
1735  * @describe
1736  *  Get current rotation angle.
1737  * @param      ec             e_client
1738  * @return     int            current angle
1739  */
1740 static int
1741 e_client_rotation_curr_angle_get(E_Client *ec)
1742 {
1743    E_OBJECT_CHECK_RETURN(ec, -1);
1744    E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, -1);
1745
1746    return ec->e.state.rot.ang.curr;
1747 }
1748
1749 /**
1750  * @describe
1751  *  Get being replaced rotation angle.
1752  * @param      ec             e_client
1753  * @return     int            be replaced angle.
1754  */
1755 static int
1756 e_client_rotation_next_angle_get(E_Client *ec)
1757 {
1758    E_OBJECT_CHECK_RETURN(ec, -1);
1759    E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, -1);
1760
1761    return ec->e.state.rot.ang.next;
1762 }
1763
1764 static E_Client *
1765 e_client_rotation_find_below(E_Client *ec)
1766 {
1767    E_Client *below_ec = NULL;
1768
1769    for (below_ec = e_client_below_get(ec);
1770         below_ec;
1771         below_ec = e_client_below_get(below_ec))
1772       {
1773          if ((below_ec->comp_data && below_ec->comp_data->mapped) &&
1774              (!e_client_util_ignored_get(below_ec)) &&
1775              (!e_client_is_iconified_by_client(below_ec)))
1776            return below_ec;
1777       }
1778
1779    return NULL;
1780 }
1781
1782 /**
1783  * @describe
1784  *  Check if this e_client is rotatable to given angle.
1785  * @param      ec             e_client
1786  * @param      ang            test angle.
1787  * @return     EINA_FALSE     can't be rotated.
1788  *             EINA_TRUE      can be rotated.
1789  */
1790 static Eina_Bool
1791 e_client_rotation_is_available(E_Client *ec, int ang)
1792 {
1793    Eina_Bool ret = EINA_FALSE;
1794    unsigned int i;
1795    E_Client *below = NULL;
1796    Eina_Bool below_rot = EINA_TRUE;
1797    int ec_x, ec_y, ec_w, ec_h;
1798    E_Zone *zone;
1799
1800    if (ang < 0) return EINA_FALSE;
1801    if (!ec->e.state.rot.support)
1802      goto no_hint;
1803
1804    zone = e_comp_zone_find_by_ec(ec);
1805    EINA_SAFETY_ON_NULL_GOTO(zone, no_hint);
1806
1807    if (ec->e.state.rot.type == E_CLIENT_ROTATION_TYPE_DEPENDENT)
1808      {
1809         // check below fullsize window's angle
1810         ec_x = ec->x;
1811         ec_y = ec->y;
1812         ec_w = ec->w;
1813         ec_h = ec->h;
1814         e_client_geometry_get(ec, &ec_x, &ec_y, &ec_w, &ec_h);
1815
1816         if ((ec_x == zone->x) && (ec_y == zone->y) &&
1817             (ec_w == zone->w) && (ec_h == zone->h))
1818           {
1819              below = e_client_rotation_find_below(ec);
1820              if (below)
1821                {
1822                   below_rot = e_client_rotation_is_available(below, ang);
1823                   if (!below_rot)
1824                     {
1825                        EDBG(ec, "ec's below(ec:%p, win:%x) can not rotate to angle [%d]", below, e_client_util_win_get(below), ang);
1826                        goto no_hint;
1827                     }
1828                }
1829           }
1830      }
1831
1832    if (ec->e.state.rot.preferred_rot == -1)
1833      {
1834         if (ec->e.state.rot.available_rots &&
1835             ec->e.state.rot.count)
1836           {
1837              for (i = 0; i < ec->e.state.rot.count; i++)
1838                {
1839                   if (ec->e.state.rot.available_rots[i] == ang)
1840                     {
1841                        ret = EINA_TRUE;
1842                        break;
1843                     }
1844                }
1845           }
1846         else
1847           goto no_hint;
1848      }
1849    else if (ec->e.state.rot.preferred_rot == ang)
1850      ret = EINA_TRUE;
1851
1852    return ret;
1853 no_hint:
1854    return (ang == 0);
1855 }
1856
1857 /**
1858  * @describe
1859  *  Set the rotation of the e_client given angle.
1860  * @param      ec             e_client
1861  * *param      rotation       angle
1862  * @return     EINA_TRUE      rotation starts or is already in progress.
1863  *             EINA_FALSE     fail
1864  */
1865 static Eina_Bool
1866 e_client_rotation_set(E_Client *ec, int rotation)
1867 {
1868    Eina_List *list, *l;
1869    E_Client *child;
1870    int curr_rot;
1871    Policy_Ext_Rotation *ext_rot;
1872
1873    if (!ec) return EINA_FALSE;
1874
1875    if (rotation < 0) return EINA_FALSE;
1876    if (!e_client_rotation_is_available(ec, rotation)) return EINA_FALSE;
1877
1878    TRACE_DS_BEGIN(CLIENT ROTATION SET);
1879    TRACE_DS_BEGIN(CLINET CURRENT ANGLE SET);
1880
1881    // in case same with current angle.
1882    curr_rot = e_client_rotation_curr_angle_get(ec);
1883    EDBG(ec, "Client Rotation name:%s, curr_rot:%d, rotation:%d", ec->icccm.name?:"NULL", curr_rot, rotation);
1884    if (curr_rot == rotation)
1885      {
1886         if (e_client_rotation_is_progress(ec))
1887           {
1888              // cancel the changes in case only doesn't send request.
1889              if ((!ec->e.state.rot.pending_change_request) &&
1890                  (!ec->e.state.rot.wait_for_done))
1891                {
1892                   EDBG(ec, "Client Rotation Canceled (curr_rot == rot %d)", rotation);
1893                   _e_client_rotation_list_remove(ec);
1894                   if (ec->e.state.rot.pending_show)
1895                     {
1896                        ec->e.state.rot.pending_show = 0;
1897                        if (!e_object_is_del(E_OBJECT(ec)))
1898                          {
1899                             evas_object_show(ec->frame); // e_client_show(ec);
1900                             e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
1901                          }
1902                     }
1903
1904                   _e_client_event_client_rotation_change_cancel_send(ec);
1905
1906                   TRACE_DS_END();
1907                   TRACE_DS_END();
1908                   return EINA_FALSE;
1909                }
1910           }
1911         else
1912           {
1913              TRACE_DS_END();
1914              TRACE_DS_END();
1915              return EINA_FALSE;
1916           }
1917      }
1918    TRACE_DS_END();
1919
1920    ext_rot = eina_hash_find(rot_hash, &ec);
1921    if (ext_rot)
1922      {
1923         if (ext_rot->wait_update_pending.timer)
1924           ecore_timer_del(ext_rot->wait_update_pending.timer);
1925         ext_rot->wait_update_pending.timer = NULL;
1926      }
1927
1928    // in case same with next angle.
1929    curr_rot = e_client_rotation_next_angle_get(ec);
1930    if (curr_rot == rotation)
1931      {
1932         // if there is reserve angle, remove it.
1933         if (ec->e.state.rot.ang.reserve != -1)
1934           {
1935              ec->e.state.rot.ang.reserve = -1;
1936           }
1937         goto finish;
1938      }
1939
1940    /* if this e_client is rotating now,
1941     * it will be rotated to this angle after rotation done.
1942     */
1943    if ((ec->e.state.rot.pending_change_request) ||
1944        (ec->e.state.rot.wait_for_done))
1945      {
1946         ec->e.state.rot.ang.reserve = rotation;
1947         goto finish;
1948      }
1949
1950    /* search rotatable window in this window's child */
1951    list = eina_list_clone(ec->transients);
1952    EINA_LIST_FOREACH(list, l, child)
1953      {
1954         // the window which type is "E_WINDOW_TYPE_NORMAL" will be rotated itself.
1955         // it shouldn't be rotated by rotation state of parent window.
1956         if (child->netwm.type == E_WINDOW_TYPE_NORMAL) continue;
1957         if (child->comp_data && !child->comp_data->mapped) continue;
1958
1959         ELOGF("ROTATION", "Do rotation of child win %s(%p)",
1960               NULL, child->icccm.name, child);
1961
1962         e_client_rotation_set(child, rotation);
1963      }
1964    eina_list_free(list);
1965
1966    if (!e_client_rotation_is_progress(ec))
1967      {
1968         _e_client_event_client_rotation_change_begin_send(ec);
1969      }
1970
1971    ec->e.state.rot.pending_change_request = 0;
1972    ec->e.state.rot.ang.next = rotation;
1973    ec->changes.rotation = 1;
1974    EC_CHANGED(ec);
1975
1976 finish:
1977    /* Now the WM has a rotatable window thus we unset variables about zone rotation cancel */
1978    if (rot.cancel.state)
1979      {
1980         rot.cancel.state = EINA_FALSE;
1981         rot.cancel.zone = NULL;
1982      }
1983
1984    TRACE_DS_END();
1985    return EINA_TRUE;
1986 }
1987
1988 static void
1989 e_client_rotation_change_request(E_Client *ec, int rotation)
1990 {
1991    if (!ec) return;
1992    if (rotation < 0) return;
1993
1994    // if this window is in withdrawn state, change the state to NORMAL.
1995    // that's because the window in withdrawn state can't render its canvas.
1996    // eventually, this window will not send the message of rotation done,
1997    // even if e request to rotation this window.
1998
1999    //TODO: e_hint set is really neeed?.
2000    e_hints_window_visible_set(ec);
2001
2002    _e_tizen_rotation_send_angle_change(ec, rotation);
2003
2004    ec->e.state.rot.wait_for_done = 1;
2005
2006    Policy_Ext_Rotation *ext_rot;
2007    ext_rot = _policy_ext_rotation_get(ec);
2008    if (ext_rot)
2009      {
2010         _create_rotation_wait_update_done_timer(ext_rot);
2011      }
2012
2013    if ((!rot.async_list) ||
2014        (!eina_list_data_find(rot.async_list, ec)))
2015      {
2016         if (rot.done_timer)
2017           ecore_timer_del(rot.done_timer);
2018         rot.done_timer = ecore_timer_add(4.0f,
2019                                          _e_client_rotation_change_done_timeout,
2020                                          ec);
2021      }
2022 }
2023
2024 /* e_zone_roation functions */
2025 static void
2026 _e_zone_rotation_set_internal(E_Zone *zone, int rot)
2027 {
2028    E_Event_Zone_Rotation_Change_Begin *ev;
2029
2030    E_OBJECT_CHECK(zone);
2031    E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
2032
2033    if (zone->rot.wait_for_done)
2034      {
2035         INF("Pending Zone Rotation: wait_for_done %d block_count %d",
2036             zone->rot.wait_for_done, zone->rot.block.mod_count);
2037         zone->rot.next = rot;
2038         zone->rot.pending = EINA_TRUE;
2039         return;
2040      }
2041
2042    if (!e_config->wm_win_rotation) return;
2043
2044    ELOGF("ROTATION", "ZONE_ROT |zone:%d|rot curr:%d, active:%d, rot:%d",
2045          NULL, zone->num, zone->rot.curr, zone->rot.act, rot);
2046
2047    if ((zone->rot.curr == rot) &&
2048        (zone->rot.act == rot))
2049      return;
2050
2051    ELOGF("ROTATION", "ZONE_ROT |wait_for_done:%d->1",
2052          NULL, zone->rot.wait_for_done);
2053
2054    zone->rot.prev = zone->rot.act;
2055    zone->rot.curr = rot;
2056    zone->rot.wait_for_done = EINA_TRUE;
2057
2058    ev = E_NEW(E_Event_Zone_Rotation_Change_Begin, 1);
2059    if (ev)
2060      {
2061         ev->zone = zone;
2062         e_object_ref(E_OBJECT(ev->zone));
2063         ecore_event_add(E_EVENT_ZONE_ROTATION_CHANGE_BEGIN,
2064                         ev, _e_zone_event_rotation_change_begin_free, NULL);
2065      }
2066 }
2067
2068 EINTERN void
2069 e_zone_rotation_set(E_Zone *zone, int rotation)
2070 {
2071    E_OBJECT_CHECK(zone);
2072    E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
2073
2074    TRACE_DS_BEGIN(ZONE ROTATION SET);
2075
2076    zone->rot.unknown_state = EINA_TRUE;
2077
2078    if (rotation == -1)
2079      {
2080         ELOGF("ROTATION", "ZONE_ROT |UNKNOWN SET|zone:%d|rot curr:%d, rot:%d",
2081               NULL, zone->num, zone->rot.curr, rotation);
2082         return;
2083      }
2084
2085    if (e_zone_rotation_block_get(zone, rotation))
2086      {
2087         if ((zone->rot.wait_for_done) ||
2088             (zone->rot.block.mod_count > 0))
2089           {
2090              if ((!zone->rot.unblock.app_hint) &&
2091                  (!e_mod_pol_rotation_is_conf_enable_angle(rotation)))
2092                {
2093                   ELOGF("ROTATION", "ZONE_ROT |SKIP|zone:%d curr:%d rotation:%d config is not allowed",
2094                         NULL, zone->num, zone->rot.curr, rotation);
2095                   zone->rot.unknown_state = EINA_FALSE;
2096                   return;
2097                }
2098
2099              INF("Pending Zone Rotation: wait_for_done %d block_count %d",
2100                  zone->rot.wait_for_done, zone->rot.block.mod_count);
2101              zone->rot.next = rotation;
2102              zone->rot.pending = EINA_TRUE;
2103              return;
2104           }
2105
2106         if (zone->rot.block.sys_auto_rot)
2107           {
2108              ELOGF("ROTATION", "ZONE_ROT |SKIP|zone:%d curr:%d rotation:%d. auto rotation is locked",
2109                    NULL, zone->num, zone->rot.curr, rotation);
2110              zone->rot.unknown_state = EINA_FALSE;
2111              return;
2112           }
2113
2114         ELOGF("ROTATION", "ZONE_ROT |UNKNOWN SET|zone:%d|rot curr:%d, rot:%d",
2115               NULL, zone->num, zone->rot.curr, rotation);
2116         return;
2117      }
2118
2119    zone->rot.unknown_state = EINA_FALSE;
2120
2121    _e_zone_rotation_set_internal(zone, rotation);
2122    TRACE_DS_END();
2123 }
2124
2125 static void
2126 e_zone_rotation_sub_set(E_Zone *zone, int rotation)
2127 {
2128    E_OBJECT_CHECK(zone);
2129    E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
2130
2131    ELOGF("ROTATION", "SUB_SET  |zone:%d|rot curr:%d, rot:%d",
2132          NULL, zone->num, zone->rot.curr, rotation);
2133
2134    zone->rot.sub = rotation;
2135
2136    if ((zone->rot.unknown_state) &&
2137        (zone->rot.curr != rotation))
2138      _e_zone_rotation_set_internal(zone, rotation);
2139 }
2140
2141 /* This function has the policy of window rotation LOCK which is
2142  * determined according to the UX or the system order of priority.
2143  */
2144 EINTERN Eina_Bool
2145 e_zone_rotation_block_get(E_Zone *zone, int rot)
2146 {
2147    /* 1. specific app which set special hint such as camera */
2148    if (zone->rot.unblock.app_hint)
2149      {
2150         ELOGF("ROTATION", "BLOCK Get. unblocked by app_hint", NULL);
2151         return EINA_FALSE;
2152      }
2153
2154    /* 2. not supported angle in wm-policy configuration */
2155    if (!e_mod_pol_rotation_is_conf_enable_angle(rot))
2156      {
2157         ELOGF("ROTATION", "BLOCK Get. CONF disabled", NULL);
2158         return EINA_TRUE;
2159      }
2160
2161    /* 3. auto-rotation through vconf */
2162    if (zone->rot.block.sys_auto_rot)
2163      {
2164         ELOGF("ROTATION", "BLOCK Get. AUTO_ROT disabled", NULL);
2165         return EINA_TRUE;
2166      }
2167
2168    /* 4. temporary block count for the E sub-modules */
2169    if (zone->rot.block.mod_count > 0)
2170      {
2171         ELOGF("ROTATION", "BLOCK Get. E internal block. %d", NULL,
2172               zone->rot.block.mod_count);
2173         return EINA_TRUE;
2174      }
2175
2176    return EINA_FALSE;
2177 }
2178
2179 EINTERN Eina_Bool
2180 e_zone_rotation_block_type_set(E_Zone *zone, E_Zone_Rot_Block_Type type, const char *name_hint, Eina_Bool block)
2181 {
2182    E_OBJECT_CHECK_RETURN(zone, EINA_FALSE);
2183    E_OBJECT_TYPE_CHECK_RETURN(zone, E_ZONE_TYPE, EINA_FALSE);
2184
2185    switch (type)
2186      {
2187       case E_ZONE_ROT_BLOCK_TYPE_SYSTEM_AUTO_ROTATION:
2188          zone->rot.block.sys_auto_rot = block;
2189          ELOGF("ROTATION", "BLOCK_SET. sys_auto_rot:%s", NULL,
2190                block ? "true" : "false");
2191          break;
2192       case E_ZONE_ROT_BLOCK_TYPE_E_MODULE:
2193          if (block) zone->rot.block.mod_count++;
2194          else       zone->rot.block.mod_count--;
2195          if (zone->rot.block.mod_count <= 0) zone->rot.block.mod_count = 0;
2196          ELOGF("ROTATION", "BLOCK_SET. e modules:%s count:%d", NULL,
2197                block ? "true" : "false", zone->rot.block.mod_count);
2198          break;
2199       default:
2200          break;
2201      }
2202
2203    return EINA_TRUE;
2204 }
2205
2206 EINTERN Eina_Bool
2207 e_zone_rotation_unblock_type_set(E_Zone *zone, E_Zone_Rot_Unblock_Type type, const char *name_hint, Eina_Bool unblock)
2208 {
2209    E_OBJECT_CHECK_RETURN(zone, EINA_FALSE);
2210    E_OBJECT_TYPE_CHECK_RETURN(zone, E_ZONE_TYPE, EINA_FALSE);
2211
2212    switch (type)
2213      {
2214       case E_ZONE_ROT_UNBLOCK_TYPE_APP_HINT:
2215          zone->rot.unblock.app_hint = unblock;
2216          ELOGF("ROTATION", "UnBLOCK_SET app hint:%s", NULL,
2217                unblock ? "true" : "false");
2218          break;
2219       default:
2220          break;
2221      }
2222
2223    return EINA_TRUE;
2224 }
2225
2226 EINTERN Eina_Bool
2227 e_zone_rotation_block_set(E_Zone *zone, const char *name_hint, Eina_Bool block)
2228 {
2229    E_Event_Zone_Rotation_Change_Begin *ev;
2230
2231    E_OBJECT_CHECK_RETURN(zone, EINA_FALSE);
2232    E_OBJECT_TYPE_CHECK_RETURN(zone, E_ZONE_TYPE, EINA_FALSE);
2233
2234    e_zone_rotation_block_type_set(zone,
2235                                   E_ZONE_ROT_BLOCK_TYPE_E_MODULE,
2236                                   name_hint,
2237                                   block);
2238    if (block) return EINA_TRUE;
2239
2240    ELOGF("ROTATION", "ROT_BLOCK|RESUME|zone:%d|count:%d|from:%s|rot.pending:%d|next:%d",
2241          NULL,
2242          zone->num,
2243          zone->rot.block.mod_count,
2244          name_hint,
2245          zone->rot.pending,
2246          zone->rot.next);
2247
2248    if (zone->rot.pending)
2249      {
2250         zone->rot.pending = EINA_FALSE;
2251         if (zone->rot.curr != zone->rot.next)
2252           {
2253              if (e_zone_rotation_block_get(zone, zone->rot.next))
2254                {
2255                   zone->rot.pending = EINA_TRUE;
2256                   ELOGF("ROTATION", "ROT_BLOCK|PAUSE|zone:%d|count:%d|from:%s", NULL,
2257                         zone->num, zone->rot.block.mod_count, name_hint);
2258                   return EINA_TRUE;
2259                }
2260
2261              ELOGF("ROTATION", "ZONE_ROT |wait_for_done:%d->1",
2262                    NULL, zone->rot.wait_for_done);
2263
2264              zone->rot.prev = zone->rot.curr;
2265              zone->rot.curr = zone->rot.next;
2266              zone->rot.wait_for_done = EINA_TRUE;
2267              zone->rot.pending = EINA_FALSE;
2268
2269              ev = E_NEW(E_Event_Zone_Rotation_Change_Begin, 1);
2270              if (ev)
2271                {
2272                   ev->zone = zone;
2273                   e_object_ref(E_OBJECT(ev->zone));
2274                   ecore_event_add(E_EVENT_ZONE_ROTATION_CHANGE_BEGIN,
2275                                   ev, _e_zone_event_rotation_change_begin_free, NULL);
2276
2277                   ELOGF("ROTATION", "ROT_SET(P|zone:%d|rot:%d",
2278                         NULL, zone->num, zone->rot.curr);
2279                }
2280           }
2281      }
2282
2283    return EINA_TRUE;
2284 }
2285
2286 static void
2287 e_zone_rotation_update_done(E_Zone *zone)
2288 {
2289    E_Event_Zone_Rotation_Change_End *ev;
2290
2291    E_OBJECT_CHECK(zone);
2292    E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
2293
2294    ELOGF("ROTATION", "ROT_DONE |zone:%d|rot:%d",
2295          NULL, zone->num, zone->rot.curr);
2296
2297    ev = E_NEW(E_Event_Zone_Rotation_Change_End, 1);
2298    if (ev)
2299      {
2300         ev->zone = zone;
2301         e_object_ref(E_OBJECT(ev->zone));
2302         ecore_event_add(E_EVENT_ZONE_ROTATION_CHANGE_END,
2303                         ev, _e_zone_event_rotation_change_end_free, NULL);
2304      }
2305
2306    ELOGF("ROTATION", "ZONE_ROT |wait_for_done:%d->0",
2307          NULL, zone->rot.wait_for_done);
2308
2309    zone->rot.wait_for_done = EINA_FALSE;
2310    if ((zone->rot.block.mod_count == 0) && (zone->rot.pending))
2311      {
2312         zone->rot.pending = EINA_FALSE;
2313         if (zone->rot.curr != zone->rot.next)
2314           {
2315              ELOGF("ROTATION", "ZONE_ROT |wait_for_done:%d->1",
2316                    NULL, zone->rot.wait_for_done);
2317
2318              zone->rot.prev = zone->rot.curr;
2319              zone->rot.curr = zone->rot.next;
2320              zone->rot.wait_for_done = EINA_TRUE;
2321              zone->rot.pending = EINA_FALSE;
2322
2323              E_Event_Zone_Rotation_Change_Begin *ev2;
2324              ev2 = E_NEW(E_Event_Zone_Rotation_Change_Begin, 1);
2325              if (ev2)
2326                {
2327                   ev2->zone = zone;
2328                   e_object_ref(E_OBJECT(ev2->zone));
2329                   ecore_event_add(E_EVENT_ZONE_ROTATION_CHANGE_BEGIN,
2330                                   ev2, _e_zone_event_rotation_change_begin_free, NULL);
2331
2332                   ELOGF("ROTATION", "ROT_SET(P|zone:%d|rot:%d",
2333                         NULL, zone->num, zone->rot.curr);
2334
2335                }
2336           }
2337      }
2338 }
2339
2340 static void
2341 e_zone_rotation_update_cancel(E_Zone *zone)
2342 {
2343    E_Event_Zone_Rotation_Change_Cancel *ev;
2344
2345    E_OBJECT_CHECK(zone);
2346    E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
2347
2348    ELOGF("ROTATION", "ZONE_ROT |wait_for_done:%d->0",
2349          NULL, zone->rot.wait_for_done);
2350
2351    zone->rot.wait_for_done = EINA_FALSE;
2352    if (zone->rot.pending)
2353      {
2354         zone->rot.prev = zone->rot.curr;
2355         zone->rot.curr = zone->rot.next;
2356         zone->rot.pending = EINA_FALSE;
2357      }
2358
2359    ev = E_NEW(E_Event_Zone_Rotation_Change_Cancel, 1);
2360    if (ev)
2361      {
2362         ev->zone = zone;
2363         e_object_ref(E_OBJECT(ev->zone));
2364         ecore_event_add(E_EVENT_ZONE_ROTATION_CHANGE_CANCEL,
2365                         ev, _e_zone_event_rotation_change_cancel_free, NULL);
2366      }
2367 }
2368
2369 static Eina_Bool
2370 _rot_cb_zone_rotation_change_begin(void *data EINA_UNUSED, int ev_type EINA_UNUSED, E_Event_Zone_Rotation_Change_Begin *ev)
2371 {
2372    if ((!ev) || (!ev->zone)) return ECORE_CALLBACK_PASS_ON;
2373
2374    DBG("Rotation Zone Set: Rotation Change Begin");
2375    if (!_e_client_rotation_zone_set(ev->zone, NULL, NULL))
2376      {
2377         /* The WM will decide to cancel zone rotation at idle time.
2378          * Because, the policy module can make list of rotation windows
2379          */
2380         rot.cancel.state = EINA_TRUE;
2381         rot.cancel.zone = ev->zone;
2382      }
2383
2384    return ECORE_CALLBACK_RENEW;
2385 }
2386
2387 static void
2388 _e_tizen_rotation_smart_placement_apply(E_Client *ec)
2389 {
2390    int angle = 0;
2391    int x, y, w, h;
2392    int new_x, new_y;
2393    int x1, y1, x2, y2;
2394    int zx, zy, zw, zh;
2395    E_Zone *zone;
2396
2397    zone = e_comp_zone_find_by_ec(ec);
2398    EINA_SAFETY_ON_NULL_RETURN(zone);
2399
2400    angle = zone->rot.curr;
2401
2402    e_client_geometry_get(ec, &x, &y, &w, &h);
2403    new_x = x;
2404    new_y = y;
2405
2406    x1 = x;
2407    x2 = x+w;
2408    y1 = y;
2409    y2 = y+h;
2410
2411    zx = zone->x;
2412    zy = zone->y;
2413    zw = zone->w;
2414    zh = zone->h;
2415
2416    // calculate x position
2417    if (x1 < 0)
2418      {
2419         if (w > zw)
2420           {
2421              if (angle == 270)
2422                new_x = -w;
2423              else
2424                new_x = 0;
2425           }
2426         else
2427           new_x = 0;
2428      }
2429    else if (x2 > zx + zw)
2430      {
2431         if (w > zw)
2432           {
2433              if (angle == 270)
2434                new_x = -w;
2435              else
2436                new_x = 0;
2437           }
2438         else
2439           new_x = zx + zw - w;
2440      }
2441    else
2442      {
2443         new_x = x;
2444      }
2445
2446    // calculate y position
2447    if (y1 < 0)
2448      {
2449         if (h > zh)
2450           {
2451              if (angle == 180)
2452                new_y = -h;
2453              else
2454                new_y = 0;
2455           }
2456         else
2457           new_y = 0;
2458      }
2459    else if (y2 > zy + zh)
2460      {
2461         if (h > zh)
2462           {
2463              if (angle == 180)
2464                new_y = -h;
2465              else
2466                new_y = 0;
2467           }
2468         else
2469           new_y = zy + zh - h;
2470      }
2471    else
2472      {
2473         new_y = y;
2474      }
2475
2476    if ((x != new_x) || (y != new_y))
2477      {
2478         ELOGF("ROTATION", "APPLY placement policy. old(%d,%d) -> new(%d,%d), angle:%d", ec, x, y, new_x, new_y, angle);
2479         e_client_util_move_without_frame(ec, new_x, new_y);
2480      }
2481 }
2482
2483 static void
2484 _e_client_rotation_wait_update_clear(E_Client *ec)
2485 {
2486    Policy_Ext_Rotation *rot;
2487
2488    rot = eina_hash_find(rot_hash, &ec);
2489    if (!rot) return;
2490
2491    rot->wait_update_pending.timer = NULL;
2492    _remove_rotation_wait_update_done_timer(rot);
2493
2494    if (!rot->angle_change_done)
2495      {
2496         ELOGF("ROTATION", "TIMEOUT... waiting for rotation_ack_done... angle change by force", ec);
2497         _e_tizen_rotation_angle_change(rot);
2498      }
2499
2500    _e_client_rotation_list_remove(ec);
2501    if (ec->e.state.rot.pending_show)
2502      {
2503         ec->e.state.rot.pending_show = 0;
2504         evas_object_show(ec->frame);
2505         e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
2506      }
2507
2508    if (rot->show_grab)
2509      E_FREE_FUNC(rot->show_grab, e_policy_visibility_client_grab_release);
2510
2511    rot->wait_update = EINA_FALSE;
2512
2513    _e_tizen_rotation_smart_placement_apply(ec);
2514 }
2515
2516 static Eina_Bool
2517 _e_client_rotation_wait_update_pending_timeout(void *data)
2518 {
2519    E_Client *ec = (E_Client *)data;
2520
2521    if ((ec) && (!e_object_is_del(E_OBJECT(ec))))
2522      WRN("Timeout Wait Update Pending %s(%p)",
2523          ec->icccm.name ? ec->icccm.name : "", ec);
2524    else
2525      WRN("Timeout Wait Update Pending (%p)", ec);
2526
2527    if (ec)
2528      _e_client_rotation_wait_update_clear(ec);
2529
2530    return ECORE_CALLBACK_CANCEL;
2531 }
2532
2533 static void
2534 _rot_cb_wl_buffer_change(void *d EINA_UNUSED, E_Client *ec)
2535 {
2536    Policy_Ext_Rotation *rot;
2537
2538    rot = eina_hash_find(rot_hash, &ec);
2539    if (!rot) return;
2540    if (ec->e.state.rot.nopending_render) return;
2541
2542    if (!rot->angle_change_done)
2543      {
2544         DBG("Update Buffer in progress of rotation ec '%s'(%p) HOOK",
2545             ec->icccm.name ? ec->icccm.name : "", ec);
2546
2547         e_pixmap_image_clear(ec->pixmap, EINA_TRUE);
2548         e_pixmap_resource_set(ec->pixmap, NULL);
2549      }
2550 }
2551
2552 static Eina_Bool
2553 _rot_cb_buffer_change(void *data EINA_UNUSED, int ev_type EINA_UNUSED, E_Event_Client *ev)
2554 {
2555    Policy_Ext_Rotation *rot;
2556
2557    if (EINA_UNLIKELY(!ev))
2558      goto end;
2559
2560    if (EINA_UNLIKELY(!ev->ec))
2561      goto end;
2562
2563    rot = eina_hash_find(rot_hash, &ev->ec);
2564    if (!rot)
2565      goto end;
2566
2567    /* WORKAROUND
2568     * wl_buffer can be destroyed after attach/damage/frame/commit to wl_surface.
2569     * we have to handle this case.
2570     */
2571    if ((!rot->angle_change_done) || (!e_pixmap_resource_get(ev->ec->pixmap)))
2572      {
2573         DBG("Update Buffer in progress of rotation ec '%s'(%p) EVENT nopending_render:%d",
2574             ev->ec->icccm.name ? ev->ec->icccm.name : "", ev->ec,
2575             ev->ec->e.state.rot.nopending_render);
2576
2577         if (!ev->ec->e.state.rot.nopending_render)
2578           {
2579              e_pixmap_image_clear(ev->ec->pixmap, EINA_TRUE);
2580              e_pixmap_resource_set(ev->ec->pixmap, NULL);
2581           }
2582      }
2583    else if (rot->wait_update)
2584      {
2585         DBG("Update Buffer After Rotation Done ec '%s'(%p) b %p pending:%d count:%d",
2586             ev->ec->icccm.name ? ev->ec->icccm.name : "",
2587             ev->ec,
2588             e_pixmap_resource_get(ev->ec->pixmap),
2589             rot->wait_update_pending.use,
2590             rot->wait_update_pending.count);
2591
2592         if (rot->wait_update_pending.use)
2593           {
2594              if (rot->wait_update_pending.count > 0)
2595                {
2596                   if (rot->wait_update_pending.count == 2)
2597                     {
2598                        if (rot->wait_update_pending.timer)
2599                          ecore_timer_del(rot->wait_update_pending.timer);
2600
2601                        rot->wait_update_pending.timer = ecore_timer_add(0.1f,
2602                                                                         _e_client_rotation_wait_update_pending_timeout,
2603                                                                         ev->ec);
2604                     }
2605
2606                   rot->wait_update_pending.count--;
2607                   return ECORE_CALLBACK_RENEW;
2608                }
2609              else
2610                {
2611                   if (rot->wait_update_pending.timer)
2612                     ecore_timer_del(rot->wait_update_pending.timer);
2613                   rot->wait_update_pending.timer = NULL;
2614                }
2615           }
2616
2617         _e_client_rotation_wait_update_clear(ev->ec);
2618      }
2619
2620    if (ev->ec->e.state.rot.pending_show)
2621      {
2622         DBG("Buffer Changed: force add update list to send frame until pending show");
2623         /* consider e_pixmap_image_clear() instead of update_add() */
2624         e_pixmap_image_clear(ev->ec->pixmap, EINA_TRUE);
2625      }
2626
2627 end:
2628    return ECORE_CALLBACK_RENEW;
2629 }
2630
2631 static void
2632 _rot_hook_new_client(void *d EINA_UNUSED, E_Client *ec)
2633 {
2634    Policy_Ext_Rotation *rot;
2635
2636    ec->e.state.rot.preferred_rot = -1;
2637    ec->e.state.rot.type = E_CLIENT_ROTATION_TYPE_NORMAL;
2638    ec->e.state.rot.ang.next = -1;
2639    ec->e.state.rot.ang.reserve = -1;
2640    ec->e.state.rot.pending_show = 0;
2641    ec->e.state.rot.ang.curr = 0;
2642    ec->e.state.rot.ang.prev = 0;
2643
2644    EINA_SAFETY_ON_NULL_RETURN(rot_hash);
2645
2646    rot = eina_hash_find(rot_hash, &ec);
2647    if (!rot) return;
2648
2649    if (rot->preferred_angle)
2650      ec->e.fetch.rot.preferred_rot = 1;
2651
2652    if (rot->available_angles)
2653      ec->e.fetch.rot.available_rots = 1;
2654 }
2655
2656 static void
2657 _rot_hook_client_del(void *d EINA_UNUSED, E_Client *ec)
2658 {
2659    Policy_Ext_Rotation *ext_rot;
2660    struct wl_resource *res;
2661    E_Zone *zone;
2662
2663    _e_client_rotation_list_remove(ec);
2664    if (rot.async_list) rot.async_list = eina_list_remove(rot.async_list, ec);
2665
2666    ec->e.state.rot.preferred_rot = -1;
2667
2668    if (ec->e.state.rot.available_rots)
2669      E_FREE(ec->e.state.rot.available_rots);
2670
2671    ext_rot = eina_hash_find(rot_hash, &ec);
2672    if (ext_rot)
2673      {
2674         if (ext_rot->wait_update_pending.timer)
2675           ecore_timer_del(ext_rot->wait_update_pending.timer);
2676         ext_rot->wait_update_pending.timer = NULL;
2677
2678         _remove_rotation_wait_update_done_timer(ext_rot);
2679
2680         EINA_LIST_FREE(ext_rot->rotation_list, res)
2681            wl_resource_set_user_data(res, NULL);
2682
2683         if (ext_rot->show_grab)
2684           E_FREE_FUNC(ext_rot->show_grab, e_policy_visibility_client_grab_release);
2685
2686         eina_hash_del_by_key(rot_hash, &ec);
2687      }
2688
2689    if (fg_ec == ec)
2690      {
2691         EDBG(ec, "Set the fg_ec to NULL");
2692         fg_ec = NULL;
2693
2694         if (_camera_check(ec))
2695           {
2696             zone = e_comp_zone_find_by_ec(ec);
2697             if (zone) _try_lock_rot_for_fg_app(zone);
2698           }
2699      }
2700 }
2701
2702 static void
2703 _rot_hook_eval_end(void *d EINA_UNUSED, E_Client *ec)
2704 {
2705    E_Zone *zone;
2706
2707    zone = e_comp_zone_find_by_ec(ec);
2708    EINA_SAFETY_ON_NULL_RETURN(zone);
2709
2710    if (ec->changes.rotation)
2711      {
2712         int ec_x, ec_y, ec_w, ec_h;
2713
2714         if (ec->moving) e_client_act_move_end(ec, NULL);
2715
2716         ec_x = ec->x;
2717         ec_y = ec->y;
2718         ec_w = ec->w;
2719         ec_h = ec->h;
2720         e_client_geometry_get(ec, &ec_x, &ec_y, &ec_w, &ec_h);
2721
2722         if ((!zone->rot.block.mod_count) &&
2723             ((!evas_object_visible_get(ec->frame)) ||
2724              (!E_INTERSECTS(ec_x, ec_y, ec_w, ec_h, zone->x, zone->y, zone->w, zone->h))))
2725           {
2726              // async list add
2727              rot.async_list = eina_list_append(rot.async_list, ec);
2728           }
2729         else
2730           {
2731              // sync list add
2732              rot.list = eina_list_append(rot.list, ec);
2733              _e_client_rotation_change_message_send(ec);
2734           }
2735         rot.fetch = EINA_TRUE;
2736         ec->changes.rotation = 0;
2737      }
2738 }
2739
2740 static void
2741 _rot_hook_eval_fetch(void *d EINA_UNUSED, E_Client *ec)
2742 {
2743    Policy_Ext_Rotation *rot;
2744
2745    Eina_Bool early_angle_change = EINA_FALSE;
2746
2747    if (!ec) return;
2748
2749    rot = eina_hash_find(rot_hash, &ec);
2750    if (!rot) return;
2751
2752    if(ec->e.fetch.rot.support)
2753      {
2754         ec->e.state.rot.support = 1;
2755
2756         ec->e.fetch.rot.need_rotation = EINA_TRUE;
2757         ec->e.fetch.rot.support = 0;
2758      }
2759    if (ec->e.fetch.rot.preferred_rot)
2760      {
2761         int _prev_preferred_rot;
2762         _prev_preferred_rot = ec->e.state.rot.preferred_rot;
2763         ec->e.state.rot.preferred_rot = -1;
2764
2765         switch (rot->preferred_angle)
2766           {
2767              case TIZEN_ROTATION_ANGLE_0:
2768                 ec->e.state.rot.preferred_rot = 0;
2769                 break;
2770              case TIZEN_ROTATION_ANGLE_90:
2771                 ec->e.state.rot.preferred_rot = 90;
2772                 break;
2773              case TIZEN_ROTATION_ANGLE_180:
2774                 ec->e.state.rot.preferred_rot = 180;
2775                 break;
2776              case TIZEN_ROTATION_ANGLE_270:
2777                 ec->e.state.rot.preferred_rot = 270;
2778                 break;
2779              default:
2780                 break;
2781           }
2782
2783         if (_prev_preferred_rot != ec->e.state.rot.preferred_rot)
2784           ec->e.fetch.rot.need_rotation = EINA_TRUE;
2785
2786         EDBG(ec, "Fetch Preferred: preferred (prev %d cur %d)",
2787             _prev_preferred_rot, ec->e.state.rot.preferred_rot);
2788
2789          early_angle_change = _rot_eval_fetch_preferred_send_angle_change(rot);
2790
2791         ec->e.fetch.rot.preferred_rot = 0;
2792      }
2793    if (ec->e.fetch.rot.available_rots)
2794      {
2795         Eina_Bool diff = EINA_FALSE;
2796         int *rots = NULL;
2797         unsigned int _prev_count = 0, count = 0, i = 0;
2798         int _prev_rots[4] = { -1, };
2799         uint32_t available_angles = 0;
2800
2801         if (ec->e.state.rot.available_rots)
2802           {
2803              memcpy(_prev_rots,
2804                     ec->e.state.rot.available_rots,
2805                     (sizeof(int) * ec->e.state.rot.count));
2806           }
2807
2808         _prev_count = ec->e.state.rot.count;
2809         ec->e.state.rot.count = 0;
2810
2811         /* check avilable_angles */
2812         if (rot->available_angles & TIZEN_ROTATION_ANGLE_0) count++;
2813         if (rot->available_angles & TIZEN_ROTATION_ANGLE_90) count++;
2814         if (rot->available_angles & TIZEN_ROTATION_ANGLE_180) count++;
2815         if (rot->available_angles & TIZEN_ROTATION_ANGLE_270) count++;
2816
2817         if (count != 0)
2818           rots = (int*)E_NEW(int, count);
2819
2820         if (!rots)
2821           {
2822              if (ec->e.state.rot.available_rots)
2823                {
2824                   /* restore previous rotation hints */
2825                   memcpy(ec->e.state.rot.available_rots, _prev_rots, (sizeof(int) * _prev_count));
2826                }
2827              goto end_fetch_rot;
2828           }
2829
2830         if (ec->e.state.rot.available_rots)
2831           E_FREE(ec->e.state.rot.available_rots);
2832
2833         available_angles = rot->available_angles;
2834
2835         if ((count > 0) && (rots))
2836           {
2837              for (i = 0; i < count; i++)
2838               {
2839                   if (available_angles & TIZEN_ROTATION_ANGLE_0)
2840                     {
2841                        rots[i] = 0;
2842                        available_angles = available_angles & ~TIZEN_ROTATION_ANGLE_0;
2843                     }
2844                   else if (available_angles & TIZEN_ROTATION_ANGLE_90)
2845                     {
2846                        rots[i] = 90;
2847                        available_angles = available_angles & ~TIZEN_ROTATION_ANGLE_90;
2848                     }
2849                   else if (available_angles & TIZEN_ROTATION_ANGLE_180)
2850                     {
2851                        rots[i] = 180;
2852                        available_angles = available_angles & ~TIZEN_ROTATION_ANGLE_180;
2853                     }
2854                   else if (available_angles & TIZEN_ROTATION_ANGLE_270)
2855                     {
2856                        rots[i] = 270;
2857                        available_angles = available_angles & ~TIZEN_ROTATION_ANGLE_270;
2858                     }
2859                }
2860
2861              ec->e.state.rot.available_rots = rots;
2862              ec->e.state.rot.count = count;
2863
2864              if (_prev_count != count) diff = EINA_TRUE;
2865
2866              for (i = 0; i < count; i++)
2867                {
2868                   if ((!diff) && (_prev_rots[i] != rots[i]))
2869                     {
2870                        diff = EINA_TRUE;
2871                        break;
2872                     }
2873                }
2874            }
2875         /* check avilable_angles end*/
2876
2877         /* Print fetch information */
2878           {
2879              Eina_Strbuf *b = eina_strbuf_new();
2880
2881              EINF(ec, "Fetch Available");
2882              if (_prev_count > 0)
2883                {
2884                   for (i = 0; i < _prev_count; i++)
2885                     eina_strbuf_append_printf(b, "%d ", _prev_rots[i]);
2886                   INF("\tprev %s", eina_strbuf_string_get(b));
2887                   eina_strbuf_reset(b);
2888                }
2889
2890              for (i = 0; i < count; i++)
2891                eina_strbuf_append_printf(b, "%d ", rots[i]);
2892              INF("\tcur %s", eina_strbuf_string_get(b));
2893
2894              eina_strbuf_free(b);
2895           }
2896
2897         if (diff) ec->e.fetch.rot.need_rotation = EINA_TRUE;
2898         ec->e.fetch.rot.available_rots = 0;
2899
2900          // after preferred calc., if there were no early event, check available angle again
2901          if (!early_angle_change)
2902            early_angle_change = _rot_eval_fetch_available_send_angle_change(rot);
2903      }
2904 end_fetch_rot:
2905
2906    rot->hint_fetch = 1;
2907    if (early_angle_change)
2908      {
2909         EDBG(ec, "Send angle_change early for ec %x, Wait ack and next surface commit after ack", e_client_util_win_get(ec));
2910      }
2911    else if ((ec->new_client) && (ec->e.state.rot.pending_show))
2912      {
2913         ec->e.state.rot.pending_show = 0;
2914         evas_object_show(ec->frame);
2915         if (!ec->changes.rotation)
2916           e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
2917      }
2918    else if ((evas_object_visible_get(ec->frame) && (ec->e.fetch.rot.need_rotation)))
2919      {
2920         DBG("Rotation Zone Set: Fetch Hint");
2921         E_Zone *zone;
2922         zone = e_comp_zone_find_by_ec(ec);
2923         if (zone) _e_client_rotation_zone_set(zone, NULL, NULL);
2924      }
2925
2926    if (ec->e.fetch.rot.need_rotation)
2927      ec->e.fetch.rot.need_rotation = EINA_FALSE;
2928 }
2929
2930 /* Occasionally, some client destroys only the xdg_surface and keeps the wl_surface.
2931  * In this case, E20 has the zombie ec for maintaining of this kind of client.
2932  * If the zombie ec is going to be alive again (it means that client tries to show window),
2933  * then the zombie ec has the unmap state on the E_COMP_OBJECT_INTERCEPT_HOOK_SHOW_HELPER handler.
2934  * So fg_ec of rotation module can not be changed to activated ec even if it is shown on the screen.
2935  *
2936  * Thus, we need to use a new hook for changing to right fg_ec of rotation module in this case.
2937  * E_POL_VIS_HOOK_TYPE_FG_SET is good point to be called after E_COMP_OBJECT_INTERCEPT_HOOK_SHOW_HELPER
2938  * hook and given ec has always mapped state.
2939  */
2940 static Eina_Bool
2941 _rot_fg_set(E_Client *ec, Eina_Bool check_obscured)
2942 {
2943    Policy_Ext_Rotation *rot;
2944    E_Zone *zone;
2945
2946    rot = eina_hash_find(rot_hash, &ec);
2947    if (!rot)
2948      return EINA_TRUE;
2949
2950    if (e_pixmap_type_get(ec->pixmap) == E_PIXMAP_TYPE_EXT_OBJECT)
2951      return EINA_TRUE;
2952
2953    if (!rot->hint_fetch)
2954      {
2955         /* need to fetch rotation hint. */
2956         ec->e.state.rot.pending_show = 1;
2957         EC_CHANGED(ec);
2958         return EINA_FALSE;
2959      }
2960
2961    if (ec->e.state.rot.pending_show)
2962      return EINA_FALSE;
2963
2964    zone = e_comp_zone_find_by_ec(ec);
2965    EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
2966
2967    if (e_policy_visibility_client_is_activity(ec))
2968      {
2969         EDBG(ec, "Check ec %x to set fg_ec", e_client_util_win_get(ec));
2970         if (_no_active_lockscreen_check(ec))
2971           {
2972              if (check_obscured)
2973                {
2974                   if (ec->visibility.obscured != E_VISIBILITY_FULLY_OBSCURED)
2975                     {
2976                        EDBG(ec, "Set the fg_ec to %x", e_client_util_win_get(ec));
2977                        fg_ec = ec;
2978                     }
2979                }
2980              else
2981                {
2982                   /* don't need to check visibility of given foreground ec
2983                    * it will have E_VISIBILITY_UNOBSCURED soon
2984                    */
2985                   EDBG(ec, "Set the fg_ec to %x", e_client_util_win_get(ec));
2986                   fg_ec = ec;
2987                }
2988
2989              if (_camera_check(ec))
2990                _unlock_rot_for_fg_app(zone);
2991              else
2992                _try_lock_rot_for_fg_app(zone);
2993           }
2994      }
2995
2996    _e_client_rotation_zone_set(zone, ec, NULL);
2997    if (ec->changes.rotation)
2998      {
2999         EDBG(ec, "Postpone show: ang %d", ec->e.state.rot.ang.next);
3000         e_pixmap_image_clear(ec->pixmap, 1);
3001         ec->e.state.rot.pending_show = 1;
3002         /* to be invoked 'eval_end' */
3003         EC_CHANGED(ec);
3004         return EINA_FALSE;
3005      }
3006
3007    return EINA_TRUE;
3008 }
3009
3010 static Eina_Bool
3011 _rot_hook_fg_set(void *d EINA_UNUSED, E_Client *ec)
3012 {
3013    return _rot_fg_set(ec, EINA_FALSE);
3014 }
3015
3016 static Eina_Bool
3017 _rot_intercept_hook_show_helper(void *d EINA_UNUSED, E_Client *ec)
3018 {
3019    return _rot_fg_set(ec, EINA_TRUE);
3020 }
3021
3022 static Eina_Bool
3023 _rot_intercept_hook_hide(void *d EINA_UNUSED, E_Client *ec)
3024 {
3025    E_Zone *zone;
3026
3027    // TODO: Add VKBD Hide, VKBD Parent Hide routine.
3028    // clear pending_show, because this window is hidden now.
3029    ec->e.state.rot.pending_show = 0;
3030
3031    zone = e_comp_zone_find_by_ec(ec);
3032    EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_TRUE);
3033
3034    if (fg_ec == ec)
3035      {
3036         EDBG(ec, "Set the fg_ec to NULL");
3037         fg_ec = NULL;
3038
3039         if (_camera_check(ec))
3040           _try_lock_rot_for_fg_app(zone);
3041      }
3042
3043    // for rotating ec in the force_update_list
3044    _e_client_rotation_zone_set(zone, fg_ec, ec);
3045
3046    return EINA_TRUE;
3047 }
3048
3049 static Eina_Bool
3050 _rot_cb_idle_enterer(void *data EINA_UNUSED)
3051 {
3052    Eina_List *l;
3053    E_Client *ec;
3054    int n;
3055
3056    if (rot.cancel.state)
3057      {
3058         /* there is no border which supports window manager rotation */
3059         e_zone_rotation_update_cancel(rot.cancel.zone);
3060         rot.cancel.state = EINA_FALSE;
3061         rot.cancel.zone = NULL;
3062      }
3063
3064    if (rot.fetch)
3065      {
3066         n = eina_list_count(rot.list);
3067
3068         if (n == 1)
3069           {
3070              ec = eina_list_data_get(rot.list);
3071              if (ec->e.state.rot.nopending_render == 0)
3072                {
3073                   if (!rot.screen_lock)
3074                     {
3075                        ELOGF("ROTATION", "RENDERING pause", NULL);
3076
3077                        e_pixmap_image_clear(ec->pixmap, 1);
3078                        e_comp_canvas_norender_push();
3079                        rot.screen_lock = EINA_TRUE;
3080                     }
3081                }
3082              else
3083                {
3084                   if (rot.screen_lock)
3085                     {
3086                        ELOGF("ROTATION", "RENDERING resume", NULL);
3087                        e_comp_canvas_norender_pop();
3088                        rot.screen_lock = EINA_FALSE;
3089                     }
3090                }
3091           }
3092         // if there is windows over 2 that has to be rotated or is existed window needs resizing,
3093         // lock the screen.
3094         // but, DO NOT lock the screen when block state by E module
3095         else if (n > 1)
3096           {
3097              Eina_Bool rot_block = EINA_FALSE;
3098              E_Zone *zone;
3099
3100              EINA_LIST_FOREACH(rot.list, l, ec)
3101                {
3102                   zone = e_comp_zone_find_by_ec(ec);
3103                   if (!zone) continue;
3104
3105                   if (zone->rot.block.mod_count)
3106                     {
3107                        rot_block = EINA_TRUE;
3108                     }
3109                }
3110              if ((!rot.screen_lock) && (!rot_block))
3111                {
3112                   ELOGF("ROTATION", "RENDERING pause", NULL);
3113
3114                   EINA_LIST_FOREACH(rot.list, l, ec)
3115                      e_pixmap_image_clear(ec->pixmap, 1);
3116
3117                   e_comp_canvas_norender_push();
3118                   rot.screen_lock = EINA_TRUE;
3119                }
3120           }
3121         else
3122           {
3123              /* n == 0 */
3124              Eina_List *zlist = NULL;
3125              E_Zone *zone = NULL;
3126
3127              if (rot.async_list)
3128                {
3129                   EINA_LIST_FREE(rot.async_list, ec)
3130                     {
3131                        zone = e_comp_zone_find_by_ec(ec);
3132                        if (zone)
3133                          {
3134                             if (!eina_list_data_find(zlist, zone))
3135                               zlist = eina_list_append(zlist, zone);
3136                             _e_client_rotation_change_message_send(ec);
3137                          }
3138                     }
3139
3140                   EINA_LIST_FOREACH(zlist, l, zone)
3141                     e_zone_rotation_update_cancel(zone);
3142                   if (zlist)
3143                     eina_list_free(zlist);
3144                }
3145           }
3146
3147         rot.fetch = EINA_FALSE;
3148      }
3149
3150    return ECORE_CALLBACK_RENEW;
3151 }
3152
3153 static void
3154 _rot_hook_pixmap_unusable(void *data EINA_UNUSED, E_Pixmap *cp)
3155 {
3156    E_Client *ec = (E_Client *)e_pixmap_client_get(cp);
3157    if (!ec) return;
3158
3159    if (ec->e.state.rot.pending_show)
3160      {
3161         ELOGF("ROTATION", "Unset Postpone show", ec);
3162         ec->e.state.rot.pending_show = 0;
3163      }
3164 }
3165
3166 Eina_Bool
3167 e_mod_rot_wl_init(void)
3168 {
3169    EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp_wl, EINA_FALSE);
3170    EINA_SAFETY_ON_NULL_RETURN_VAL(e_comp_wl->wl.disp, EINA_FALSE);
3171
3172    rot_global = wl_global_create(e_comp_wl->wl.disp, &tizen_policy_ext_interface, 3,
3173                                  NULL, _e_tizen_policy_ext_bind_cb);
3174    if (!rot_global)
3175      {
3176         ERR("Could not add tizen_policy_ext to wayland globals: %m");
3177         return EINA_FALSE;
3178      }
3179
3180    rot_hash = eina_hash_pointer_new(_policy_ext_rotation_free);
3181
3182    E_LIST_HANDLER_APPEND(rot_cbs,     E_EVENT_ZONE_ROTATION_CHANGE_BEGIN, _rot_cb_zone_rotation_change_begin, NULL);
3183    E_LIST_HANDLER_APPEND(rot_cbs,     E_EVENT_CLIENT_BUFFER_CHANGE,       _rot_cb_buffer_change,              NULL);
3184    E_CLIENT_HOOK_APPEND(rot_ec_hooks, E_CLIENT_HOOK_NEW_CLIENT,           _rot_hook_new_client,               NULL);
3185    E_CLIENT_HOOK_APPEND(rot_ec_hooks, E_CLIENT_HOOK_DEL,                  _rot_hook_client_del,               NULL);
3186    E_CLIENT_HOOK_APPEND(rot_ec_hooks, E_CLIENT_HOOK_EVAL_END,             _rot_hook_eval_end,                 NULL);
3187    E_CLIENT_HOOK_APPEND(rot_ec_hooks, E_CLIENT_HOOK_EVAL_FETCH,           _rot_hook_eval_fetch,               NULL);
3188    E_COMP_OBJECT_INTERCEPT_HOOK_APPEND(rot_obj_hooks, E_COMP_OBJECT_INTERCEPT_HOOK_SHOW_HELPER, _rot_intercept_hook_show_helper, NULL);
3189    E_COMP_OBJECT_INTERCEPT_HOOK_APPEND(rot_obj_hooks, E_COMP_OBJECT_INTERCEPT_HOOK_HIDE,        _rot_intercept_hook_hide,        NULL);
3190    E_COMP_WL_HOOK_APPEND(wl_hooks,                    E_COMP_WL_HOOK_BUFFER_CHANGE,             _rot_cb_wl_buffer_change,        NULL);
3191    E_PIXMAP_HOOK_APPEND(rot_pixmap_hooks, E_PIXMAP_HOOK_UNUSABLE, _rot_hook_pixmap_unusable, NULL);
3192
3193    E_Pol_Vis_Hook *h = e_policy_visibility_hook_add(E_POL_VIS_HOOK_TYPE_FG_SET, _rot_hook_fg_set, NULL);
3194    pol_vis_hooks = eina_list_append(pol_vis_hooks, h);
3195
3196    rot_idle_enterer = ecore_idle_enterer_add(_rot_cb_idle_enterer, NULL);
3197
3198    return EINA_TRUE;
3199 }
3200
3201 void
3202 e_mod_rot_wl_shutdown(void)
3203 {
3204    E_FREE_FUNC(rot_hash, eina_hash_free);
3205    E_FREE_FUNC(rot.force_update_list, eina_list_free);
3206
3207    E_FREE_LIST(pol_vis_hooks, e_policy_visibility_hook_del);
3208    E_FREE_LIST(wl_hooks, e_comp_wl_hook_del);
3209    E_FREE_LIST(rot_ec_hooks, e_client_hook_del);
3210    E_FREE_LIST(rot_cbs, ecore_event_handler_del);
3211    E_FREE_LIST(rot_obj_hooks, e_comp_object_intercept_hook_del);
3212    E_FREE_LIST(rot_pixmap_hooks, e_pixmap_hook_del);
3213
3214    if (rot_global)
3215      {
3216         wl_global_destroy(rot_global);
3217         rot_global = NULL;
3218      }
3219
3220    if (rot_idle_enterer)
3221      {
3222          ecore_idle_enterer_del(rot_idle_enterer);
3223          rot_idle_enterer = NULL;
3224      }
3225 }
3226
3227 EINTERN void
3228 e_mod_pol_rotation_force_update_add(E_Zone *zone EINA_UNUSED, E_Client *ec)
3229 {
3230    rot.force_update_list = eina_list_append(rot.force_update_list, ec);
3231 }
3232
3233 EINTERN void
3234 e_mod_pol_rotation_force_update_del(E_Zone *zone EINA_UNUSED, E_Client *ec)
3235 {
3236    rot.force_update_list = eina_list_remove(rot.force_update_list, ec);
3237 }