#include "e.h" static int _e_client_hooks_delete = 0; static int _e_client_hooks_walking = 0; E_API int E_EVENT_CLIENT_ADD = -1; E_API int E_EVENT_CLIENT_REMOVE = -1; E_API int E_EVENT_CLIENT_ZONE_SET = -1; E_API int E_EVENT_CLIENT_DESK_SET = -1; E_API int E_EVENT_CLIENT_RESIZE = -1; E_API int E_EVENT_CLIENT_MOVE = -1; E_API int E_EVENT_CLIENT_SHOW = -1; E_API int E_EVENT_CLIENT_HIDE = -1; E_API int E_EVENT_CLIENT_ICONIFY = -1; E_API int E_EVENT_CLIENT_UNICONIFY = -1; E_API int E_EVENT_CLIENT_STACK = -1; E_API int E_EVENT_CLIENT_FOCUS_IN = -1; E_API int E_EVENT_CLIENT_FOCUS_OUT = -1; E_API int E_EVENT_CLIENT_PROPERTY = -1; E_API int E_EVENT_CLIENT_FULLSCREEN = -1; E_API int E_EVENT_CLIENT_UNFULLSCREEN = -1; static Eina_Hash *clients_hash[2] = {NULL}; // pixmap->client static unsigned int focus_track_frozen = 0; static int warp_to = 0; static int warp_to_x = 0; static int warp_to_y = 0; static int warp_x[2] = {0}; //{cur,prev} static int warp_y[2] = {0}; //{cur,prev} static Ecore_Timer *warp_timer = NULL; static E_Client *focused = NULL; static E_Client *warp_client = NULL; static E_Client *ecmove = NULL; static E_Client *ecresize = NULL; static E_Client *action_client = NULL; static E_Drag *client_drag = NULL; static Eina_List *focus_stack = NULL; static Eina_List *raise_stack = NULL; static Eina_Bool comp_grabbed = EINA_FALSE; static Evas_Object *action_rect; static Eina_List *handlers = NULL; //static Eina_Bool client_grabbed = EINA_FALSE; static Ecore_Event_Handler *action_handler_key = NULL; static Ecore_Event_Handler *action_handler_mouse = NULL; static Ecore_Timer *action_timer = NULL; static Eina_Rectangle action_orig = {0, 0, 0, 0}; static E_Client_Layout_Cb _e_client_layout_cb = NULL; EINTERN void e_client_focused_set(E_Client *ec); static Eina_Inlist *_e_client_hooks[E_CLIENT_HOOK_LAST] = {NULL}; /////////////////////////////////////////// static Eina_Bool _e_client_cb_efreet_cache_update(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED) { const Eina_List *l; E_Client *ec; /* mark all clients for desktop/icon updates */ EINA_LIST_FOREACH(e_comp->clients, l, ec) { E_FREE_FUNC(ec->desktop, efreet_desktop_free); if (e_object_is_del(E_OBJECT(ec))) continue; ec->changes.icon = 1; EC_CHANGED(ec); } return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_client_cb_config_icon_theme(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED) { const Eina_List *l; E_Client *ec; /* mark all clients for desktop/icon updates */ EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (e_object_is_del(E_OBJECT(ec))) continue; ec->changes.icon = 1; EC_CHANGED(ec); } return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_client_cb_config_mode(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED) { const Eina_List *l; E_Client *ec; E_Layer layer; /* move fullscreen borders above everything */ if (e_config->mode.presentation) layer = E_LAYER_CLIENT_TOP; else if (!e_config->allow_above_fullscreen) layer = E_LAYER_CLIENT_FULLSCREEN; else return ECORE_CALLBACK_RENEW; EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (e_object_is_del(E_OBJECT(ec))) continue; if ((ec->fullscreen) || (ec->need_fullscreen)) { ec->fullscreen = 0; evas_object_layer_set(ec->frame, layer); ec->fullscreen = 1; } } return ECORE_CALLBACK_PASS_ON; } static Eina_Bool _e_client_cb_pointer_warp(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Pointer_Warp *ev) { if (ecmove) evas_object_move(ecmove->frame, ecmove->x + (ev->curr.x - ev->prev.x), ecmove->y + (ev->curr.y - ev->prev.y)); return ECORE_CALLBACK_RENEW; } static Eina_Bool _e_client_cb_desk_window_profile_change(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Desk_Window_Profile_Change *ev EINA_UNUSED) { const Eina_List *l; E_Client *ec; EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (e_object_is_del(E_OBJECT(ec))) continue; ec->e.fetch.profile = 1; EC_CHANGED(ec); } return ECORE_CALLBACK_RENEW; } static void _e_client_cb_drag_finished(E_Drag *drag, int dropped EINA_UNUSED) { E_Client *ec; ec = drag->data; UNREFD(ec, 1); e_object_unref(E_OBJECT(ec)); client_drag = NULL; } static void _e_client_desk_window_profile_wait_desk_delfn(void *data, void *obj) { E_Client *ec = data; E_Desk *desk = obj, *new_desk; const char *p; int i; if (e_object_is_del(E_OBJECT(ec))) return; ec->e.state.profile.wait_desk_delfn = NULL; eina_stringshare_replace(&ec->e.state.profile.wait, NULL); if (ec->e.state.profile.wait_desk) e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk)); ec->e.state.profile.wait_desk = NULL; ec->e.state.profile.wait_for_done = 0; if (!ec->e.state.profile.use) return; new_desk = e_comp_desk_window_profile_get(desk->window_profile); if (new_desk) e_client_desk_set(ec, new_desk); else { for (i = 0; i < ec->e.state.profile.num; i++) { p = ec->e.state.profile.available_list[i]; new_desk = e_comp_desk_window_profile_get(p); if (new_desk) { e_client_desk_set(ec, new_desk); break; } } } } //////////////////////////////////////////////// static Eina_Bool _e_client_pointer_warp_to_center_timer(void *data EINA_UNUSED) { if (warp_to && warp_client) { int x, y; double spd; ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); /* move hasn't happened yet */ if ((x == warp_x[1]) && (y == warp_y[1])) return EINA_TRUE; if ((abs(x - warp_x[0]) > 5) || (abs(y - warp_y[0]) > 5)) { /* User moved the mouse, so stop warping */ warp_to = 0; goto cleanup; } spd = e_config->pointer_warp_speed; warp_x[1] = x = warp_x[0]; warp_y[1] = y = warp_y[0]; warp_x[0] = (x * (1.0 - spd)) + (warp_to_x * spd); warp_y[0] = (y * (1.0 - spd)) + (warp_to_y * spd); if ((warp_x[0] == x) && (warp_y[0] == y)) { warp_x[0] = warp_to_x; warp_y[0] = warp_to_y; warp_to = 0; goto cleanup; } ecore_evas_pointer_warp(e_comp->ee, warp_x[0], warp_y[0]); return ECORE_CALLBACK_RENEW; } cleanup: E_FREE_FUNC(warp_timer, ecore_timer_del); if (warp_client) { warp_x[0] = warp_x[1] = warp_y[0] = warp_y[1] = -1; if (warp_client->modal) { warp_client = NULL; return ECORE_CALLBACK_CANCEL; } e_focus_event_mouse_in(warp_client); if (warp_client->iconic) { if (!warp_client->lock_user_iconify) e_client_uniconify(warp_client); } if (warp_client->shaded) { if (!warp_client->lock_user_shade) e_client_unshade(warp_client, warp_client->shade_dir); } if (!warp_client->lock_focus_out) { evas_object_focus_set(warp_client->frame, 1); e_client_focus_latest_set(warp_client); } warp_client = NULL; } return ECORE_CALLBACK_CANCEL; } //////////////////////////////////////////////// static void _e_client_hooks_clean(void) { Eina_Inlist *l; E_Client_Hook *ch; unsigned int x; for (x = 0; x < E_CLIENT_HOOK_LAST; x++) EINA_INLIST_FOREACH_SAFE(_e_client_hooks[x], l, ch) { if (!ch->delete_me) continue; _e_client_hooks[x] = eina_inlist_remove(_e_client_hooks[x], EINA_INLIST_GET(ch)); free(ch); } } static Eina_Bool _e_client_hook_call(E_Client_Hook_Point hookpoint, E_Client *ec) { E_Client_Hook *ch; e_object_ref(E_OBJECT(ec)); _e_client_hooks_walking++; EINA_INLIST_FOREACH(_e_client_hooks[hookpoint], ch) { if (ch->delete_me) continue; ch->func(ch->data, ec); if ((hookpoint != E_CLIENT_HOOK_DEL) && (hookpoint != E_CLIENT_HOOK_MOVE_END) && (hookpoint != E_CLIENT_HOOK_RESIZE_END) && (hookpoint != E_CLIENT_HOOK_FOCUS_UNSET) && e_object_is_del(E_OBJECT(ec))) break; } _e_client_hooks_walking--; if ((_e_client_hooks_walking == 0) && (_e_client_hooks_delete > 0)) _e_client_hooks_clean(); return !!e_object_unref(E_OBJECT(ec)); } /////////////////////////////////////////// static void _e_client_event_simple_free(void *d EINA_UNUSED, E_Event_Client *ev) { UNREFD(ev->ec, 3); e_object_unref(E_OBJECT(ev->ec)); free(ev); } static void _e_client_event_simple(E_Client *ec, int type) { E_Event_Client *ev; ev = E_NEW(E_Event_Client, 1); ev->ec = ec; REFD(ec, 3); e_object_ref(E_OBJECT(ec)); ecore_event_add(type, ev, (Ecore_End_Cb)_e_client_event_simple_free, NULL); } static void _e_client_event_property(E_Client *ec, int prop) { E_Event_Client_Property *ev; ev = E_NEW(E_Event_Client_Property, 1); ev->ec = ec; ev->property = prop; REFD(ec, 33); e_object_ref(E_OBJECT(ec)); ecore_event_add(E_EVENT_CLIENT_PROPERTY, ev, (Ecore_End_Cb)_e_client_event_simple_free, NULL); } static void _e_client_event_desk_set_free(void *d EINA_UNUSED, E_Event_Client_Desk_Set *ev) { UNREFD(ev->ec, 4); e_object_unref(E_OBJECT(ev->ec)); e_object_unref(E_OBJECT(ev->desk)); free(ev); } static void _e_client_event_zone_set_free(void *d EINA_UNUSED, E_Event_Client_Zone_Set *ev) { UNREFD(ev->ec, 5); e_object_unref(E_OBJECT(ev->ec)); e_object_unref(E_OBJECT(ev->zone)); free(ev); } //////////////////////////////////////////////// static int _e_client_action_input_win_del(void) { if (!comp_grabbed) return 0; comp_grabbed = 0; E_FREE_FUNC(action_rect, evas_object_del); e_comp_shape_queue(); e_comp_ungrab_input(1, 1); return 1; } static void _e_client_action_finish(void) { if (comp_grabbed) _e_client_action_input_win_del(); if (action_handler_key && action_client) evas_object_freeze_events_set(action_client->frame, 0); E_FREE_FUNC(action_timer, ecore_timer_del); E_FREE_FUNC(action_handler_key, ecore_event_handler_del); E_FREE_FUNC(action_handler_mouse, ecore_event_handler_del); if (action_client) { action_client->keyboard_resizing = 0; if (action_client->internal_elm_win) ecore_event_window_ignore_events(elm_win_window_id_get(action_client->internal_elm_win), 0); } action_client = NULL; } static void _e_client_mouse_action_end(E_Client *ec) { if (!ec->cur_mouse_action) return; if (ec->cur_mouse_action->func.end_mouse) ec->cur_mouse_action->func.end_mouse(E_OBJECT(ec), "", NULL); else if (ec->cur_mouse_action->func.end) ec->cur_mouse_action->func.end(E_OBJECT(ec), ""); E_FREE_FUNC(ec->cur_mouse_action, e_object_unref); } static void _e_client_revert_focus(E_Client *ec) { E_Client *pec; E_Desk *desk; if (stopping) return; if (!ec->focused) return; if (!ec->zone) return; desk = e_desk_current_get(ec->zone); if (ec->desk == desk) evas_object_focus_set(ec->frame, 0); if (ec->stack.prev) { ec->stack.focus_skip = 1; pec = e_client_stack_active_adjust(ec); ec->stack.focus_skip = 0; if ((pec != ec) && (!pec->iconic)) evas_object_focus_set(pec->frame, 1); else { if ((e_object_is_del(E_OBJECT(ec))) || (ec->iconic)) { Eina_Bool unlock = ec->lock_focus_out; ec->lock_focus_out = 1; pec = e_desk_last_focused_focus(desk); ec->lock_focus_out = unlock; } } } else if ((ec->parent) && (ec->parent->desk == desk) && (ec->parent->modal == ec)) { evas_object_focus_set(ec->parent->frame, 1); if (e_config->raise_on_revert_focus) evas_object_raise(ec->parent->frame); } else if (e_config->focus_revert_on_hide_or_close) { Eina_Bool unlock = ec->lock_focus_out; ec->lock_focus_out = 1; pec = e_desk_last_focused_focus(desk); ec->lock_focus_out = unlock; } else if (e_config->focus_policy == E_FOCUS_MOUSE) { pec = e_client_under_pointer_get(desk, ec); if (pec) evas_object_focus_set(pec->frame, 1); /* no autoraise/revert here because it's probably annoying */ } } static void _e_client_free(E_Client *ec) { if (ec->pixmap) { if (e_pixmap_free(ec->pixmap)) e_pixmap_client_set(ec->pixmap, NULL); ec->pixmap = NULL; } if (ec->frame) { e_comp_object_redirected_set(ec->frame, 0); e_comp_object_render_update_del(ec->frame); } E_OBJECT(ec)->references++; if (ec->fullscreen) { ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec); if (!ec->desk->fullscreen_clients) e_comp_render_queue(); } if (ec->new_client) e_comp->new_clients--; if (ec->e.state.profile.use) { e_client_desk_window_profile_wait_desk_set(ec, NULL); if (ec->e.state.profile.available_list) { int i; for (i = 0; i < ec->e.state.profile.num; i++) eina_stringshare_replace(&ec->e.state.profile.available_list[i], NULL); E_FREE(ec->e.state.profile.available_list); } ec->e.state.profile.num = 0; eina_stringshare_replace(&ec->e.state.profile.set, NULL); eina_stringshare_replace(&ec->e.state.profile.wait, NULL); eina_stringshare_replace(&ec->e.state.profile.name, NULL); ec->e.state.profile.wait_for_done = 0; ec->e.state.profile.use = 0; } if (ec->e.state.video_parent && ec->e.state.video_parent_client) { ec->e.state.video_parent_client->e.state.video_child = eina_list_remove(ec->e.state.video_parent_client->e.state.video_child, ec); } if (ec->e.state.video_child) { E_Client *tmp; EINA_LIST_FREE(ec->e.state.video_child, tmp) tmp->e.state.video_parent_client = NULL; } E_FREE_FUNC(ec->internal_elm_win, evas_object_del); E_FREE_FUNC(ec->desktop, efreet_desktop_free); E_FREE_FUNC(ec->post_job, ecore_idle_enterer_del); E_FREE_FUNC(ec->kill_timer, ecore_timer_del); E_FREE_LIST(ec->pending_resize, free); if (ec->remember) { E_Remember *rem; rem = ec->remember; ec->remember = NULL; e_remember_unuse(rem); } ec->group = eina_list_free(ec->group); ec->transients = eina_list_free(ec->transients); ec->stick_desks = eina_list_free(ec->stick_desks); E_FREE(ec->netwm.extra_types); eina_stringshare_replace(&ec->border.name, NULL); eina_stringshare_replace(&ec->bordername, NULL); eina_stringshare_replace(&ec->icccm.name, NULL); eina_stringshare_replace(&ec->icccm.class, NULL); eina_stringshare_replace(&ec->icccm.title, NULL); eina_stringshare_replace(&ec->icccm.icon_name, NULL); eina_stringshare_replace(&ec->icccm.machine, NULL); eina_stringshare_replace(&ec->icccm.window_role, NULL); if ((ec->icccm.command.argc > 0) && (ec->icccm.command.argv)) { int i; for (i = 0; i < ec->icccm.command.argc; i++) free(ec->icccm.command.argv[i]); E_FREE(ec->icccm.command.argv); } eina_stringshare_replace(&ec->netwm.name, NULL); eina_stringshare_replace(&ec->netwm.icon_name, NULL); eina_stringshare_replace(&ec->internal_icon, NULL); eina_stringshare_replace(&ec->internal_icon_key, NULL); eina_stringshare_replace(&ec->uuid, NULL); focus_stack = eina_list_remove(focus_stack, ec); raise_stack = eina_list_remove(raise_stack, ec); e_hints_client_list_set(); if (ec->e.state.profile.wait_desk) { e_object_delfn_del(E_OBJECT(ec->e.state.profile.wait_desk), ec->e.state.profile.wait_desk_delfn); ec->e.state.profile.wait_desk_delfn = NULL; e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk)); } if (ec->stack.prev) ec->stack.prev->stack.next = ec->stack.next; if (ec->stack.next) ec->stack.next->stack.prev = ec->stack.prev; ec->e.state.profile.wait_desk = NULL; evas_object_del(ec->frame); E_OBJECT(ec)->references--; free(ec); } static void _e_client_del(E_Client *ec) { E_Client *child; E_Client_Volume_Sink *sink; EINA_LIST_FREE(ec->sinks, sink) e_client_volume_sink_remove(ec, sink); for (child = ec->stack.next; child; child = child->stack.next) e_client_act_close_begin(child); ec->changed = 0; focus_stack = eina_list_remove(focus_stack, ec); raise_stack = eina_list_remove(raise_stack, ec); if (ec->exe_inst) { if (ec->exe_inst->phony && (eina_list_count(ec->exe_inst->clients) == 1)) { if (e_exec_phony_del(ec->exe_inst)) ec->exe_inst = NULL; } else { if (!ec->exe_inst->deleted) { ec->exe_inst->clients = eina_list_remove(ec->exe_inst->clients, ec); ec->exe_inst = NULL; } } } _e_client_mouse_action_end(ec); if (action_client == ec) _e_client_action_finish(); e_pointer_type_pop(e_comp->pointer, ec, NULL); if (warp_client == ec) { E_FREE_FUNC(warp_timer, ecore_timer_del); warp_client = NULL; } if ((client_drag) && (client_drag->data == ec)) { e_object_del(E_OBJECT(client_drag)); client_drag = NULL; } if (ec->border_menu) e_menu_deactivate(ec->border_menu); if (!stopping) { e_client_comp_hidden_set(ec, 1); if (ec->frame) evas_object_pass_events_set(ec->frame, 1); } E_FREE_FUNC(ec->border_locks_dialog, e_object_del); E_FREE_FUNC(ec->border_remember_dialog, e_object_del); E_FREE_FUNC(ec->border_border_dialog, e_object_del); E_FREE_FUNC(ec->border_prop_dialog, e_object_del); E_FREE_FUNC(ec->color_editor, evas_object_del); e_int_client_menu_del(ec); E_FREE_FUNC(ec->raise_timer, ecore_timer_del); if (ec->internal_elm_win) evas_object_hide(ec->internal_elm_win); if (ec->focused) _e_client_revert_focus(ec); if (ec->frame) evas_object_focus_set(ec->frame, 0); E_FREE_FUNC(ec->ping_poller, ecore_poller_del); eina_hash_del_by_key(clients_hash[e_pixmap_type_get(ec->pixmap)], &ec->pixmap); /* must be called before parent/child clear */ _e_client_hook_call(E_CLIENT_HOOK_DEL, ec); E_FREE(ec->comp_data); if ((!ec->new_client) && (!stopping)) _e_client_event_simple(ec, E_EVENT_CLIENT_REMOVE); if (ec->parent) { ec->parent->transients = eina_list_remove(ec->parent->transients, ec); ec->parent = NULL; } EINA_LIST_FREE(ec->transients, child) child->parent = NULL; if (ec->leader) { ec->leader->group = eina_list_remove(ec->leader->group, ec); if (ec->leader->modal == ec) ec->leader->modal = NULL; ec->leader = NULL; } EINA_LIST_FREE(ec->group, child) child->leader = NULL; e_comp->clients = eina_list_remove(e_comp->clients, ec); if (ec->frame) e_comp_object_render_update_del(ec->frame); } /////////////////////////////////////////// static Eina_Bool _e_client_cb_kill_timer(void *data) { E_Client *ec = data; // dont wait until it's hung - // if (ec->hung) // { if (ec->netwm.pid > 1) kill(ec->netwm.pid, SIGKILL); // } ec->kill_timer = NULL; return ECORE_CALLBACK_CANCEL; } static Eina_Bool _e_client_cb_ping_poller(void *data) { E_Client *ec; ec = data; if (ec->ping_ok) { if (ec->hung) { ec->hung = 0; evas_object_smart_callback_call(ec->frame, "unhung", NULL); E_FREE_FUNC(ec->kill_timer, ecore_timer_del); } } else { /* if time between last ping and now is greater * than half the ping interval... */ if ((ecore_loop_time_get() - ec->ping) > ((e_config->ping_clients_interval * ecore_poller_poll_interval_get(ECORE_POLLER_CORE)) / 2.0)) { if (!ec->hung) { ec->hung = 1; evas_object_smart_callback_call(ec->frame, "hung", NULL); /* FIXME: if below dialog is up - hide it now */ } if (ec->delete_requested) { /* FIXME: pop up dialog saying app is hung - kill client, or pid */ e_client_act_kill_begin(ec); } } } ec->ping_poller = NULL; e_client_ping(ec); return ECORE_CALLBACK_CANCEL; } /////////////////////////////////////////// static int _e_client_action_input_win_new(void) { if (comp_grabbed) { CRI("DOUBLE COMP GRAB! ACK!!!!"); return 1; } comp_grabbed = e_comp_grab_input(1, 1); if (!comp_grabbed) _e_client_action_input_win_del(); return comp_grabbed; } static void _e_client_action_event_grabber_mouse_up(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { Evas_Event_Mouse_Down *ev = event_info; E_Binding_Event_Mouse_Button ev2; if (!action_client) return; e_bindings_evas_event_mouse_button_convert(ev, &ev2); e_client_mouse_up(action_client, ev->button, &ev->output, &ev2); if (!action_client) return; if (action_client->moving) e_client_act_move_end(action_client, NULL); if (!action_client) return; if (e_client_util_resizing_get(action_client)) e_client_act_resize_end(action_client, NULL); } static void _e_client_action_event_grabber_mouse_move(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) { Evas_Event_Mouse_Move *ev = event_info; if (action_client) e_client_mouse_move(action_client, &ev->cur.output); } static void _e_client_action_event_grabber_init(E_Client *ec) { action_rect = e_comp_canvas_event_grabber_add(); evas_object_event_callback_add(action_rect, EVAS_CALLBACK_MOUSE_UP, _e_client_action_event_grabber_mouse_up, NULL); evas_object_event_callback_add(action_rect, EVAS_CALLBACK_MOUSE_MOVE, _e_client_action_event_grabber_mouse_move, NULL); evas_object_smart_member_add(ec->frame, action_rect); evas_object_resize(action_rect, e_comp->w, e_comp->h); evas_object_layer_set(action_rect, EVAS_LAYER_MAX - 100); evas_object_show(action_rect); evas_object_event_grabber_freeze_when_visible_set(action_rect, 1); e_comp_shape_queue(); } static void _e_client_action_init(E_Client *ec) { action_orig.x = ec->x; action_orig.y = ec->y; action_orig.w = ec->w; action_orig.h = ec->h; if (action_client) { action_client->keyboard_resizing = 0; if (action_client->internal_elm_win) ecore_event_window_ignore_events(elm_win_window_id_get(action_client->internal_elm_win), 0); } action_client = ec; if (ec->internal_elm_win) ecore_event_window_ignore_events(elm_win_window_id_get(ec->internal_elm_win), 1); } static void _e_client_action_restore_orig(E_Client *ec) { if (action_client != ec) return; evas_object_geometry_set(ec->frame, action_orig.x, action_orig.y, action_orig.w, action_orig.h); } static int _e_client_key_down_modifier_apply(int modifier, int value) { if (modifier & ECORE_EVENT_MODIFIER_CTRL) return value * 5; else if (modifier & ECORE_EVENT_MODIFIER_ALT) { value /= 5; if (value) return value; else return 1; } return value; } static int _e_client_move_begin(E_Client *ec) { if ((ec->fullscreen) || (ec->lock_user_location)) return 0; /* if (client_grabbed && !e_grabinput_get(e_client_util_pwin_get(ec), 0, e_client_util_pwin_get(ec))) { client_grabbed = 0; return 0; } */ if (!_e_client_action_input_win_new()) return 0; ec->moving = 1; ecmove = ec; if (!ec->lock_user_stacking) { if (e_config->border_raise_on_mouse_action) evas_object_raise(ec->frame); } _e_client_hook_call(E_CLIENT_HOOK_MOVE_BEGIN, ec); if (!ec->moving) { if (ecmove == ec) ecmove = NULL; _e_client_action_input_win_del(); return 0; } E_FREE_FUNC(ec->raise_timer, ecore_timer_del); _e_client_action_event_grabber_init(ec); return 1; } static int _e_client_move_end(E_Client *ec) { //if (client_grabbed) //{ //e_grabinput_release(e_client_util_pwin_get(ec), e_client_util_pwin_get(ec)); //client_grabbed = 0; //} _e_client_action_input_win_del(); e_pointer_mode_pop(ec, E_POINTER_MOVE); ec->moving = 0; _e_client_hook_call(E_CLIENT_HOOK_MOVE_END, ec); ecmove = NULL; return 1; } static Eina_Bool _e_client_action_move_timeout(void *data EINA_UNUSED) { _e_client_move_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_CANCEL; } static void _e_client_action_move_timeout_add(void) { double timeout = e_config->border_keyboard.timeout; E_FREE_FUNC(action_timer, ecore_timer_del); if (!EINA_DBL_NONZERO(timeout)) timeout = 5.0; action_timer = ecore_timer_loop_add(timeout, _e_client_action_move_timeout, NULL); } static Eina_Bool _e_client_move_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Event_Key *ev = event; int x, y, dx, dy; if (!comp_grabbed) return ECORE_CALLBACK_RENEW; if (!action_client) { ERR("no action_client!"); goto stop; } x = action_client->x; y = action_client->y; dx = e_config->border_keyboard.move.dx; dx = _e_client_key_down_modifier_apply(ev->modifiers, dx); dy = e_config->border_keyboard.move.dy; dy = _e_client_key_down_modifier_apply(ev->modifiers, dy); switch (e_util_key_geometry_action_get(ev->key, &x, &y, dx, dy)) { case E_UTIL_ACTION_DONE: goto stop; break; case E_UTIL_ACTION_ABORT: _e_client_action_restore_orig(action_client); goto stop; break; case E_UTIL_ACTION_DO: evas_object_move(action_client->frame, x, y); _e_client_action_move_timeout_add(); break; case E_UTIL_ACTION_NONE: default: break; } return ECORE_CALLBACK_PASS_ON; stop: _e_client_move_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_DONE; } static Eina_Bool _e_client_move_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED) { if (!comp_grabbed) return ECORE_CALLBACK_RENEW; if (!action_client) ERR("no action_client!"); _e_client_move_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_DONE; } static void _e_client_moveinfo_gather(E_Client *ec, const char *source) { if (e_util_glob_match(source, "mouse,*,1")) ec->moveinfo.down.button = 1; else if (e_util_glob_match(source, "mouse,*,2")) ec->moveinfo.down.button = 2; else if (e_util_glob_match(source, "mouse,*,3")) ec->moveinfo.down.button = 3; else ec->moveinfo.down.button = 0; if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) { ec->moveinfo.down.mx = ec->mouse.last_down[ec->moveinfo.down.button - 1].mx; ec->moveinfo.down.my = ec->mouse.last_down[ec->moveinfo.down.button - 1].my; } else { ec->moveinfo.down.mx = ec->mouse.current.mx; ec->moveinfo.down.my = ec->mouse.current.my; } } static void _e_client_resize_handle(E_Client *ec) { int x, y, w, h; int new_x, new_y, new_w, new_h; int tw, th; Eina_List *skiplist = NULL; if (e_comp->updating) return; x = ec->x; y = ec->y; w = ec->w; h = ec->h; if ((ec->resize_mode == E_POINTER_RESIZE_TR) || (ec->resize_mode == E_POINTER_RESIZE_R) || (ec->resize_mode == E_POINTER_RESIZE_BR)) { if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) w = ec->mouse.last_down[ec->moveinfo.down.button - 1].w + (ec->mouse.current.mx - ec->moveinfo.down.mx); else w = ec->moveinfo.down.w + (ec->mouse.current.mx - ec->moveinfo.down.mx); } else if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_L) || (ec->resize_mode == E_POINTER_RESIZE_BL)) { if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) w = ec->mouse.last_down[ec->moveinfo.down.button - 1].w - (ec->mouse.current.mx - ec->moveinfo.down.mx); else w = ec->moveinfo.down.w - (ec->mouse.current.mx - ec->moveinfo.down.mx); } if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_T) || (ec->resize_mode == E_POINTER_RESIZE_TR)) { if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) h = ec->mouse.last_down[ec->moveinfo.down.button - 1].h - (ec->mouse.current.my - ec->moveinfo.down.my); else h = ec->moveinfo.down.h - (ec->mouse.current.my - ec->moveinfo.down.my); } else if ((ec->resize_mode == E_POINTER_RESIZE_BL) || (ec->resize_mode == E_POINTER_RESIZE_B) || (ec->resize_mode == E_POINTER_RESIZE_BR)) { if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) h = ec->mouse.last_down[ec->moveinfo.down.button - 1].h + (ec->mouse.current.my - ec->moveinfo.down.my); else h = ec->moveinfo.down.h + (ec->mouse.current.my - ec->moveinfo.down.my); } tw = ec->w; th = ec->h; if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_L) || (ec->resize_mode == E_POINTER_RESIZE_BL)) x += (tw - w); if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_T) || (ec->resize_mode == E_POINTER_RESIZE_TR)) y += (th - h); skiplist = eina_list_append(skiplist, ec); e_resist_client_position(skiplist, ec->x, ec->y, ec->w, ec->h, x, y, w, h, &new_x, &new_y, &new_w, &new_h); eina_list_free(skiplist); w = new_w; h = new_h; if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE) { if (ec->zone) { w = MIN(w, ec->zone->w); h = MIN(h, ec->zone->h); } } e_client_resize_limit(ec, &new_w, &new_h); if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_L) || (ec->resize_mode == E_POINTER_RESIZE_BL)) new_x += (w - new_w); if ((ec->resize_mode == E_POINTER_RESIZE_TL) || (ec->resize_mode == E_POINTER_RESIZE_T) || (ec->resize_mode == E_POINTER_RESIZE_TR)) new_y += (h - new_h); evas_object_geometry_set(ec->frame, new_x, new_y, new_w, new_h); } static int _e_client_resize_end(E_Client *ec) { _e_client_action_input_win_del(); e_pointer_mode_pop(ec, ec->resize_mode); ec->resize_mode = E_POINTER_RESIZE_NONE; /* If this border was maximized, we need to unset Maximized state or * on restart, E still thinks it's maximized */ if (ec->maximized != E_MAXIMIZE_NONE) e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL, ec->maximized & E_MAXIMIZE_VERTICAL); _e_client_hook_call(E_CLIENT_HOOK_RESIZE_END, ec); ecresize = NULL; return 1; } static Eina_Bool _e_client_action_resize_timeout(void *data EINA_UNUSED) { _e_client_resize_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_CANCEL; } static void _e_client_action_resize_timeout_add(void) { double timeout = e_config->border_keyboard.timeout; E_FREE_FUNC(action_timer, ecore_timer_del); if (!EINA_DBL_NONZERO(timeout)) timeout = 5.0; action_timer = ecore_timer_loop_add(timeout, _e_client_action_resize_timeout, NULL); } static Eina_Bool _e_client_resize_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { Ecore_Event_Key *ev = event; int w, h, dx, dy; if (!comp_grabbed) return ECORE_CALLBACK_RENEW; if (!action_client) { ERR("no action_client!"); goto stop; } w = action_client->w; h = action_client->h; dx = e_config->border_keyboard.resize.dx; if (dx < action_client->icccm.step_w) dx = action_client->icccm.step_w; dx = _e_client_key_down_modifier_apply(ev->modifiers, dx); if (dx < action_client->icccm.step_w) dx = action_client->icccm.step_w; dy = e_config->border_keyboard.resize.dy; if (dy < action_client->icccm.step_h) dy = action_client->icccm.step_h; dy = _e_client_key_down_modifier_apply(ev->modifiers, dy); if (dy < action_client->icccm.step_h) dy = action_client->icccm.step_h; switch (e_util_key_geometry_action_get(ev->key, &w, &h, dx, dy)) { case E_UTIL_ACTION_DONE: goto stop; break; case E_UTIL_ACTION_ABORT: _e_client_action_restore_orig(action_client); goto stop; break; case E_UTIL_ACTION_DO: if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE) { if (action_client->zone) { w = MIN(w, action_client->zone->w); h = MIN(h, action_client->zone->h); } } e_client_resize_limit(action_client, &w, &h); evas_object_resize(action_client->frame, w, h); _e_client_action_resize_timeout_add(); break; case E_UTIL_ACTION_NONE: default: break; } return ECORE_CALLBACK_PASS_ON; stop: _e_client_resize_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_DONE; } static Eina_Bool _e_client_resize_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED) { if (!comp_grabbed) return ECORE_CALLBACK_RENEW; if (!action_client) ERR("no action_client!"); _e_client_resize_end(action_client); _e_client_action_finish(); return ECORE_CALLBACK_DONE; } //////////////////////////////////////////////// static E_Client * _e_client_under_pointer_helper(E_Desk *desk, E_Client *exclude, int x, int y) { E_Client *ec = NULL, *cec; E_CLIENT_REVERSE_FOREACH(cec) { /* If a border was specified which should be excluded from the list * (because it will be closed shortly for example), skip */ if (e_client_util_ignored_get(cec) || (!e_client_util_desk_visible(cec, desk))) continue; if (!evas_object_visible_get(cec->frame)) continue; if ((exclude) && (cec == exclude)) continue; if (!E_INSIDE(x, y, cec->x, cec->y, cec->w, cec->h)) continue; /* If the layer is higher, the position of the window is higher * (always on top vs always below) */ if (!ec || (cec->layer > ec->layer)) ec = cec; } return ec; } //////////////////////////////////////////////// static void _e_client_zones_layout_calc(E_Client *ec, int *zx, int *zy, int *zw, int *zh) { int x, y, w, h; E_Zone *zone_above, *zone_below, *zone_left, *zone_right; if (!ec->zone) return; x = ec->zone->x; y = ec->zone->y; w = ec->zone->w; h = ec->zone->h; if (eina_list_count(e_comp->zones) == 1) { if (zx) *zx = x; if (zy) *zy = y; if (zw) *zw = w; if (zh) *zh = h; return; } zone_left = e_comp_zone_xy_get((x - w + 5), y); zone_right = e_comp_zone_xy_get((x + w + 5), y); zone_above = e_comp_zone_xy_get(x, (y - h + 5)); zone_below = e_comp_zone_xy_get(x, (y + h + 5)); if (!(zone_above) && (y)) zone_above = e_comp_zone_xy_get(x, (h - 5)); if (!(zone_left) && (x)) zone_left = e_comp_zone_xy_get((x - 5), y); if (zone_right) w = zone_right->x + zone_right->w; if (zone_left) w = ec->zone->x + ec->zone->w; if (zone_below) h = zone_below->y + zone_below->h; if (zone_above) h = ec->zone->y + ec->zone->h; if ((zone_left) && (zone_right)) w = ec->zone->w + zone_right->x; if ((zone_above) && (zone_below)) h = ec->zone->h + zone_below->y; if (x) x -= ec->zone->w; if (y) y -= ec->zone->h; if (zx) *zx = x > 0 ? x : 0; if (zy) *zy = y > 0 ? y : 0; if (zw) *zw = w; if (zh) *zh = h; } static void _e_client_stay_within_canvas(E_Client *ec, int x, int y, int *new_x, int *new_y) { int new_x_max, new_y_max; int zw, zh; Eina_Bool lw, lh; if (!ec->zone) { if (new_x) *new_x = x; if (new_y) *new_y = y; return; } _e_client_zones_layout_calc(ec, NULL, NULL, &zw, &zh); new_x_max = zw - ec->w; new_y_max = zh - ec->h; lw = ec->w > zw ? EINA_TRUE : EINA_FALSE; lh = ec->h > zh ? EINA_TRUE : EINA_FALSE; if (lw) { if (x <= new_x_max) *new_x = new_x_max; else if (x >= 0) *new_x = 0; } else { if (x >= new_x_max) *new_x = new_x_max; else if (x <= 0) *new_x = 0; } if (lh) { if (y <= new_y_max) *new_y = new_y_max; else if (y >= 0) *new_y = 0; } else { if (y >= new_y_max) *new_y = new_y_max; else if (y <= 0) *new_y = 0; } } static void _e_client_reset_lost_window(E_Client *ec) { E_OBJECT_CHECK(ec); if (ec->during_lost) return; ec->during_lost = EINA_TRUE; if (ec->iconic) e_client_uniconify(ec); if (!ec->moving) e_comp_object_util_center(ec->frame); evas_object_raise(ec->frame); if (!ec->lock_focus_out) evas_object_focus_set(ec->frame, 1); e_client_pointer_warp_to_center(ec); ec->during_lost = EINA_FALSE; } static void _e_client_move_lost_window_to_center(E_Client *ec) { int loss_overlap = 5; int zw, zh, zx, zy; if (ec->during_lost) return; if (!ec->zone) return; _e_client_zones_layout_calc(ec, &zx, &zy, &zw, &zh); if (!E_INTERSECTS(zx + loss_overlap, zy + loss_overlap, zw - (2 * loss_overlap), zh - (2 * loss_overlap), ec->x, ec->y, ec->w, ec->h)) { if (e_config->edge_flip_dragging) { Eina_Bool lf, rf, tf, bf; lf = rf = tf = bf = EINA_TRUE; if (ec->zone->desk_x_count <= 1) lf = rf = EINA_FALSE; else if (!e_config->desk_flip_wrap) { if (ec->zone->desk_x_current == 0) lf = EINA_FALSE; if (ec->zone->desk_x_current == (ec->zone->desk_x_count - 1)) rf = EINA_FALSE; } if (ec->zone->desk_y_count <= 1) tf = bf = EINA_FALSE; else if (!e_config->desk_flip_wrap) { if (ec->zone->desk_y_current == 0) tf = EINA_FALSE; if (ec->zone->desk_y_current == (ec->zone->desk_y_count - 1)) bf = EINA_FALSE; } if (!(lf) && (ec->x <= loss_overlap) && !(ec->zone->flip.switching)) _e_client_reset_lost_window(ec); if (!(rf) && (ec->x >= (ec->zone->w - loss_overlap)) && !(ec->zone->flip.switching)) _e_client_reset_lost_window(ec); if (!(tf) && (ec->y <= loss_overlap) && !(ec->zone->flip.switching)) _e_client_reset_lost_window(ec); if (!(bf) && (ec->y >= (ec->zone->h - loss_overlap)) && !(ec->zone->flip.switching)) _e_client_reset_lost_window(ec); } if (!e_config->edge_flip_dragging) _e_client_reset_lost_window(ec); } } //////////////////////////////////////////////// static void _e_client_zone_update(E_Client *ec) { Eina_List *l; E_Zone *zone; /* still within old zone - leave it there */ if (ec->zone && E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h)) return; /* find a new zone */ EINA_LIST_FOREACH(e_comp->zones, l, zone) { if (E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, zone->x, zone->y, zone->w, zone->h)) { e_client_zone_set(ec, zone); return; } } } //////////////////////////////////////////////// E_API Eina_List * e_client_stack_list_prepare(E_Client *ec) { E_Client *ec2; Eina_List *list = NULL; for (ec2 = ec->stack.prev; ec2; ec2 = ec2->stack.prev) { ec2->stack.ignore++; list = eina_list_prepend(list, ec2); } ec->stack.ignore++; list = eina_list_append(list, ec); for (ec2 = ec->stack.next; ec2; ec2 = ec2->stack.next) { ec2->stack.ignore++; list = eina_list_append(list, ec2); } return list; } E_API void e_client_stack_list_finish(Eina_List *list) { E_Client *ec; EINA_LIST_FREE(list, ec) ec->stack.ignore--; } E_API E_Client * e_client_stack_top_get(E_Client *ec) { E_Client *ec2; for (ec2 = ec; ec2; ec2 = ec2->stack.next) { if (!ec2->stack.next) return ec2; } return ec; } E_API E_Client * e_client_stack_bottom_get(E_Client *ec) { E_Client *ec2; for (ec2 = ec; ec2; ec2 = ec2->stack.prev) { if (!ec2->stack.prev) return ec2; } return ec; } E_API E_Client * e_client_stack_active_adjust(E_Client *ec) { E_Client *pec = ec; if ((!ec->stack.prev) && (!ec->stack.next)) return ec; ec = e_client_stack_top_get(ec); for (; ec; ec = ec->stack.prev) { if (e_object_is_del(E_OBJECT(ec))) continue; if (ec->stack.focus_skip) continue; if (ec->iconic) continue; if (ec->visible) break; } if (!ec) ec = pec; return ec; } E_API Eina_Bool e_client_stack_focused_get(E_Client *ec) { E_Client *ec2; ec2 = e_client_stack_bottom_get(ec); for (; ec2; ec2 = ec2->stack.next) { if (ec2->focused) return EINA_TRUE; } return EINA_FALSE; } E_API Eina_Bool e_client_stack_iconified_get(E_Client *ec) { E_Client *ec2; ec2 = e_client_stack_bottom_get(ec); for (; ec2; ec2 = ec2->stack.next) { if (ec2->iconic) return EINA_TRUE; } return EINA_FALSE; } E_API Eina_Bool e_client_stack_urgent_get(E_Client *ec) { E_Client *ec2; ec2 = e_client_stack_bottom_get(ec); for (; ec2; ec2 = ec2->stack.next) { if (ec2->urgent) return EINA_TRUE; } return EINA_FALSE; } //////////////////////////////////////////////// static void _e_client_cb_evas_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { E_Client *ec = data; if (stopping) return; //ignore all of this if we're shutting down! if (e_object_is_del(data)) return; //client is about to die _e_client_mouse_action_end(ec); if (action_client == ec) _e_client_action_finish(); if (!evas_object_pass_events_get(ec->frame)) e_pointer_type_pop(e_comp->pointer, ec, NULL); if (!ec->hidden) { if (ec->focused) _e_client_revert_focus(ec); } ec->want_focus = ec->take_focus = 0; ec->post_show = 0; if (ec->new_client || ec->iconic) return; _e_client_event_simple(ec, E_EVENT_CLIENT_HIDE); } static void _e_client_cb_evas_shade_done(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { E_Client *ec = data; ec->shading = 0; ec->shaded = !(ec->shaded); ec->changes.shaded = 1; ec->changes.shading = 1; e_client_comp_hidden_set(ec, ec->shaded); } static void _e_client_cb_evas_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { E_Client *ec = data; Evas_Coord x, y; ec->pre_res_change.valid = 0; if (ec->internal_elm_win) { EC_CHANGED(ec); ec->changes.pos = 1; } _e_client_event_simple(ec, E_EVENT_CLIENT_MOVE); if (!ec->ignored) _e_client_zone_update(ec); evas_object_geometry_get(ec->frame, &x, &y, NULL, NULL); if (ec->stack.prev || ec->stack.next) { // do nothing - handled by idle enterer eval } else { if ((e_config->transient.move) && (ec->transients)) { Eina_List *list = eina_list_clone(ec->transients); E_Client *child; EINA_LIST_FREE(list, child) { if (child->placed) evas_object_move(child->frame, child->x + x - ec->pre_cb.x, child->y + y - ec->pre_cb.y); } } } if (ec->moving || (ecmove == ec)) _e_client_hook_call(E_CLIENT_HOOK_MOVE_UPDATE, ec); e_remember_update(ec); if (ec->fullscreen || (ec->maximized & E_MAXIMIZE_DIRECTION)) e_hints_window_size_set(ec); ec->pre_cb.x = x; ec->pre_cb.y = y; } static void _e_client_cb_evas_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { E_Client *ec = data; Evas_Coord x, y, w, h; ec->pre_res_change.valid = 0; _e_client_event_simple(ec, E_EVENT_CLIENT_RESIZE); evas_object_geometry_get(ec->frame, &x, &y, &w, &h); if (ec->stack.prev || ec->stack.next) { // do nothing - handled by idle enterer eval } else { if ((e_config->transient.resize) && (ec->transients)) { Eina_List *list = eina_list_clone(ec->transients); E_Client *child; EINA_LIST_FREE(list, child) { Evas_Coord nx, ny, nw, nh; if (!child->placed) continue; if ((ec->pre_cb.w > 0) && (ec->pre_cb.h > 0)) { nx = x + (((child->x - x) * w) / ec->pre_cb.w); ny = y + (((child->y - y) * h) / ec->pre_cb.h); nw = (child->w * w) / ec->pre_cb.w; nh = (child->h * h) / ec->pre_cb.h; nx += ((nw - child->w) / 2); ny += ((nh - child->h) / 2); evas_object_move(child->frame, nx, ny); } } } } if (e_client_util_resizing_get(ec) || (ecresize == ec)) _e_client_hook_call(E_CLIENT_HOOK_RESIZE_UPDATE, ec); e_remember_update(ec); if (ec->fullscreen || (ec->maximized & E_MAXIMIZE_DIRECTION)) e_hints_window_size_set(ec); ec->pre_cb.w = w; ec->pre_cb.h = h; } static void _e_client_cb_evas_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { _e_client_event_simple(data, E_EVENT_CLIENT_SHOW); } static void _e_client_cb_evas_restack(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { E_Client *ec = data; if (ec->layer_block) return; if (ec->stack.prev || ec->stack.next) { if (ec->stack.ignore == 0) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child; EINA_LIST_FOREACH(list, l, child) { if (child == ec) break; evas_object_stack_below(child->frame, ec->frame); } EINA_LIST_REVERSE_FOREACH(list, l, child) { if (child == ec) break; evas_object_stack_above(child->frame, ec->frame); } e_client_stack_list_finish(list); } } else { if (e_config->transient.raise && ec->transients) e_client_transients_restack(ec); } if (ec->unredirected_single) return; e_remember_update(ec); _e_client_event_simple(ec, E_EVENT_CLIENT_STACK); } //////////////////////////////////////////////// static void _e_client_maximize_done(void *data, E_Efx_Map_Data *emd EINA_UNUSED, Evas_Object *obj) { E_Client *ec = data; ec->maximize_override = 0; ec->agent = NULL; evas_object_del(obj); } static Eina_Bool _e_client_maximize_run(E_Client *ec, int x, int y, int w, int h) { int pw, ph; Eina_Bool disabled = EINA_FALSE; if (e_pixmap_size_get(ec->pixmap, &pw, &ph)) { e_comp_object_frame_wh_adjust(ec->frame, pw, ph, &pw, &ph); disabled = (w == pw) && (h == ph); } if ((!disabled) && e_config->window_maximize_animate && (!ec->maximize_anims_disabled) && (!starting) && (!ec->changes.need_maximize)) { evas_object_del(ec->agent); ec->agent = e_comp_object_agent_add(ec->frame); e_efx_resize(ec->agent, e_config->window_maximize_transition, E_EFX_POINT(x, y), w, h, e_config->window_maximize_time, _e_client_maximize_done, ec); return EINA_TRUE; } evas_object_geometry_set(ec->frame, x, y, w, h); return EINA_FALSE; } //////////////////////////////////////////////// static void _e_client_eval(E_Client *ec) { int rem_change = 0; int send_event = 1; unsigned int prop = 0; int zx = 0, zy = 0, zw = 0, zh = 0; if (e_object_is_del(E_OBJECT(ec))) { CRI("_e_client_eval(%p) with deleted border! - %d\n", ec, ec->new_client); ec->changed = 0; return; } if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_NEW_CLIENT, ec)) return; if ((ec->new_client) && (!e_client_util_ignored_get(ec)) && (ec->zone)) { _e_client_event_simple(ec, E_EVENT_CLIENT_ADD); e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh); /* enforce wm size hints for initial sizing */ if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE) { ec->w = MIN(ec->w, ec->zone->w); ec->h = MIN(ec->h, ec->zone->h); } e_client_resize_limit(ec, &ec->w, &ec->h); if (ec->re_manage && e_comp_object_frame_exists(ec->frame)) { int x = ec->x, y = ec->y; if (ec->x) e_comp_object_frame_xy_adjust(ec->frame, ec->x, 0, &ec->x, NULL); if (ec->y) e_comp_object_frame_xy_adjust(ec->frame, 0, ec->y, NULL, &ec->y); if ((x != ec->x) || (y != ec->y)) ec->changes.pos = 1; ec->placed = 1; ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y; } if ((ec->stack.prev) && (!ec->dialog)) { E_Client *ec2 = e_client_stack_bottom_get(ec); ec->stack.ignore++; evas_object_move(ec->frame, ec2->x, ec2->y); evas_object_resize(ec->frame, ec2->w, ec2->h); ec->stack.ignore--; } } if (ec->new_client && (!ec->override)) e_hints_window_init(ec); if ((!e_client_util_ignored_get(ec)) && ec->zone && ec->visible && (!ec->placed)) { e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh); if (ec->parent) { Eina_Bool centered = EINA_FALSE; if (ec->parent->zone != e_zone_current_get()) { e_client_zone_set(ec, ec->parent->zone); e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh); } if (evas_object_visible_get(ec->parent->frame)) { if ((!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh)) || (!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, ec->parent->x, ec->parent->y, ec->parent->w, ec->parent->h))) { int x, y; e_comp_object_util_center_pos_get(ec->parent->frame, &x, &y); if (E_CONTAINS(x, y, ec->w, ec->h, zx, zy, zw, zh)) { ec->x = x, ec->y = y; } else { x = ec->parent->x; y = ec->parent->y; if (!E_CONTAINS(x, y, ec->w, ec->h, zx, zy, zw, zh)) { e_comp_object_util_center_on(ec->frame, ec->parent->frame); centered = 1; } } ec->changes.pos = 1; } } else { e_comp_object_util_center_on(ec->frame, ec->parent->frame); centered = 1; } if (centered) //test for offscreen { if (!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh)) { if (ec->x < zx) ec->x = ec->parent->x; if (ec->y < zy) ec->y = ec->parent->y; if (ec->x + ec->w > zx + zw) ec->x = ec->parent->x + ec->parent->w - ec->w; if (ec->y + ec->h > zy + zh) ec->y = ec->parent->y + ec->parent->h - ec->h; ec->changes.pos = 1; } } ec->placed = 1; ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y; } #if 0 else if ((ec->leader) && (ec->dialog)) { /* TODO: Place in center of group */ } #endif else if (ec->dialog) { E_Client *trans_ec = NULL; if (ec->icccm.transient_for) trans_ec = e_pixmap_find_client(E_PIXMAP_TYPE_X, ec->icccm.transient_for); if (trans_ec) { // if transient for a window and not placed, center on // transient parent if found ec->x = trans_ec->x + ((trans_ec->w - ec->w) / 2); ec->y = trans_ec->y + ((trans_ec->h - ec->h) / 2); } else { ec->x = zx + ((zw - ec->w) / 2); ec->y = zy + ((zh - ec->h) / 2); } ec->changes.pos = 1; ec->placed = 1; ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y; } if (!ec->placed) { Eina_List *skiplist = NULL; int new_x, new_y, t = 0; E_Client *trans_ec = NULL; if (zw > ec->w) new_x = zx + (rand() % (zw - ec->w)); else new_x = zx; if (zh > ec->h) new_y = zy + (rand() % (zh - ec->h)); else new_y = zy; e_comp_object_frame_geometry_get(ec->frame, NULL, NULL, &t, NULL); if (ec->icccm.transient_for) trans_ec = e_pixmap_find_client(E_PIXMAP_TYPE_X, ec->icccm.transient_for); if (trans_ec) { // if transient for a window and not placed, center on // transient parent if found new_x = trans_ec->x + ((trans_ec->w - ec->w) / 2); new_y = trans_ec->y + ((trans_ec->h - ec->h) / 2); } else if ((e_config->window_placement_policy == E_WINDOW_PLACEMENT_SMART) || (e_config->window_placement_policy == E_WINDOW_PLACEMENT_ANTIGADGET)) { skiplist = eina_list_append(skiplist, ec); if (ec->transients) skiplist = eina_list_merge(skiplist, eina_list_clone(ec->transients)); if (ec->desk) e_place_desk_region_smart(ec->desk, skiplist, ec->x, ec->y, ec->w, ec->h, &new_x, &new_y); else e_place_zone_region_smart(ec->zone, skiplist, ec->x, ec->y, ec->w, ec->h, &new_x, &new_y); eina_list_free(skiplist); } else if (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL) { e_place_zone_manual(ec->zone, ec->w, t, &new_x, &new_y); } else { e_place_zone_cursor(ec->zone, ec->x, ec->y, ec->w, ec->h, t, &new_x, &new_y); } ec->x = new_x; ec->y = new_y; ec->changes.pos = 1; ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y; } else if (!E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh)) { /* If an ec is placed out of bound, fix it! */ ec->x = zx + ((zw - ec->w) / 2); ec->y = zy + ((zh - ec->h) / 2); ec->changes.pos = 1; } /* Recreate state */ if ((ec->e.state.centered) && ((!ec->remember) || ((ec->remember) && (!(ec->remember->apply & E_REMEMBER_APPLY_POS))))) { ec->x = zx + (zw - ec->w) / 2; ec->y = zy + (zh - ec->h) / 2; ec->changes.pos = 1; } /* if the explicit geometry request asks for the app to be * in another zone - well move it there */ { E_Zone *zone = NULL; int x, y; x = MAX(ec->x, 0); y = MAX(ec->y, 0); if ((!ec->re_manage) && ((ec->x != x) || (ec->y != y))) zone = e_comp_zone_xy_get(x, y); if (!zone) { zone = e_comp_zone_xy_get(ec->x + (ec->w / 2), ec->y + (ec->h / 2)); if (zone) { E_Zone *z2 = e_comp_zone_xy_get(ec->x, ec->y); if (z2 && (z2 != zone)) { size_t psz = 0; E_Zone *zf = z2; Eina_List *l; EINA_LIST_FOREACH(e_comp->zones, l, z2) { int w, h; x = ec->x, y = ec->y, w = ec->w, h = ec->h; E_RECTS_CLIP_TO_RECT(x, y, w, h, z2->x, z2->y, z2->w, z2->h); if (w * h == z2->w * z2->h) { /* client fully covering zone */ zf = z2; break; } if ((unsigned)(w * h) > psz) { psz = w * h; zf = z2; } } zone = zf; } } } if (!zone) zone = e_comp_zone_xy_get(ec->x, ec->y); if (!zone) zone = e_comp_zone_xy_get(ec->x + ec->w - 1, ec->y); if (!zone) zone = e_comp_zone_xy_get(ec->x + ec->w - 1, ec->y + ec->h - 1); if (!zone) zone = e_comp_zone_xy_get(ec->x, ec->y + ec->h - 1); if ((zone) && (zone != ec->zone)) e_client_zone_set(ec, zone); } } if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_NEW_CLIENT, ec)) return; /* effect changes to the window border itself */ if ((ec->changes.shading)) { /* show at start of unshade (but don't hide until end of shade) */ //if (ec->shaded) //ecore_x_window_raise(ec->win); ec->changes.shading = 0; send_event = 0; rem_change = 1; } if (ec->changes.shaded) send_event = 0; if ((ec->changes.shaded) && (ec->changes.pos) && (ec->changes.size)) { //if (ec->shaded) //ecore_x_window_lower(ec->win); //else //ecore_x_window_raise(ec->win); ec->changes.shaded = 0; rem_change = 1; } else if ((ec->changes.shaded) && (ec->changes.pos)) { //if (ec->shaded) //ecore_x_window_lower(ec->win); //else //ecore_x_window_raise(ec->win); ec->changes.size = 1; ec->changes.shaded = 0; rem_change = 1; } else if ((ec->changes.shaded) && (ec->changes.size)) { //if (ec->shaded) //ecore_x_window_lower(ec->win); //else //ecore_x_window_raise(ec->win); ec->changes.shaded = 0; rem_change = 1; } else if (ec->changes.shaded) { //if (ec->shaded) //ecore_x_window_lower(ec->win); //else //ecore_x_window_raise(ec->win); ec->changes.shaded = 0; rem_change = 1; } if (ec->changes.size) { ec->changes.size = 0; if ((!ec->shaded) && (!ec->shading)) evas_object_resize(ec->frame, ec->w, ec->h); rem_change = 1; prop |= E_CLIENT_PROPERTY_SIZE; } if (ec->changes.pos) { Eina_Bool placed = ec->placed; ec->changes.pos = 0; evas_object_move(ec->frame, ec->x, ec->y); if (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL) ec->placed = placed; rem_change = 1; prop |= E_CLIENT_PROPERTY_POS; } if (ec->changes.need_rescale) { e_client_rescale(ec); ec->changes.need_rescale = 0; } if (ec->changes.reset_gravity) { ec->changes.reset_gravity = 0; rem_change = 1; prop |= E_CLIENT_PROPERTY_GRAVITY; } if ((ec->changes.visible) && (ec->visible) && (ec->new_client) && (!ec->iconic)) { int x, y; ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); if ((!ec->placed) && (!ec->re_manage) && (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL) && (!((ec->icccm.transient_for != 0) || (ec->dialog))) && (!ecmove) && (!ecresize)) { /* Set this window into moving state */ ec->cur_mouse_action = e_action_find("window_move"); if (ec->next_mouse_action_ignore) ec->cur_mouse_action = NULL; if (ec->cur_mouse_action) { if ((!ec->cur_mouse_action->func.end_mouse) && (!ec->cur_mouse_action->func.end)) ec->cur_mouse_action = NULL; if (ec->cur_mouse_action) { int t; ec->x = x - (ec->w >> 1); e_comp_object_frame_geometry_get(ec->frame, NULL, NULL, &t, NULL); ec->y = y - (t >> 1); EC_CHANGED(ec); ec->changes.pos = 1; } } } evas_object_show(ec->frame); if (ec->cur_mouse_action) { ec->moveinfo.down.x = ec->x; ec->moveinfo.down.y = ec->y; ec->moveinfo.down.w = ec->w; ec->moveinfo.down.h = ec->h; ec->mouse.current.mx = x; ec->mouse.current.my = y; ec->moveinfo.down.button = 0; ec->moveinfo.down.mx = x; ec->moveinfo.down.my = y; e_object_ref(E_OBJECT(ec->cur_mouse_action)); ec->cur_mouse_action->func.go(E_OBJECT(ec), NULL); if (e_config->border_raise_on_mouse_action) evas_object_raise(ec->frame); evas_object_focus_set(ec->frame, 1); } if (evas_object_visible_get(ec->frame)) ec->changes.visible = 0; } else if ((ec->changes.visible) && (ec->new_client)) { ec->changes.visible = 0; if (!ec->iconic) _e_client_event_simple(ec, E_EVENT_CLIENT_HIDE); } if (ec->changes.icon) { if (!ec->new_client) E_FREE_FUNC(ec->desktop, efreet_desktop_free); if (ec->remember && ec->remember->prop.desktop_file) { Efreet_Desktop *d; const char *desktop = ec->remember->prop.desktop_file; d = efreet_desktop_get(desktop); if (!d) d = efreet_util_desktop_name_find(desktop); if (d) { efreet_desktop_free(ec->desktop); ec->desktop = d; } } if (!ec->desktop) { if (ec->steam.steam_game_id) { Efreet_Desktop *d; Eina_List *desks = efreet_util_desktop_name_glob_list("*"); EINA_LIST_FREE(desks, d) { if (!d->exec) continue; if (!strncmp(d->exec, "steam ", 6)) { const char *st = strstr(d->exec, "steam://rungameid/"); if (st) { st += strlen("steam://rungameid/"); unsigned int id = atoi(st); if (id == ec->steam.steam_game_id) ec->desktop = d; } } } } } if (!ec->desktop) { E_Exec_Instance *inst; inst = e_exec_startup_id_pid_instance_find(ec->netwm.startup_id, ec->netwm.pid); if (inst && inst->clients) { E_Client *ec2 = eina_list_data_get(inst->clients); if (ec2->netwm.pid == ec->netwm.pid) ec->desktop = inst->desktop; } else if (inst) ec->desktop = inst->desktop; if (ec->desktop) efreet_desktop_ref(ec->desktop); } if (!ec->desktop) { if (ec->internal && (ec->icccm.class && (!strncmp(ec->icccm.class, "e_fwin::", 8)))) ec->desktop = efreet_util_desktop_exec_find("enlightenment_filemanager"); } if (!ec->desktop) { if ((ec->icccm.name) || (ec->icccm.class)) ec->desktop = efreet_util_desktop_wm_class_find(ec->icccm.name, ec->icccm.class); } if (!ec->desktop && ec->icccm.command.argv && (ec->icccm.command.argc > 0)) { ec->desktop = efreet_util_desktop_exec_find(ec->icccm.command.argv[0]); } if (!ec->desktop) { // special case hacks for specific apps that just don't do things // right so we have to work around them if (ec->icccm.class && ec->icccm.name && (!strcmp(ec->icccm.class, "Steam")) && (!strcmp(ec->icccm.name, "Steam"))) { ec->desktop = efreet_util_desktop_file_id_find("steam.desktop"); } /* libreoffice and maybe others match window class with .desktop file name */ else if (ec->icccm.class) { char buf[4096] = {0}; snprintf(buf, sizeof(buf), "%s.desktop", ec->icccm.class); ec->desktop = efreet_util_desktop_file_id_find(buf); if (!ec->desktop) { char *s; strncpy(buf, ec->icccm.class, sizeof(buf) - 1); s = buf; eina_str_tolower(&s); if (strcmp(s, ec->icccm.class)) ec->desktop = efreet_util_desktop_exec_find(s); } } } if (!ec->desktop && ec->icccm.name) { /* this works for most cases as fallback. useful when app is run from a shell */ ec->desktop = efreet_util_desktop_exec_find(ec->icccm.name); } if (!ec->desktop && ec->parent) { E_Client *ec2 = ec->parent; if (ec2->desktop) { efreet_desktop_ref(ec2->desktop); ec->desktop = ec2->desktop; } } if (ec->desktop) { if (!ec->exe_inst) e_exec_phony(ec); if (!ec->exe_inst->desktop) { efreet_desktop_ref(ec->desktop); ec->exe_inst->desktop = ec->desktop; } } ec->changes.icon = !e_comp_object_frame_icon_update(ec->frame); prop |= E_CLIENT_PROPERTY_ICON; } if (ec->new_client) e_comp->new_clients--; ec->new_client = 0; ec->changed = ec->changes.pos || ec->changes.size || ec->changes.stack || ec->changes.prop || ec->changes.border || ec->changes.reset_gravity || ec->changes.shading || ec->changes.shaded || ec->changes.shape || ec->changes.shape_input || ec->changes.icon || ec->changes.internal_state || ec->changes.need_maximize || ec->changes.need_unmaximize; ec->changes.stack = 0; if ((!ec->input_only) && (!ec->iconic) && ((!ec->zone) || e_client_util_desk_visible(ec, e_desk_current_get(ec->zone))) && ((ec->take_focus) || (ec->want_focus))) { ec->take_focus = 0; if ((ec->stack.prev) && (!ec->stack.next) && (ec->stack.prev == e_client_focused_get())) { e_client_focus_set_with_pointer(ec); } else if ((e_config->focus_setting == E_FOCUS_NEW_WINDOW) || (ec->want_focus)) { ec->want_focus = 0; e_client_focus_set_with_pointer(ec); } else if (ec->dialog) { if ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) || ((e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED) && (ec->parent == e_client_focused_get()))) { e_client_focus_set_with_pointer(ec); } } else { /* focus window by default when it is the only one on desk */ E_Client *ec2 = NULL; Eina_List *l; EINA_LIST_FOREACH(focus_stack, l, ec2) { if (ec == ec2) continue; if ((!ec2->iconic) && (ec2->visible) && ((ec->desk == ec2->desk) || ec2->sticky)) break; } if (!ec2) { e_client_focus_set_with_pointer(ec); } } } else ec->take_focus = ec->want_focus = 0; if (ec->changes.need_maximize) { E_Maximize max = ec->maximized; ec->maximized = E_MAXIMIZE_NONE; e_client_maximize(ec, max); ec->changes.need_maximize = 0; } else if (ec->changes.need_unmaximize) { e_client_unmaximize(ec, ec->maximized); ec->changes.need_unmaximize = 0; } if (ec->need_fullscreen) { e_client_fullscreen(ec, e_config->fullscreen_policy); ec->need_fullscreen = 0; } if (rem_change) e_remember_update(ec); if (send_event && rem_change && prop) { _e_client_event_property(ec, prop); } _e_client_hook_call(E_CLIENT_HOOK_EVAL_END, ec); } static void _e_client_frame_update(E_Client *ec) { const char *bordername; ec->border.changed = 0; if ((!e_comp_object_frame_allowed(ec->frame)) && (!e_comp_object_frame_exists(ec->frame))) return; if (ec->fullscreen || ec->borderless) bordername = "borderless"; else if (ec->bordername) bordername = ec->bordername; else if (ec->mwm.borderless) bordername = "borderless"; else if (((ec->icccm.transient_for != 0) || (ec->dialog)) && (ec->icccm.min_w == ec->icccm.max_w) && (ec->icccm.min_h == ec->icccm.max_h)) bordername = "noresize_dialog"; else if ((ec->icccm.min_w == ec->icccm.max_w) && (ec->icccm.min_h == ec->icccm.max_h)) bordername = "noresize"; else if (ec->shaped) bordername = "shaped"; else if (e_pixmap_is_x(ec->pixmap) && ((!ec->icccm.accepts_focus) && (!ec->icccm.take_focus))) bordername = "nofocus"; else if (ec->urgent) bordername = "urgent"; else if (((ec->icccm.transient_for && (!ec->netwm.type)) || (ec->dialog)) && (e_pixmap_is_x(ec->pixmap))) bordername = "dialog"; else if (ec->netwm.state.modal) bordername = "modal"; else if ((ec->netwm.state.skip_taskbar) || (ec->netwm.state.skip_pager)) bordername = "skipped"; /* else if ((ec->internal) && (ec->icccm.class) && (!strncmp(ec->icccm.class, "e_fwin", 6))) bordername = "internal_fileman"; */ else bordername = e_config->theme_default_border_style; if (!bordername) bordername = "default"; e_client_border_set(ec, bordername); } //////////////////////////////////////////////// EINTERN void e_client_idler_before(void) { const Eina_List *l; E_Client *ec; if ((!eina_hash_population(clients_hash[0])) && (!eina_hash_population(clients_hash[1]))) return; EINA_LIST_FOREACH(e_comp->clients, l, ec) { Eina_Stringshare *title; // pass 1 - eval0. fetch properties on new or on change and // call hooks to decide what to do - maybe move/resize if (ec->ignored || (!ec->changed)) continue; if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_FETCH, ec)) continue; /* FETCH is hooked by the compositor to get client hints */ title = e_client_util_name_get(ec); if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_FETCH, ec)) continue; if (title != e_client_util_name_get(ec)) _e_client_event_property(ec, E_CLIENT_PROPERTY_TITLE); /* PRE_POST_FETCH calls e_remember apply for new client */ if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_POST_FETCH, ec)) continue; if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_FETCH, ec)) continue; if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_FRAME_ASSIGN, ec)) continue; if ((ec->border.changed) && (!ec->shaded) && (!e_client_is_stacking(ec)) && ((!ec->override) || ec->internal) && (!(((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN)))) _e_client_frame_update(ec); ec->border.changed = 0; _e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_FRAME_ASSIGN, ec); } E_CLIENT_FOREACH(ec) { if (ec->ignored) continue; // pass 2 - show windows needing show if ((ec->changes.visible) && (ec->visible) && (!ec->new_client) && (!ec->changes.pos) && (!ec->changes.size)) { evas_object_show(ec->frame); ec->changes.visible = !evas_object_visible_get(ec->frame); } if ((!ec->new_client) && (!e_client_util_ignored_get(ec)) && (!E_INSIDE(ec->x, ec->y, 0, 0, e_comp->w - 5, e_comp->h - 5)) && (!E_INSIDE(ec->x, ec->y, 0 - ec->w + 5, 0 - ec->h + 5, e_comp->w - 5, e_comp->h - 5)) ) { if (e_config->screen_limits != E_CLIENT_OFFSCREEN_LIMIT_ALLOW_FULL) { if (ec->parent) e_comp_object_util_center_on(ec->frame, ec->parent->frame); else _e_client_move_lost_window_to_center(ec); } } // handle window stack if (!ec->stack.prev && ec->stack.next) { if (ec->stack.ignore == 0) { Eina_List *ll, *list = e_client_stack_list_prepare(ec); E_Client *child, *bottom, *moving = NULL, *rel; int x, y; bottom = rel = e_client_stack_bottom_get(ec); EINA_LIST_FOREACH(list, ll, child) { if (child->moving) { moving = child; break; } } if (moving) { Evas_Coord ox, oy; evas_object_geometry_get(ec->frame, &ox, &oy, NULL, NULL); rel = moving; } EINA_LIST_FOREACH(list, ll, child) { if (moving) { if (child == moving) continue; } else if (child == bottom) continue; x = rel->x + ((rel->w - child->w) / 2); y = rel->y + ((rel->h - child->h) / 2); if ((x != child->x) || (y != child->y)) { child->x = x; child->y = y; child->pre_cb.x = x; child->pre_cb.y = y; child->changes.pos = 1; child->changed = 1; } } e_client_stack_list_finish(list); } } } if (_e_client_layout_cb) _e_client_layout_cb(); // pass 3 - hide windows needing hide and eval (main eval) E_CLIENT_FOREACH(ec) { if (ec->ignored || e_object_is_del(E_OBJECT(ec))) continue; if ((ec->changes.visible) && (!ec->visible)) { evas_object_hide(ec->frame); ec->changes.visible = 0; } if (ec->changed) _e_client_eval(ec); if ((ec->changes.visible) && (ec->visible) && (!ec->changed)) { evas_object_show(ec->frame); ec->changes.visible = !evas_object_visible_get(ec->frame); if (ec->changes.visible) EC_CHANGED(ec); if (!e_client_util_desk_visible(ec, e_desk_current_get(ec->zone))) evas_object_hide(ec->frame); } } } EINTERN Eina_Bool e_client_init(void) { clients_hash[0] = eina_hash_pointer_new(NULL); clients_hash[1] = eina_hash_pointer_new(NULL); E_LIST_HANDLER_APPEND(handlers, E_EVENT_POINTER_WARP, _e_client_cb_pointer_warp, NULL); E_LIST_HANDLER_APPEND(handlers, EFREET_EVENT_DESKTOP_CACHE_UPDATE, _e_client_cb_efreet_cache_update, NULL); E_LIST_HANDLER_APPEND(handlers, EFREET_EVENT_ICON_CACHE_UPDATE, _e_client_cb_efreet_cache_update, NULL); E_LIST_HANDLER_APPEND(handlers, E_EVENT_CONFIG_ICON_THEME, _e_client_cb_config_icon_theme, NULL); E_LIST_HANDLER_APPEND(handlers, E_EVENT_CONFIG_MODE_CHANGED, _e_client_cb_config_mode, NULL); E_LIST_HANDLER_APPEND(handlers, E_EVENT_DESK_WINDOW_PROFILE_CHANGE, _e_client_cb_desk_window_profile_change, NULL); E_EVENT_CLIENT_ADD = ecore_event_type_new(); E_EVENT_CLIENT_REMOVE = ecore_event_type_new(); E_EVENT_CLIENT_DESK_SET = ecore_event_type_new(); E_EVENT_CLIENT_ZONE_SET = ecore_event_type_new(); E_EVENT_CLIENT_RESIZE = ecore_event_type_new(); E_EVENT_CLIENT_MOVE = ecore_event_type_new(); E_EVENT_CLIENT_SHOW = ecore_event_type_new(); E_EVENT_CLIENT_HIDE = ecore_event_type_new(); E_EVENT_CLIENT_ICONIFY = ecore_event_type_new(); E_EVENT_CLIENT_UNICONIFY = ecore_event_type_new(); E_EVENT_CLIENT_STACK = ecore_event_type_new(); E_EVENT_CLIENT_FOCUS_IN = ecore_event_type_new(); E_EVENT_CLIENT_FOCUS_OUT = ecore_event_type_new(); E_EVENT_CLIENT_PROPERTY = ecore_event_type_new(); E_EVENT_CLIENT_FULLSCREEN = ecore_event_type_new(); E_EVENT_CLIENT_UNFULLSCREEN = ecore_event_type_new(); return (!!clients_hash[1]); } EINTERN void e_client_shutdown(void) { E_FREE_FUNC(clients_hash[0], eina_hash_free); E_FREE_FUNC(clients_hash[1], eina_hash_free); E_FREE_LIST(handlers, ecore_event_handler_del); e_int_client_menu_hooks_clear(); E_FREE_FUNC(warp_timer, ecore_timer_del); warp_client = NULL; } E_API void e_client_unignore(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->ignored) return; ec->ignored = 0; if (!e_client_util_ignored_get(ec)) { if (starting) focus_stack = eina_list_prepend(focus_stack, ec); else focus_stack = eina_list_append(focus_stack, ec); } _e_client_event_simple(ec, E_EVENT_CLIENT_ADD); _e_client_hook_call(E_CLIENT_HOOK_UNIGNORE, ec); } E_API E_Client * e_client_new(E_Pixmap *cp, int first_map, int internal) { E_Client *ec; E_Pixmap_Type ptype = e_pixmap_type_get(cp); if ((ptype != E_PIXMAP_TYPE_X) && (ptype != E_PIXMAP_TYPE_WL)) return NULL; if (eina_hash_find(clients_hash[ptype], &cp)) return NULL; ec = E_OBJECT_ALLOC(E_Client, E_CLIENT_TYPE, _e_client_free); if (!ec) return NULL; e_object_del_func_set(E_OBJECT(ec), E_OBJECT_CLEANUP_FUNC(_e_client_del)); ec->focus_policy_override = E_FOCUS_LAST; ec->w = 1; ec->h = 1; ec->internal = internal; ec->pixmap = cp; e_pixmap_client_set(cp, ec); ec->resize_mode = E_POINTER_RESIZE_NONE; ec->layer = E_LAYER_CLIENT_NORMAL; /* printf("##- ON MAP CLIENT 0x%x SIZE %ix%i %i:%i\n", * ec->win, ec->w, ec->h, att->x, att->y); */ /* FIXME: if first_map is 1 then we should ignore the first hide event * or ensure the window is already hidden and events flushed before we * create a border for it */ if (first_map) { // printf("##- FIRST MAP\n"); ec->re_manage = 1; // needed to be 1 for internal windw and on restart. // ec->ignore_first_unmap = 2; } ec->offer_resistance = 1; ec->new_client = 1; e_comp->new_clients++; if (!_e_client_hook_call(E_CLIENT_HOOK_NEW_CLIENT, ec)) { /* delete the above allocated object */ //e_object_del(E_OBJECT(ec)); return NULL; } ec->icccm.title = NULL; ec->icccm.name = NULL; ec->icccm.class = NULL; ec->icccm.icon_name = NULL; ec->icccm.machine = NULL; ec->icccm.min_w = 1; ec->icccm.min_h = 1; ec->icccm.max_w = 32767; ec->icccm.max_h = 32767; ec->icccm.base_w = 0; ec->icccm.base_h = 0; ec->icccm.step_w = -1; ec->icccm.step_h = -1; ec->icccm.min_aspect = 0.0; ec->icccm.max_aspect = 0.0; ec->netwm.pid = 0; ec->netwm.name = NULL; ec->netwm.icon_name = NULL; ec->netwm.desktop = 0; ec->netwm.state.modal = 0; ec->netwm.state.sticky = 0; ec->netwm.state.shaded = 0; ec->netwm.state.hidden = 0; ec->netwm.state.maximized_v = 0; ec->netwm.state.maximized_h = 0; ec->netwm.state.skip_taskbar = 0; ec->netwm.state.skip_pager = 0; ec->netwm.state.fullscreen = 0; ec->netwm.state.stacking = E_STACKING_NONE; ec->netwm.action.move = 0; ec->netwm.action.resize = 0; ec->netwm.action.minimize = 0; ec->netwm.action.shade = 0; ec->netwm.action.stick = 0; ec->netwm.action.maximized_h = 0; ec->netwm.action.maximized_v = 0; ec->netwm.action.fullscreen = 0; ec->netwm.action.change_desktop = 0; ec->netwm.action.close = 0; ec->netwm.opacity = 255; e_comp->clients = eina_list_append(e_comp->clients, ec); eina_hash_add(clients_hash[ptype], &ec->pixmap, ec); if (!ec->ignored) EC_CHANGED(ec); e_comp_object_client_add(ec); if (ec->override) _e_client_zone_update(ec); else if (!ec->desk) e_client_desk_set(ec, e_desk_current_get(e_zone_current_get())); if (!ec->re_manage) ec->placed = ec->changes.pos = 0; //ensure placement is run if (ec->frame) { evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_SHOW, _e_client_cb_evas_show, ec); evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_HIDE, _e_client_cb_evas_hide, ec); evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_MOVE, _e_client_cb_evas_move, ec); evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_RESIZE, _e_client_cb_evas_resize, ec); evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_RESTACK, _e_client_cb_evas_restack, ec); evas_object_smart_callback_add(ec->frame, "shade_done", _e_client_cb_evas_shade_done, ec); if (ec->override) evas_object_layer_set(ec->frame, E_LAYER_CLIENT_ABOVE); else evas_object_layer_set(ec->frame, E_LAYER_CLIENT_NORMAL); } if (!e_client_util_ignored_get(ec)) { if (starting) focus_stack = eina_list_prepend(focus_stack, ec); else focus_stack = eina_list_append(focus_stack, ec); } e_hints_client_list_set(); return ec; } E_API Eina_Bool e_client_desk_window_profile_available_check(E_Client *ec, const char *profile) { int i; E_OBJECT_CHECK_RETURN(ec, EINA_FALSE); E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, EINA_FALSE); EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE); EINA_SAFETY_ON_FALSE_RETURN_VAL(ec->e.state.profile.use, EINA_FALSE); if (ec->e.state.profile.num == 0) return EINA_TRUE; for (i = 0; i < ec->e.state.profile.num; i++) { if (!e_util_strcmp(ec->e.state.profile.available_list[i], profile)) return EINA_TRUE; } return EINA_FALSE; } E_API void e_client_desk_window_profile_wait_desk_set(E_Client *ec, E_Desk *desk) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); E_OBJECT_CHECK(desk); E_OBJECT_TYPE_CHECK(desk, E_DESK_TYPE); if (ec->e.state.profile.wait_desk == desk) return; if (ec->e.state.profile.wait_desk_delfn) { if (ec->e.state.profile.wait_desk) e_object_delfn_del(E_OBJECT(ec->e.state.profile.wait_desk), ec->e.state.profile.wait_desk_delfn); ec->e.state.profile.wait_desk_delfn = NULL; } if (ec->e.state.profile.wait_desk) e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk)); ec->e.state.profile.wait_desk = NULL; if (desk) { ec->e.state.profile.wait_desk_delfn = e_object_delfn_add(E_OBJECT(desk), _e_client_desk_window_profile_wait_desk_delfn, ec); } ec->e.state.profile.wait_desk = desk; if (ec->e.state.profile.wait_desk) e_object_ref(E_OBJECT(ec->e.state.profile.wait_desk)); } E_API void e_client_desk_set(E_Client *ec, E_Desk *desk) { E_Event_Client_Desk_Set *ev; E_Desk *old_desk; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); E_OBJECT_CHECK(desk); E_OBJECT_TYPE_CHECK(desk, E_DESK_TYPE); if (ec->desk == desk) return; if (ec->e.state.profile.use) { const char *profile = desk->window_profile; if (!profile) profile = elm_config_profile_get(); if (!profile) profile = "standard"; if (e_util_strcmp(ec->e.state.profile.name, profile)) { if (e_client_desk_window_profile_available_check(ec, profile)) { eina_stringshare_replace(&ec->e.state.profile.set, profile); eina_stringshare_replace(&ec->e.state.profile.wait, NULL); ec->e.state.profile.wait_for_done = 0; e_client_desk_window_profile_wait_desk_set(ec, desk); EC_CHANGED(ec); } } } if (ec->fullscreen) { if (ec->desk) ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec); desk->fullscreen_clients = eina_list_append(desk->fullscreen_clients, ec); } old_desk = ec->desk; ec->desk = desk; if (ec->frame) { e_comp_object_effect_unclip(ec->frame); e_comp_object_effect_set(ec->frame, NULL); } if (desk->visible || ec->sticky) { // force visibility if its a stack window going onto this desktop if (ec->stack.prev || ec->stack.next) ec->hidden = 0; if ((!ec->hidden) && (!ec->iconic)) evas_object_show(ec->frame); } else { ec->hidden = 1; evas_object_hide(ec->frame); } e_client_comp_hidden_set(ec, (!desk->visible) && (!ec->sticky)); e_client_zone_set(ec, desk->zone); e_hints_window_desktop_set(ec); if (old_desk) { ev = E_NEW(E_Event_Client_Desk_Set, 1); ev->ec = ec; UNREFD(ec, 4); e_object_ref(E_OBJECT(ec)); ev->desk = old_desk; e_object_ref(E_OBJECT(old_desk)); ecore_event_add(E_EVENT_CLIENT_DESK_SET, ev, (Ecore_End_Cb)_e_client_event_desk_set_free, NULL); if (old_desk->zone == ec->zone) { if (ec->maximized || ec->fullscreen) e_client_rescale(ec); } } if (ec->stack.prev || ec->stack.next) { if (ec->stack.ignore == 0) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child; EINA_LIST_FOREACH(list, l, child) { if (child == ec) break; e_client_desk_set(child, ec->desk); evas_object_stack_below(child->frame, ec->frame); } EINA_LIST_REVERSE_FOREACH(list, l, child) { if (child == ec) break; e_client_desk_set(child, ec->desk); evas_object_stack_above(child->frame, ec->frame); } e_client_stack_list_finish(list); } } else { if (e_config->transient.desktop) { E_Client *child; const Eina_List *l; EINA_LIST_FOREACH(ec->transients, l, child) e_client_desk_set(child, ec->desk); e_client_transients_restack(ec); } } e_remember_update(ec); _e_client_hook_call(E_CLIENT_HOOK_DESK_SET, ec); evas_object_smart_callback_call(ec->frame, "desk_change", ec); } E_API Eina_Bool e_client_comp_grabbed_get(void) { return comp_grabbed; } E_API E_Client * e_client_action_get(void) { return action_client; } E_API E_Client * e_client_warping_get(void) { return warp_client; } E_API Eina_List * e_clients_immortal_list(void) { const Eina_List *l; Eina_List *list = NULL; E_Client *ec; EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (ec->lock_life) list = eina_list_append(list, ec); } return list; } ////////////////////////////////////////////////////////// E_API void e_client_mouse_in(E_Client *ec, int x, int y) { if (comp_grabbed) return; if (warp_client && (ec != warp_client)) return; if (e_object_is_del(E_OBJECT(ec))) return; if (ec->desk && ec->desk->animate_count) return; ec->mouse.current.mx = x; ec->mouse.current.my = y; ec->mouse.in = 1; if ((!ec->iconic) && (!e_client_util_ignored_get(ec))) e_focus_event_mouse_in(ec); } E_API void e_client_mouse_out(E_Client *ec, int x, int y) { if (comp_grabbed) return; if (ec->desk && ec->desk->animate_count) return; if (e_pixmap_is_x(ec->pixmap) && E_INSIDE(x, y, ec->x, ec->y, ec->w, ec->h)) return; ec->mouse.current.mx = x; ec->mouse.current.my = y; ec->mouse.in = 0; if (ec->fullscreen) return; if (e_object_is_del(E_OBJECT(ec))) return; if ((!ec->iconic) && (!e_client_util_ignored_get(ec))) e_focus_event_mouse_out(ec); } E_API void e_client_mouse_wheel(E_Client *ec, Evas_Point *output, E_Binding_Event_Wheel *ev) { EINA_SAFETY_ON_NULL_RETURN(ec); if (action_client) return; ec->mouse.current.mx = output->x; ec->mouse.current.my = output->y; if ((!ec->cur_mouse_action) && (!e_client_util_ignored_get(ec))) e_bindings_wheel_event_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(ec), ev); } E_API void e_client_mouse_down(E_Client *ec, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev) { Eina_Bool did_act = EINA_FALSE; E_Client *pfocus; int player; EINA_SAFETY_ON_NULL_RETURN(ec); if (action_client || ec->iconic || e_client_util_ignored_get(ec)) return; if ((button >= 1) && (button <= 3)) { ec->mouse.last_down[button - 1].mx = output->x; ec->mouse.last_down[button - 1].my = output->y; ec->mouse.last_down[button - 1].x = ec->x; ec->mouse.last_down[button - 1].y = ec->y; ec->mouse.last_down[button - 1].w = ec->w; ec->mouse.last_down[button - 1].h = ec->h; } else { ec->moveinfo.down.x = ec->x; ec->moveinfo.down.y = ec->y; ec->moveinfo.down.w = ec->w; ec->moveinfo.down.h = ec->h; } ec->mouse.current.mx = output->x; ec->mouse.current.my = output->y; pfocus = e_client_focused_get(); player = ec->layer; if (!ec->cur_mouse_action) { ec->cur_mouse_action = e_bindings_mouse_down_event_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(ec), ev); if (ec->next_mouse_action_ignore) ec->cur_mouse_action = NULL; if (ec->cur_mouse_action) { did_act = EINA_TRUE; e_object_ref(E_OBJECT(ec->cur_mouse_action)); if (ec->internal) { int button_mask, i; Evas *e; e = evas_object_evas_get(ec->internal_elm_win); button_mask = evas_pointer_button_down_mask_get(e); for (i = 0; i < 32; i++) { if ((button_mask & (1 << i))) evas_event_feed_mouse_up(e, i + 1, EVAS_BUTTON_NONE, 0, NULL); } evas_event_feed_mouse_out(e, 0, NULL); } } } if ((!did_act) || (((pfocus == e_client_focused_get()) || (ec == e_client_focused_get())) && (ec->layer >= player))) e_focus_event_mouse_down(ec); if ((button >= 1) && (button <= 3)) { ec->mouse.last_down[button - 1].mx = output->x; ec->mouse.last_down[button - 1].my = output->y; ec->mouse.last_down[button - 1].x = ec->x; ec->mouse.last_down[button - 1].y = ec->y; ec->mouse.last_down[button - 1].w = ec->w; ec->mouse.last_down[button - 1].h = ec->h; } else { ec->moveinfo.down.x = ec->x; ec->moveinfo.down.y = ec->y; ec->moveinfo.down.w = ec->w; ec->moveinfo.down.h = ec->h; } ec->mouse.current.mx = output->x; ec->mouse.current.my = output->y; } E_API void e_client_mouse_up(E_Client *ec, int button, Evas_Point *output, E_Binding_Event_Mouse_Button* ev) { EINA_SAFETY_ON_NULL_RETURN(ec); if (ec->iconic || e_client_util_ignored_get(ec)) return; if ((button >= 1) && (button <= 3)) { ec->mouse.last_up[button - 1].mx = output->x; ec->mouse.last_up[button - 1].my = output->y; ec->mouse.last_up[button - 1].x = ec->x; ec->mouse.last_up[button - 1].y = ec->y; } ec->mouse.current.mx = output->x; ec->mouse.current.my = output->y; /* also we don't pass the same params that went in - then again that */ /* should be ok as we are just ending the action if it has an end */ if (ec->cur_mouse_action) _e_client_mouse_action_end(ec); else { if (!e_bindings_mouse_up_event_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(ec), ev)) e_focus_event_mouse_up(ec); } if ((button >= 1) && (button <= 3)) { ec->mouse.last_up[button - 1].mx = output->x; ec->mouse.last_up[button - 1].my = output->y; ec->mouse.last_up[button - 1].x = ec->x; ec->mouse.last_up[button - 1].y = ec->y; } ec->drag.start = 0; } E_API void e_client_mouse_move(E_Client *ec, Evas_Point *output) { EINA_SAFETY_ON_NULL_RETURN(ec); if (ec->iconic || e_client_util_ignored_get(ec)) return; if ((ec->mouse.current.mx == output->x) && (ec->mouse.current.my == output->y)) return; ec->mouse.current.mx = output->x; ec->mouse.current.my = output->y; if (ec->moving) { int x, y, new_x, new_y; int new_w, new_h; Eina_List *skiplist = NULL; if (action_handler_key) return; if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3)) { x = ec->mouse.last_down[ec->moveinfo.down.button - 1].x + (ec->mouse.current.mx - ec->moveinfo.down.mx); y = ec->mouse.last_down[ec->moveinfo.down.button - 1].y + (ec->mouse.current.my - ec->moveinfo.down.my); } else { x = ec->moveinfo.down.x + (ec->mouse.current.mx - ec->moveinfo.down.mx); y = ec->moveinfo.down.y + (ec->mouse.current.my - ec->moveinfo.down.my); } e_comp_object_frame_xy_adjust(ec->frame, x, y, &new_x, &new_y); skiplist = eina_list_append(skiplist, ec); e_resist_client_position(skiplist, ec->x, ec->y, ec->w, ec->h, x, y, ec->w, ec->h, &new_x, &new_y, &new_w, &new_h); eina_list_free(skiplist); if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE) _e_client_stay_within_canvas(ec, x, y, &new_x, &new_y); ec->shelf_fix.x = 0; ec->shelf_fix.y = 0; ec->shelf_fix.modified = 0; evas_object_move(ec->frame, new_x, new_y); } else if (e_client_util_resizing_get(ec)) { if (action_handler_key) return; _e_client_resize_handle(ec); } else if (ec->drag.start) { if ((ec->drag.x == -1) && (ec->drag.y == -1)) { ec->drag.x = output->x; ec->drag.y = output->y; } else if (ec->zone) { int dx, dy; dx = ec->drag.x - output->x; dy = ec->drag.y - output->y; if (((dx * dx) + (dy * dy)) > (e_config->drag_resist * e_config->drag_resist)) { /* start drag! */ if ( #ifndef HAVE_WAYLAND_ONLY ec->netwm.icons || #endif ec->desktop || ec->internal_icon) { Evas_Object *o = NULL; int x, y, w, h; const char *drag_types[] = { "enlightenment/border" }; REFD(ec, 1); e_object_ref(E_OBJECT(ec)); e_comp_object_frame_icon_geometry_get(ec->frame, &x, &y, &w, &h); client_drag = e_drag_new(output->x, output->y, drag_types, 1, ec, -1, NULL, _e_client_cb_drag_finished); client_drag->button_mask = evas_pointer_button_down_mask_get(e_comp->evas); e_drag_resize(client_drag, w, h); o = e_client_icon_add(ec, client_drag->evas); if (!o) { /* FIXME: fallback icon for drag */ o = evas_object_rectangle_add(client_drag->evas); evas_object_color_set(o, 255, 255, 255, 255); } e_drag_object_set(client_drag, o); e_drag_start(client_drag, output->x + (ec->drag.x - x), output->y + (ec->drag.y - y)); } ec->drag.start = 0; } } } } /////////////////////////////////////////////////////// E_API void e_client_res_change_geometry_save(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (ec->pre_res_change.valid) return; ec->pre_res_change.valid = 1; ec->pre_res_change.x = ec->x; ec->pre_res_change.y = ec->y; ec->pre_res_change.w = ec->w; ec->pre_res_change.h = ec->h; ec->pre_res_change.saved.x = ec->saved.x; ec->pre_res_change.saved.y = ec->saved.y; ec->pre_res_change.saved.w = ec->saved.w; ec->pre_res_change.saved.h = ec->saved.h; } E_API void e_client_res_change_geometry_restore(E_Client *ec) { struct { unsigned char valid E_BITFIELD; int x, y, w, h; struct { int x, y, w, h; } saved; } pre_res_change; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->pre_res_change.valid) return; if (ec->new_client) return; if (!ec->zone) return; memcpy(&pre_res_change, &ec->pre_res_change, sizeof(pre_res_change)); if (ec->fullscreen) { if ((eina_list_count(e_comp->zones) > 1) || (e_config->fullscreen_policy == E_FULLSCREEN_RESIZE)) evas_object_geometry_set(ec->frame, ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h); else { e_client_unfullscreen(ec); e_client_fullscreen(ec, e_config->fullscreen_policy); } } else if (ec->maximized != E_MAXIMIZE_NONE) { int x, y, w, h; if (e_client_maximize_geometry_get(ec, ec->maximized, &x, &y, &w, &h)) { Eina_Bool override = ec->maximize_override; ec->maximize_override = 1; evas_object_geometry_set(ec->frame, x, y, w, h); ec->maximize_override = override; } else { E_Maximize max = ec->maximized; e_client_unmaximize(ec, E_MAXIMIZE_BOTH); e_client_maximize(ec, max); } } else { int x, y, w, h, zx, zy, zw, zh; ec->saved.x = ec->pre_res_change.saved.x; ec->saved.y = ec->pre_res_change.saved.y; ec->saved.w = ec->pre_res_change.saved.w; ec->saved.h = ec->pre_res_change.saved.h; e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh); if (ec->saved.w > zw) ec->saved.w = zw; if ((ec->saved.x + ec->saved.w) > (zx + zw)) ec->saved.x = zx + zw - ec->saved.w; if (ec->saved.h > zh) ec->saved.h = zh; if ((ec->saved.y + ec->saved.h) > (zy + zh)) ec->saved.y = zy + zh - ec->saved.h; x = ec->pre_res_change.x; y = ec->pre_res_change.y; w = ec->pre_res_change.w; h = ec->pre_res_change.h; if (w > zw) w = zw; if (h > zh) h = zh; if ((x + w) > (zx + zw)) x = zx + zw - w; if ((y + h) > (zy + zh)) y = zy + zh - h; evas_object_move(ec->frame, x, y); if (w && h) { e_client_resize_limit(ec, &w, &h); evas_object_resize(ec->frame, w, h); } } memcpy(&ec->pre_res_change, &pre_res_change, sizeof(pre_res_change)); } E_API void e_client_rescale(E_Client *ec) { Eina_Bool shaded; int shade_dir; if (stopping) return; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (e_comp->updating) { ec->changes.need_rescale = 1; EC_CHANGED(ec); return; } ec->changes.need_rescale = 0; shaded = ec->shaded; shade_dir = ec->shade_dir; if (shaded) e_client_unshade(ec, shade_dir); ec->pre_res_change.valid = 0; e_client_res_change_geometry_save(ec); e_client_res_change_geometry_restore(ec); if (shaded) e_client_shade(ec, shade_dir); } E_API void e_client_zone_set(E_Client *ec, E_Zone *zone) { E_Event_Client_Zone_Set *ev; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); E_OBJECT_CHECK(zone); E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE); if (ec->zone == zone) return; /* if the window does not lie in the new zone, move it so that it does */ if (!E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, zone->x, zone->y, zone->w, zone->h)) { int x, y; if (ec->zone) { /* first guess -- get offset from old zone, and apply to new zone */ x = zone->x + (ec->x - ec->zone->x); y = zone->y + (ec->y - ec->zone->y); } else x = ec->x, y = ec->y; /* keep window from hanging off bottom and left */ if (x + ec->w > zone->x + zone->w) x += (zone->x + zone->w) - (x + ec->w); if (y + ec->h > zone->y + zone->h) y += (zone->y + zone->h) - (y + ec->h); /* make sure to and left are on screen (if the window is larger than the zone, it will hang off the bottom / right) */ if (x < zone->x) x = zone->x; if (y < zone->y) y = zone->y; if (!E_INTERSECTS(x, y, ec->w, ec->h, zone->x, zone->y, zone->w, zone->h)) { /* still not in zone at all, so just move it to closest edge */ if (x < zone->x) x = zone->x; if (x >= zone->x + zone->w) x = zone->x + zone->w - ec->w; if (y < zone->y) y = zone->y; if (y >= zone->y + zone->h) y = zone->y + zone->h - ec->h; } evas_object_move(ec->frame, x, y); } ec->zone = zone; if ((!ec->desk) || (ec->desk->zone != ec->zone)) e_client_desk_set(ec, e_desk_current_get(ec->zone)); ev = E_NEW(E_Event_Client_Zone_Set, 1); ev->ec = ec; REFD(ec, 5); e_object_ref(E_OBJECT(ec)); ev->zone = zone; e_object_ref(E_OBJECT(zone)); ecore_event_add(E_EVENT_CLIENT_ZONE_SET, ev, (Ecore_End_Cb)_e_client_event_zone_set_free, NULL); e_remember_update(ec); if (ec->maximized || ec->fullscreen) e_client_rescale(ec); } E_API void e_client_geometry_get(E_Client *ec, int *x, int *y, int *w, int *h) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (ec->frame) evas_object_geometry_get(ec->frame, x, y, w, h); else { if (x) *x = ec->x; if (y) *y = ec->y; if (w) *w = ec->w; if (h) *h = ec->h; } } E_API E_Client * e_client_above_get(const E_Client *ec) { unsigned int x; E_Client *ec2; EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL); if (EINA_INLIST_GET(ec)->next) //check current layer { EINA_INLIST_FOREACH(EINA_INLIST_GET(ec)->next, ec2) if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } if (ec->layer == E_LAYER_CLIENT_PRIO) return NULL; if (e_comp_canvas_client_layer_map(ec->layer) == 9999) return NULL; /* go up the layers until we find one */ for (x = e_comp_canvas_layer_map(ec->layer) + 1; x <= e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x++) { if (!e_comp->layers[x].clients) continue; EINA_INLIST_FOREACH(e_comp->layers[x].clients, ec2) if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } return NULL; } E_API E_Client * e_client_below_get(const E_Client *ec) { unsigned int x; E_Client *ec2; Eina_Inlist *l; E_OBJECT_CHECK_RETURN(ec, NULL); E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, NULL); EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL); if (EINA_INLIST_GET(ec)->prev) //check current layer { for (l = EINA_INLIST_GET(ec)->prev; l; l = l->prev) { ec2 = EINA_INLIST_CONTAINER_GET(l, E_Client);; if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } } if (ec->layer == E_LAYER_CLIENT_DESKTOP) return NULL; if (e_comp_canvas_client_layer_map(ec->layer) == 9999) return NULL; /* go down the layers until we find one */ x = e_comp_canvas_layer_map(ec->layer); if (x > 0) x--; for (; x >= e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x--) { if (!e_comp->layers[x].clients) continue; EINA_INLIST_REVERSE_FOREACH(e_comp->layers[x].clients, ec2) if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } return NULL; } E_API E_Client * e_client_bottom_get(void) { unsigned int x; for (x = e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x <= e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x++) { E_Client *ec2; if (!e_comp->layers[x].clients) continue; EINA_INLIST_FOREACH(e_comp->layers[x].clients, ec2) if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } return NULL; } E_API E_Client * e_client_top_get(void) { unsigned int x; for (x = e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x >= e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x--) { E_Client *ec2; if (!e_comp->layers[x].clients) continue; EINA_INLIST_REVERSE_FOREACH(e_comp->layers[x].clients, ec2) if (!e_object_is_del(E_OBJECT(ec2))) return ec2; } return NULL; } E_API unsigned int e_clients_count(void) { return eina_list_count(e_comp->clients); } /** * Set a callback which will be called just prior to updating the * move coordinates for a border */ E_API void e_client_move_intercept_cb_set(E_Client *ec, E_Client_Move_Intercept_Cb cb) { ec->move_intercept_cb = cb; } /////////////////////////////////////// E_API E_Client_Hook * e_client_hook_add(E_Client_Hook_Point hookpoint, E_Client_Hook_Cb func, const void *data) { E_Client_Hook *ch; EINA_SAFETY_ON_TRUE_RETURN_VAL(hookpoint >= E_CLIENT_HOOK_LAST, NULL); ch = E_NEW(E_Client_Hook, 1); if (!ch) return NULL; ch->hookpoint = hookpoint; ch->func = func; ch->data = (void*)data; _e_client_hooks[hookpoint] = eina_inlist_append(_e_client_hooks[hookpoint], EINA_INLIST_GET(ch)); return ch; } E_API void e_client_hook_del(E_Client_Hook *ch) { ch->delete_me = 1; if (_e_client_hooks_walking == 0) { _e_client_hooks[ch->hookpoint] = eina_inlist_remove(_e_client_hooks[ch->hookpoint], EINA_INLIST_GET(ch)); free(ch); } else _e_client_hooks_delete++; } /////////////////////////////////////// E_API void e_client_focus_latest_set(E_Client *ec) { if (!ec) CRI("ACK"); if (focus_track_frozen > 0) return; focus_stack = eina_list_remove(focus_stack, ec); focus_stack = eina_list_prepend(focus_stack, ec); } E_API void e_client_raise_latest_set(E_Client *ec) { if (!ec) CRI("ACK"); raise_stack = eina_list_remove(raise_stack, ec); raise_stack = eina_list_prepend(raise_stack, ec); } E_API Eina_Bool e_client_focus_track_enabled(void) { return !focus_track_frozen; } E_API void e_client_focus_track_freeze(void) { focus_track_frozen++; } E_API void e_client_focus_track_thaw(void) { if (focus_track_frozen) focus_track_frozen--; } E_API void e_client_refocus(void) { E_Client *ec; const Eina_List *l; EINA_LIST_FOREACH(e_client_focus_stack_get(), l, ec) if (ec->desk && ec->desk->visible && (!ec->iconic)) { if (e_comp->input_key_grabs || e_comp->input_mouse_grabs) break; evas_object_focus_set(ec->frame, 1); break; } } /* * Sets the focus to the given client if necessary * There are 3 cases of different focus_policy-configurations: * * - E_FOCUS_CLICK: just set the focus, the most simple one * * - E_FOCUS_MOUSE: focus is where the mouse is, so try to * warp the pointer to the window. If this fails (because * the pointer is already in the window), just set the focus. * * - E_FOCUS_SLOPPY: focus is where the mouse is or on the * last window which was focused, if the mouse is on the * desktop. So, we need to look if there is another window * under the pointer and warp to pointer to the right * one if so (also, we set the focus afterwards). In case * there is no window under pointer, the pointer is on the * desktop and so we just set the focus. * * * This function is to be called when setting the focus was not * explicitly triggered by the user (by moving the mouse or * clicking for example), but implicitly (by closing a window, * the last focused window should get focus). * */ E_API void e_client_focus_set_with_pointer(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); /* note: this is here as it seems there are enough apps that do not even * expect us to emulate a look of focus but not actually set x input * focus as we do - so simply abort any focuse set on such windows */ if (e_pixmap_is_x(ec->pixmap)) { /* be strict about accepting focus hint */ if ((!ec->icccm.accepts_focus) && (!ec->icccm.take_focus)) return; } ec = e_client_stack_active_adjust(ec); if (ec->lock_focus_out) return; if (ec == focused) return; evas_object_focus_set(ec->frame, 1); if (e_config->focus_policy == E_FOCUS_CLICK) return; if (!ec->visible) return; if (e_config->focus_policy == E_FOCUS_SLOPPY) { E_Client *pec; pec = e_client_under_pointer_get(ec->desk, ec); /* Do not slide pointer when disabled (probably breaks focus * on sloppy/mouse focus but requested by users). */ if (e_config->pointer_slide && pec && (pec != ec)) e_client_pointer_warp_to_center(ec); } else { if (e_config->pointer_slide) e_client_pointer_warp_to_center(ec); } } static Eina_Bool _e_client_is_in_parents(E_Client *ec, E_Client *ec_find) { if (ec == ec_find) return EINA_TRUE; if (e_object_is_del(E_OBJECT(ec))) return EINA_FALSE; if (ec->parent) return _e_client_is_in_parents(ec->parent, ec_find); return EINA_FALSE; } EINTERN void e_client_focused_set(E_Client *ec) { E_Client *ec2, *ec_unfocus = focused; Eina_List *l, *ll; if (ec == focused) return; focused = ec; if ((ec) && (ec->zone)) { ec->focused = 1; e_client_urgent_set(ec, 0); if (!e_config->allow_above_fullscreen) { int x, total = ec->zone->desk_x_count * ec->zone->desk_y_count; for (x = 0; x < total; x++) { E_Desk *desk = ec->zone->desks[x]; /* if there's any fullscreen non-parents on this desk, unfullscreen them */ EINA_LIST_FOREACH_SAFE(desk->fullscreen_clients, l, ll, ec2) { if (ec2 == ec) continue; if (e_object_is_del(E_OBJECT(ec2))) continue; /* but only if it's the same desk or one of the clients is sticky */ if ((desk == ec->desk) || (ec->sticky || ec2->sticky)) { if (!_e_client_is_in_parents(ec, ec2)) e_client_unfullscreen(ec2); } } } } } while ((ec_unfocus) && (ec_unfocus->zone)) { Eina_Bool is_popup, is_child; ec_unfocus->want_focus = ec_unfocus->focused = 0; if (!e_object_is_del(E_OBJECT(ec_unfocus))) e_focus_event_focus_out(ec_unfocus); if (ec_unfocus->mouse.in && ec && (!e_client_util_is_popup(ec)) && (e_config->focus_policy != E_FOCUS_CLICK) && e_config->pointer_slide) e_client_mouse_out(ec_unfocus, ec_unfocus->x - 1, ec_unfocus->y - 1); E_FREE_FUNC(ec_unfocus->raise_timer, ecore_timer_del); is_popup = EINA_FALSE; is_child = EINA_FALSE; if ((ec) && ((ec->netwm.type == E_WINDOW_TYPE_MENU) || (ec->netwm.type == E_WINDOW_TYPE_TOOLBAR) || (ec->netwm.type == E_WINDOW_TYPE_DOCK) || (ec->netwm.type == E_WINDOW_TYPE_SPLASH) || (ec->netwm.type == E_WINDOW_TYPE_TOOLTIP) || (ec->netwm.type == E_WINDOW_TYPE_DROPDOWN_MENU) || (ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU) || (ec->netwm.type == E_WINDOW_TYPE_COMBO) || (ec->netwm.type == E_WINDOW_TYPE_DND) || (ec->netwm.type == E_WINDOW_TYPE_NOTIFICATION))) is_popup = EINA_TRUE; if ((ec) && _e_client_is_in_parents(ec, ec_unfocus)) is_child = EINA_TRUE; /* if there unfocus client is fullscreen and visible */ if ((!is_popup) && (!is_child) && (!e_config->allow_above_fullscreen) && (ec_unfocus->fullscreen) && (!ec_unfocus->iconic) && (!ec_unfocus->hidden) && (ec_unfocus->zone == e_zone_current_get()) && ((ec_unfocus->desk == e_desk_current_get(ec_unfocus->zone)) || (ec_unfocus->sticky))) { Eina_Bool have_vis_child = EINA_FALSE; /* if any of its children are visible */ EINA_LIST_FOREACH(ec_unfocus->transients, l, ec2) { if ((ec2->zone == ec_unfocus->zone) && ((ec2->desk == ec_unfocus->desk) || (ec2->sticky) || (ec_unfocus->sticky))) { have_vis_child = EINA_TRUE; break; } } /* if no children are visible, unfullscreen */ if ((!e_object_is_del(E_OBJECT(ec_unfocus))) && (!have_vis_child)) e_client_unfullscreen(ec_unfocus); } _e_client_hook_call(E_CLIENT_HOOK_FOCUS_UNSET, ec_unfocus); /* only send event here if we're not being deleted */ if ((!e_object_is_del(E_OBJECT(ec_unfocus))) && (e_object_ref_get(E_OBJECT(ec_unfocus)) > 0)) { _e_client_event_simple(ec_unfocus, E_EVENT_CLIENT_FOCUS_OUT); e_client_urgent_set(ec_unfocus, ec_unfocus->icccm.urgent); } break; } if (!ec) return; _e_client_hook_call(E_CLIENT_HOOK_FOCUS_SET, ec); e_focus_event_focus_in(ec); if (!focus_track_frozen) e_client_focus_latest_set(ec); e_hints_active_window_set(ec); _e_client_event_simple(ec, E_EVENT_CLIENT_FOCUS_IN); if (ec->sticky && ec->desk && (!ec->desk->visible)) e_client_desk_set(ec, e_desk_current_get(ec->zone)); } E_API void e_client_activate(E_Client *ec, Eina_Bool just_do_it) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); ec = e_client_stack_active_adjust(ec); if ((e_config->focus_setting == E_FOCUS_NEW_WINDOW) || ((ec->parent) && ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) || ((ec->parent->focused) && (e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED)))) || (just_do_it)) { if (ec->iconic) { if (e_config->clientlist_warp_to_iconified_desktop == 1) e_desk_show(ec->desk); if (!ec->lock_user_iconify) e_client_uniconify(ec); } if ((!ec->iconic) && ((!ec->sticky) || e_config->focus_revert_allow_sticky)) { int val = e_config->focus_last_focused_per_desktop; /* prevent infinite focus loops during refocus */ if (!ec->lock_focus_out) e_config->focus_last_focused_per_desktop = 0; e_desk_show(ec->desk); e_config->focus_last_focused_per_desktop = val; } if (!ec->lock_user_stacking) evas_object_raise(ec->frame); if (ec->shaded || ec->shading) e_client_unshade(ec, ec->shade_dir); if (!ec->lock_focus_out) { /* XXX ooffice does send this request for config dialogs when the main window gets focus. causing the pointer to jump back and forth. */ if ((e_config->focus_policy != E_FOCUS_CLICK) && (!ec->new_client) && (!e_config->disable_all_pointer_warps) && (!e_util_strcmp(ec->icccm.name, "VCLSalFrame"))) ecore_evas_pointer_warp(e_comp->ee, ec->x + (ec->w / 2), ec->y + (ec->h / 2)); evas_object_focus_set(ec->frame, 1); } } } E_API E_Client * e_client_focused_get(void) { return focused; } E_API Eina_List * e_client_focus_stack_get(void) { return focus_stack; } YOLO E_API void e_client_focus_stack_set(Eina_List *l) { focus_stack = l; } E_API Eina_List * e_client_raise_stack_get(void) { return raise_stack; } E_API Eina_List * e_client_lost_windows_get(E_Zone *zone) { Eina_List *list = NULL; const Eina_List *l; E_Client *ec; int loss_overlap = 5; E_OBJECT_CHECK_RETURN(zone, NULL); E_OBJECT_TYPE_CHECK_RETURN(zone, E_ZONE_TYPE, NULL); EINA_LIST_FOREACH(e_comp->clients, l, ec) { if (ec->zone != zone) continue; if (e_client_util_ignored_get(ec)) continue; if (!E_INTERSECTS(ec->zone->x + loss_overlap, ec->zone->y + loss_overlap, ec->zone->w - (2 * loss_overlap), ec->zone->h - (2 * loss_overlap), ec->x, ec->y, ec->w, ec->h)) { list = eina_list_append(list, ec); } } return list; } /////////////////////////////////////// E_API void e_client_shade(E_Client *ec, E_Direction dir) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (((!ec->shaded) && ec->shading) || (ec->shaded && (!ec->shading)) || (ec->fullscreen) || ((ec->maximized) && (!e_config->allow_manip))) return; if (!e_util_strcmp("borderless", ec->bordername)) return; if (!e_comp_object_frame_allowed(ec->frame)) return; e_hints_window_shaded_set(ec, 1); e_hints_window_shade_direction_set(ec, dir); ec->take_focus = 0; ec->shading = 1; ec->shade_dir = dir; if (e_config->border_shade_animate && (!ec->new_client)) { ec->changes.shading = 1; EC_CHANGED(ec); evas_object_smart_callback_call(ec->frame, "shading", (uintptr_t*)dir); } else evas_object_smart_callback_call(ec->frame, "shaded", (uintptr_t*)dir); e_remember_update(ec); } E_API void e_client_unshade(E_Client *ec, E_Direction dir) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if ((!ec->shaded) || (ec->shading)) return; e_hints_window_shaded_set(ec, 0); e_hints_window_shade_direction_set(ec, dir); ec->shading = 1; ec->shade_dir = 0; if (e_config->border_shade_animate) { ec->changes.shading = 1; EC_CHANGED(ec); evas_object_smart_callback_call(ec->frame, "unshading", (uintptr_t*)dir); } else evas_object_smart_callback_call(ec->frame, "unshaded", (uintptr_t*)dir); e_remember_update(ec); } /////////////////////////////////////// E_API Eina_Bool e_client_maximize_geometry_get(const E_Client *ec, E_Maximize max, int *mx, int *my, int *mw, int *mh) { int x1, yy1, x2, y2; int x = 0, y = 0, w, h, pw, ph; int zx, zy, zw, zh; int ecx, ecy, ecw, ech; if (e_client_util_ignored_get(ec)) return EINA_FALSE; zx = zy = zw = zh = 0; switch (max & E_MAXIMIZE_TYPE) { case E_MAXIMIZE_FULLSCREEN: w = ec->zone->w; h = ec->zone->h; e_client_resize_limit(ec, &w, &h); /* center x-direction */ x1 = ec->zone->x + (ec->zone->w - w) / 2; /* center y-direction */ yy1 = ec->zone->y + (ec->zone->h - h) / 2; switch (max & E_MAXIMIZE_DIRECTION) { case E_MAXIMIZE_BOTH: x = x1, y = yy1; break; case E_MAXIMIZE_VERTICAL: x = ec->x, y = yy1, w = ec->w; break; case E_MAXIMIZE_HORIZONTAL: x = x1, y = ec->y, h = ec->h; break; case E_MAXIMIZE_LEFT: x = ec->zone->x, y = ec->zone->y, w /= 2; break; case E_MAXIMIZE_RIGHT: x = x1, y = ec->zone->y, w /= 2; break; } if (mx) *mx = x; if (my) *my = y; if (mw) *mw = w; if (mh) *mh = h; break; case E_MAXIMIZE_SMART: case E_MAXIMIZE_EXPAND: e_zone_desk_useful_geometry_get(ec->zone, ec->desk, &zx, &zy, &zw, &zh); w = zw, h = zh; e_comp_object_frame_xy_unadjust(ec->frame, ec->x, ec->y, &ecx, &ecy); e_comp_object_frame_wh_unadjust(ec->frame, ec->w, ec->h, &ecw, &ech); if (ecw < zw) w = ecw; if (ech < zh) h = ech; if (ecx < zx) // window left not useful coordinates x1 = zx; else if (ecx + ecw > zx + zw) // window right not useful coordinates x1 = zx + zw - ecw; else // window normal position x1 = ecx; if (ecy < zy) // window top not useful coordinates yy1 = zy; else if (ecy + ech > zy + zh) // window bottom not useful coordinates yy1 = zy + zh - ech; else // window normal position yy1 = ecy; switch (max & E_MAXIMIZE_DIRECTION) { case E_MAXIMIZE_BOTH: x = zx, y = zy, w = zw, h = zh; break; case E_MAXIMIZE_VERTICAL: x = ec->x, y = zy, w = ec->w, h = zh; break; case E_MAXIMIZE_HORIZONTAL: x = zx, y = ec->y, w = zw, h = ec->h; break; case E_MAXIMIZE_LEFT: x = zx, y = zy, w = zw / 2, h = zh; break; case E_MAXIMIZE_RIGHT: x = zx + zw / 2, y = zy, w = zw / 2, h = zh; break; } break; case E_MAXIMIZE_FILL: x1 = ec->zone->x; yy1 = ec->zone->y; x2 = ec->zone->x + ec->zone->w; y2 = ec->zone->y + ec->zone->h; e_zone_desk_useful_geometry_get(ec->zone, ec->desk, &zx, &zy, &zw, &zh); x1 = zx, yy1 = zy, x2 = x1 + zw, y2 = yy1 + zh; w = x2 - x1; h = y2 - yy1; pw = w; ph = h; e_client_resize_limit(ec, &w, &h); /* center x-direction */ x1 = x1 + (pw - w) / 2; /* center y-direction */ yy1 = yy1 + (ph - h) / 2; switch (max & E_MAXIMIZE_DIRECTION) { case E_MAXIMIZE_BOTH: x = x1, y = yy1; break; case E_MAXIMIZE_VERTICAL: x = ec->x, y = yy1, w = ec->w; break; case E_MAXIMIZE_HORIZONTAL: x = x1, y = ec->y, h = ec->h; break; case E_MAXIMIZE_LEFT: x = ec->zone->x, y = ec->zone->y, w /= 2; break; case E_MAXIMIZE_RIGHT: x = x1, y = ec->zone->y, w /= 2; break; } break; default: return EINA_FALSE; } if (mx) *mx = x; if (my) *my = y; if (mw) *mw = w; if (mh) *mh = h; return EINA_TRUE; } E_API void e_client_maximize(E_Client *ec, E_Maximize max) { Eina_Bool override; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->maximized == max) return; if (!(max & E_MAXIMIZE_DIRECTION)) max |= E_MAXIMIZE_BOTH; if ((ec->shaded) || (ec->shading)) return; /* Only allow changes in vertical/ horizontal maximization */ if (((ec->maximized & E_MAXIMIZE_DIRECTION) == (max & E_MAXIMIZE_DIRECTION)) || ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_BOTH)) return; if (ec->new_client) { ec->changes.need_maximize = 1; ec->maximized &= ~E_MAXIMIZE_TYPE; ec->maximized |= max; EC_CHANGED(ec); if (ec->re_manage) ec->changes.pos = 0; return; } if ((max & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN) evas_object_smart_callback_call(ec->frame, "fullscreen", NULL); else evas_object_smart_callback_call(ec->frame, "maximize", NULL); evas_object_smart_callback_call(ec->frame, "maximize_pre", &max); if (!max) return; if (ec->moving) { if (ec == action_client) e_comp_canvas_feed_mouse_up(0); //triggers event grabber cb } override = ec->maximize_override; if (ec->fullscreen) e_client_unfullscreen(ec); ec->pre_res_change.valid = 0; if (!ec->saved.set) { if (!(ec->maximized & E_MAXIMIZE_HORIZONTAL)) { /* Horizontal hasn't been set */ if (ec->changes.pos) e_comp_object_frame_xy_adjust(ec->frame, ec->x, 0, &ec->saved.x, NULL); else ec->saved.x = ec->client.x; ec->saved.x -= ec->zone->x; if (ec->visible) ec->saved.w = ec->client.w; } if (!(ec->maximized & E_MAXIMIZE_VERTICAL)) { /* Vertical hasn't been set */ if (ec->changes.pos) e_comp_object_frame_xy_adjust(ec->frame, 0, ec->y, NULL, &ec->saved.y); else ec->saved.y = ec->client.y; ec->saved.y -= ec->zone->y; if (ec->visible) ec->saved.h = ec->client.h; } ec->saved.zone = ec->zone->num; ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame)); } ec->maximize_override = 1; { int x, y, w, h; e_client_maximize_geometry_get(ec, max, &x, &y, &w, &h); if (!_e_client_maximize_run(ec, x, y, w, h)) ec->maximize_override = override; } /* Remove previous type */ ec->maximized &= ~E_MAXIMIZE_TYPE; /* Add new maximization. It must be added, so that VERTICAL + HORIZONTAL == BOTH */ ec->maximized |= max; ec->changes.need_unmaximize = 0; if ((ec->maximized & E_MAXIMIZE_DIRECTION) > E_MAXIMIZE_BOTH) /* left/right maximize */ e_hints_window_maximized_set(ec, 0, ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_LEFT) || ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_RIGHT)); else e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL, ec->maximized & E_MAXIMIZE_VERTICAL); e_remember_update(ec); evas_object_smart_callback_call(ec->frame, "maximize_done", NULL); } E_API Eina_Bool e_client_unmaximize_geometry_get(const E_Client *ec, E_Maximize max, int *mx, int *my, int *mw, int *mh) { int w, h, x, y; if (e_client_util_ignored_get(ec)) return EINA_FALSE; if (!(ec->maximized & E_MAXIMIZE_TYPE)) return EINA_FALSE; if ((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN) { if (mx) *mx = ec->saved.x + ec->zone->x; if (my) *my = ec->saved.y + ec->zone->y; if (mw) *mw = ec->saved.w; if (mh) *mh = ec->saved.h; return EINA_TRUE; } w = ec->client.w; h = ec->client.h; x = ec->client.x; y = ec->client.y; max &= (ec->maximized & E_MAXIMIZE_DIRECTION); if (max & E_MAXIMIZE_VERTICAL) { /* Remove vertical */ h = ec->saved.h; y = ec->saved.y + ec->zone->y; } if (max & E_MAXIMIZE_HORIZONTAL) { /* Remove horizontal */ w = ec->saved.w; x = ec->saved.x + ec->zone->x; } if (mx) *mx = x; if (my) *my = y; if (mw) *mw = w; if (mh) *mh = h; return EINA_TRUE; } E_API void e_client_unmaximize(E_Client *ec, E_Maximize max) { E_Maximize unmax = max; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (!(max & E_MAXIMIZE_DIRECTION)) { CRI("BUG: Unmaximize call without direction!"); return; } if (ec->new_client) { ec->changes.need_unmaximize = 1; EC_CHANGED(ec); return; } if ((ec->shaded) || (ec->shading)) return; /* Remove directions not used */ unmax &= (ec->maximized & E_MAXIMIZE_DIRECTION); evas_object_smart_callback_call(ec->frame, "unmaximize_pre", &unmax); /* Can only remove existing maximization directions */ if ((!unmax) && (!ec->need_fullscreen)) return; if (!unmax) unmax = max & (ec->maximized & E_MAXIMIZE_DIRECTION); if (ec->maximized & E_MAXIMIZE_TYPE) { ec->pre_res_change.valid = 0; ec->changes.need_maximize = 0; if ((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN) { E_Maximize tmp_max = ec->maximized; //un-set maximized state for updating frame. ec->maximized = E_MAXIMIZE_NONE; _e_client_frame_update(ec); // re-set maximized state for unmaximize smart callback. ec->maximized = tmp_max; evas_object_smart_callback_call(ec->frame, "unfullscreen", NULL); // un-set maximized state. ec->maximized = E_MAXIMIZE_NONE; e_client_util_move_resize_without_frame(ec, ec->saved.x + ec->zone->x, ec->saved.y + ec->zone->y, ec->saved.w, ec->saved.h); ec->saved.x = ec->saved.y = ec->saved.w = ec->saved.h = 0; ec->saved.set = 0; e_hints_window_size_unset(ec); } else { int w, h, x, y; Eina_Bool horiz = EINA_FALSE, vert = EINA_FALSE; Eina_Bool fullscreen = !!eina_list_data_find(ec->desk->fullscreen_clients, ec); e_client_unmaximize_geometry_get(ec, unmax, &x, &y, &w, &h); if (unmax & E_MAXIMIZE_VERTICAL) { /* Remove vertical */ vert = EINA_TRUE; if ((unmax & E_MAXIMIZE_VERTICAL) == E_MAXIMIZE_VERTICAL) { if ((ec->maximized & E_MAXIMIZE_LEFT) == E_MAXIMIZE_LEFT) ec->maximized &= ~E_MAXIMIZE_LEFT; if ((ec->maximized & E_MAXIMIZE_RIGHT) == E_MAXIMIZE_RIGHT) ec->maximized &= ~E_MAXIMIZE_RIGHT; ec->maximized &= ~E_MAXIMIZE_VERTICAL; } if ((unmax & E_MAXIMIZE_LEFT) == E_MAXIMIZE_LEFT) ec->maximized &= ~E_MAXIMIZE_LEFT; if ((unmax & E_MAXIMIZE_RIGHT) == E_MAXIMIZE_RIGHT) ec->maximized &= ~E_MAXIMIZE_RIGHT; } if (unmax & E_MAXIMIZE_HORIZONTAL) { /* Remove horizontal */ horiz = EINA_TRUE; ec->maximized &= ~E_MAXIMIZE_HORIZONTAL; } if (!(ec->maximized & E_MAXIMIZE_DIRECTION)) { ec->maximized = E_MAXIMIZE_NONE; _e_client_frame_update(ec); e_hints_window_size_unset(ec); } if (e_config->window_maximize_animate && (!ec->maximize_anims_disabled)) ec->maximize_override = 1; if (!fullscreen) evas_object_smart_callback_call(ec->frame, "unmaximize", NULL); if (ec->saved.frame && (e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame)))) { e_comp_object_frame_xy_adjust(ec->frame, x, y, &x, &y); e_comp_object_frame_wh_adjust(ec->frame, w, h, &w, &h); } e_client_resize_limit(ec, &w, &h); if (fullscreen) evas_object_smart_callback_call(ec->frame, "unmaximize", NULL); if (!_e_client_maximize_run(ec, x, y, w, h)) ec->maximize_override = 0; if (vert) ec->saved.h = ec->saved.y = 0; if (horiz) ec->saved.w = ec->saved.x = 0; if (vert && horiz) ec->saved.set = ec->saved.frame = 0; } e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL, ec->maximized & E_MAXIMIZE_VERTICAL); } e_remember_update(ec); evas_object_smart_callback_call(ec->frame, "unmaximize_done", NULL); ec->changes.need_unmaximize = 0; } E_API void e_client_fullscreen(E_Client *ec, E_Fullscreen policy) { int x = 0, y = 0, w = 0, h = 0; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if ((ec->shaded) || (ec->shading) || ec->fullscreen) return; if ((!e_config->allow_above_fullscreen) && (!ec->desk->visible)) return; ec->need_fullscreen = 1; if (ec->new_client) return; if (e_comp->nocomp_ec && (ec->desk == e_comp->nocomp_ec->desk)) { e_object_unref(E_OBJECT(e_comp->nocomp_ec)); e_object_ref(E_OBJECT(ec)); e_comp->nocomp_ec = ec; } ec->desk->fullscreen_clients = eina_list_append(ec->desk->fullscreen_clients, ec); ec->pre_res_change.valid = 0; if (ec->maximized) { x = ec->saved.x; y = ec->saved.y; w = ec->saved.w; h = ec->saved.h; } else { if (ec->changes.pos) e_comp_object_frame_xy_adjust(ec->frame, ec->x, ec->y, &ec->saved.x, &ec->saved.y); else { ec->saved.x = ec->client.x; ec->saved.y = ec->client.y; } ec->saved.x -= ec->zone->x; ec->saved.y -= ec->zone->y; if (ec->visible) { ec->saved.w = ec->client.w; ec->saved.h = ec->client.h; } } E_FREE_FUNC(ec->agent, evas_object_del); ec->saved.maximized = ec->maximized; ec->saved.zone = ec->zone->num; ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame)); if (ec->maximized) { Eina_Bool maximize_anims_disabled = ec->maximize_anims_disabled; ec->maximize_anims_disabled = 1; e_client_unmaximize(ec, E_MAXIMIZE_BOTH); ec->maximize_anims_disabled = maximize_anims_disabled; ec->saved.x = x; ec->saved.y = y; ec->saved.w = w; ec->saved.h = h; ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame)); } ec->saved.layer = ec->layer; ec->saved.set = 1; if (!e_config->allow_above_fullscreen) evas_object_layer_set(ec->frame, E_LAYER_CLIENT_FULLSCREEN); else if (e_config->mode.presentation) evas_object_layer_set(ec->frame, E_LAYER_CLIENT_TOP); ec->fullscreen = 1; #ifndef HAVE_WAYLAND_ONLY if ((eina_list_count(e_comp->zones) > 1) || (policy == E_FULLSCREEN_RESIZE) || (!ecore_x_randr_query())) #else if ((eina_list_count(e_comp->zones) > 1) || (policy == E_FULLSCREEN_RESIZE)) #endif { evas_object_geometry_set(ec->frame, ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h); } else if (policy == E_FULLSCREEN_ZOOM) { /* compositor backends! */ evas_object_smart_callback_call(ec->frame, "fullscreen_zoom", NULL); } e_hints_window_fullscreen_set(ec, 1); e_hints_window_size_unset(ec); if (!e_client_util_ignored_get(ec)) _e_client_frame_update(ec); ec->fullscreen_policy = policy; evas_object_smart_callback_call(ec->frame, "fullscreen", NULL); _e_client_event_simple(ec, E_EVENT_CLIENT_FULLSCREEN); e_remember_update(ec); ec->need_fullscreen = 0; } E_API void e_client_unfullscreen(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if ((ec->shaded) || (ec->shading)) return; if (!ec->fullscreen) return; ec->pre_res_change.valid = 0; ec->fullscreen = 0; ec->need_fullscreen = 0; ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec); if (ec->fullscreen_policy == E_FULLSCREEN_ZOOM) evas_object_smart_callback_call(ec->frame, "unfullscreen_zoom", NULL); if (!e_client_util_ignored_get(ec)) _e_client_frame_update(ec); ec->fullscreen_policy = 0; evas_object_smart_callback_call(ec->frame, "unfullscreen", NULL); if (ec->saved.maximized) { Eina_Bool maximize_anims_disabled = ec->maximize_anims_disabled; ec->changes.pos = ec->changes.size = 0; ec->maximize_anims_disabled = 1; e_client_maximize(ec, (e_config->maximize_policy & E_MAXIMIZE_TYPE) | ec->saved.maximized); ec->maximize_anims_disabled = maximize_anims_disabled; } else { if (ec->saved.frame && (e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame)))) e_client_util_move_resize_without_frame(ec, ec->zone->x + ec->saved.x, ec->zone->y + ec->saved.y, ec->saved.w, ec->saved.h); else evas_object_geometry_set(ec->frame, ec->zone->x + ec->saved.x, ec->zone->y + ec->saved.y, ec->saved.w, ec->saved.h); ec->saved.w = ec->saved.x = ec->saved.h = ec->saved.y = 0; ec->saved.set = ec->saved.frame = 0; } evas_object_layer_set(ec->frame, ec->saved.layer); e_hints_window_fullscreen_set(ec, 0); _e_client_event_simple(ec, E_EVENT_CLIENT_UNFULLSCREEN); e_remember_update(ec); if (!ec->desk->fullscreen_clients) e_comp_render_queue(); } /////////////////////////////////////// E_API void e_client_iconify(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->shading || ec->iconic) return; if (((ec->stack.prev || ec->stack.next)) && (!ec->stack.ignore)) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child; EINA_LIST_FOREACH(list, l, child) { e_client_iconify(child); } e_client_stack_list_finish(list); E_Desk *desk; desk = e_desk_current_get(ec->zone); e_desk_last_focused_focus(desk); return; } ec->iconic = 1; ec->want_focus = ec->take_focus = 0; ec->changes.visible = 0; if (ec->fullscreen) ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec); e_client_comp_hidden_set(ec, 1); if (!ec->stack.ignore) { if (!ec->new_client) { _e_client_revert_focus(ec); evas_object_hide(ec->frame); } e_client_urgent_set(ec, ec->icccm.urgent); } else { if (!ec->new_client) evas_object_hide(ec->frame); e_client_urgent_set(ec, ec->icccm.urgent); if (ec->focused) evas_object_focus_set(ec->frame, 0); } _e_client_event_simple(ec, E_EVENT_CLIENT_ICONIFY); if (!ec->stack.prev && !ec->stack.next) { if (e_config->transient.iconify) { E_Client *child; Eina_List *list = eina_list_clone(ec->transients); EINA_LIST_FREE(list, child) e_client_iconify(child); } } e_remember_update(ec); } E_API void e_client_uniconify(E_Client *ec) { E_Desk *desk; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->shading || (!ec->iconic)) return; if (((ec->stack.prev || ec->stack.next)) && (!ec->stack.ignore)) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child, *ec_focus = NULL; EINA_LIST_FOREACH(list, l, child) { e_client_uniconify(child); if (!l->next) ec_focus = child; } e_client_stack_list_finish(list); evas_object_raise(ec_focus->frame); evas_object_focus_set(ec_focus->frame, 1); return; } desk = e_desk_current_get(ec->desk->zone); e_client_desk_set(ec, desk); if (!ec->stack.ignore) evas_object_raise(ec->frame); evas_object_show(ec->frame); e_client_comp_hidden_set(ec, 0); ec->deskshow = ec->iconic = 0; if (!ec->stack.ignore) evas_object_focus_set(ec->frame, 1); _e_client_event_simple(ec, E_EVENT_CLIENT_UNICONIFY); if (!ec->stack.prev && !ec->stack.next) { if (e_config->transient.iconify) { E_Client *child; Eina_List *list = eina_list_clone(ec->transients); EINA_LIST_FREE(list, child) e_client_uniconify(child); } } e_remember_update(ec); } /////////////////////////////////////// E_API void e_client_urgent_set(E_Client *ec, Eina_Bool urgent) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (urgent && e_screensaver_on_get() && e_config->screensaver_wake_on_urgent) { int x, y; ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); ecore_evas_pointer_warp(e_comp->ee, x, y); e_comp_canvas_notidle(); } if (!ec->zone) return; urgent = !!urgent; if (urgent == ec->urgent) return; _e_client_event_property(ec, E_CLIENT_PROPERTY_URGENCY); if (urgent && (!ec->focused) && (!ec->want_focus)) { e_comp_object_signal_emit(ec->frame, "e,state,urgent", "e"); ec->urgent = urgent; } else { e_comp_object_signal_emit(ec->frame, "e,state,not_urgent", "e"); ec->urgent = 0; } } /////////////////////////////////////// E_API void e_client_stick(E_Client *ec) { E_Desk *desk; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->sticky) return; desk = ec->desk; ec->desk = NULL; if (desk && ec->fullscreen) desk->fullscreen_clients = eina_list_remove(desk->fullscreen_clients, ec); ec->sticky = 1; ec->hidden = 0; e_hints_window_sticky_set(ec, 1); e_client_desk_set(ec, desk); evas_object_smart_callback_call(ec->frame, "stick", NULL); if (ec->stack.prev || ec->stack.next) { if (ec->stack.ignore == 0) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child; EINA_LIST_FOREACH(list, l, child) { if (child == ec) continue; child->sticky = 1; e_hints_window_sticky_set(child, 1); evas_object_show(ec->frame); } e_client_stack_list_finish(list); } } else { if (e_config->transient.desktop) { E_Client *child; Eina_List *list = eina_list_clone(ec->transients); EINA_LIST_FREE(list, child) { child->sticky = 1; e_hints_window_sticky_set(child, 1); evas_object_show(ec->frame); } } } _e_client_event_property(ec, E_CLIENT_PROPERTY_STICKY); e_remember_update(ec); } E_API void e_client_unstick(E_Client *ec) { E_Desk *desk; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; /* Set the desk before we unstick the client */ if (!ec->sticky) return; desk = e_desk_current_get(ec->zone); if (ec->desk && ec->fullscreen) ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec); ec->desk = NULL; ec->hidden = ec->sticky = 0; e_hints_window_sticky_set(ec, 0); e_client_desk_set(ec, desk); evas_object_smart_callback_call(ec->frame, "unstick", NULL); if (ec->stack.prev || ec->stack.next) { if (ec->stack.ignore == 0) { Eina_List *l, *list = e_client_stack_list_prepare(ec); E_Client *child; EINA_LIST_FOREACH(list, l, child) { if (child == ec) continue; child->sticky = 1; e_hints_window_sticky_set(child, 0); } e_client_stack_list_finish(list); } } else { if (e_config->transient.desktop) { E_Client *child; Eina_List *list = eina_list_clone(ec->transients); EINA_LIST_FREE(list, child) { child->sticky = 0; e_hints_window_sticky_set(child, 0); } } } _e_client_event_property(ec, E_CLIENT_PROPERTY_STICKY); e_client_desk_set(ec, e_desk_current_get(ec->zone)); e_remember_update(ec); } E_API void e_client_pinned_set(E_Client *ec, Eina_Bool set) { E_Layer layer; EINA_SAFETY_ON_NULL_RETURN(ec); ec->borderless = !!set; ec->user_skip_winlist = !!set; if (set) layer = E_LAYER_CLIENT_BELOW; else layer = E_LAYER_CLIENT_NORMAL; evas_object_layer_set(ec->frame, layer); ec->border.changed = 1; EC_CHANGED(ec); } E_API void e_client_prop_misc_changed(E_Client *ec) { EINA_SAFETY_ON_NULL_RETURN(ec); _e_client_event_property(ec, E_CLIENT_PROPERTY_MISC); } /////////////////////////////////////// E_API Eina_Bool e_client_border_set(E_Client *ec, const char *name) { Eina_Stringshare *pborder; E_OBJECT_CHECK_RETURN(ec, EINA_FALSE); E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, EINA_FALSE); if ((!e_comp_object_frame_allowed(ec->frame)) && (!e_comp_object_frame_exists(ec->frame))) return EINA_FALSE; if (ec->border.changed) CRI("CALLING WHEN border.changed SET!"); if (eina_streq(ec->border.name, name)) return EINA_TRUE; if (ec->mwm.borderless && (!eina_streq(name, "borderless"))) { e_util_dialog_show(_("Client Error!"), _("Something has attempted to set a border when it shouldn't! Report this!")); CRI("border change attempted for MWM borderless client!"); } if ((!ec->border.name) && eina_streq(name, "borderless")) return EINA_TRUE; pborder = ec->border.name; ec->border.name = eina_stringshare_add(name); if (e_comp_object_frame_theme_set(ec->frame, name)) { eina_stringshare_del(pborder); return EINA_TRUE; } eina_stringshare_del(ec->border.name); ec->border.name = pborder; return EINA_FALSE; } /////////////////////////////////////// E_API void e_client_comp_hidden_set(E_Client *ec, Eina_Bool hidden) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; hidden = !!hidden; if (ec->comp_hidden == hidden) return; ec->comp_hidden = hidden; evas_object_smart_callback_call(ec->frame, "comp_hidden", NULL); } /////////////////////////////////////// E_API void e_client_act_move_keyboard(E_Client *ec) { EINA_SAFETY_ON_NULL_RETURN(ec); if (!ec->zone) return; if (!_e_client_move_begin(ec)) return; _e_client_action_init(ec); if (!_e_client_hook_call(E_CLIENT_HOOK_MOVE_UPDATE, ec)) return; if (!action_handler_key) action_handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _e_client_move_key_down, NULL); if (!action_handler_mouse) action_handler_mouse = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_client_move_mouse_down, NULL); _e_client_action_move_timeout_add(); } E_API void e_client_act_resize_keyboard(E_Client *ec) { EINA_SAFETY_ON_NULL_RETURN(ec); if (!ec->zone) return; ec->resize_mode = E_POINTER_RESIZE_BR; ec->keyboard_resizing = 1; if (!e_client_resize_begin(ec)) { ec->keyboard_resizing = 0; return; } _e_client_action_init(ec); if (!action_handler_key) action_handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _e_client_resize_key_down, NULL); if (!action_handler_mouse) action_handler_mouse = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_client_resize_mouse_down, NULL); _e_client_action_resize_timeout_add(); } E_API void e_client_act_move_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (e_client_util_resizing_get(ec) || (ec->moving)) return; if (ev) { char source[256]; snprintf(source, sizeof(source) - 1, "mouse,down,%i", ev->button); _e_client_moveinfo_gather(ec, source); } if (!_e_client_move_begin(ec)) return; _e_client_action_init(ec); e_pointer_mode_push(ec, E_POINTER_MOVE); } E_API void e_client_act_move_end(E_Client *ec, E_Binding_Event_Mouse_Button *ev EINA_UNUSED) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (!ec->moving) return; e_zone_edge_enable(); _e_client_move_end(ec); e_zone_flip_coords_handle(ec->zone, -1, -1); _e_client_action_finish(); } E_API void e_client_act_resize_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->lock_user_size || ec->shaded || ec->shading) return; if (e_client_util_resizing_get(ec) || (ec->moving)) return; if (ev) { char source[256]; snprintf(source, sizeof(source) - 1, "mouse,down,%i", ev->button); _e_client_moveinfo_gather(ec, source); } if ((ec->mouse.current.mx > (ec->x + ec->w / 5)) && (ec->mouse.current.mx < (ec->x + ec->w * 4 / 5))) { if (ec->mouse.current.my < (ec->y + ec->h / 2)) { ec->resize_mode = E_POINTER_RESIZE_T; } else { ec->resize_mode = E_POINTER_RESIZE_B; } } else if (ec->mouse.current.mx < (ec->x + ec->w / 2)) { if ((ec->mouse.current.my > (ec->y + ec->h / 5)) && (ec->mouse.current.my < (ec->y + ec->h * 4 / 5))) { ec->resize_mode = E_POINTER_RESIZE_L; } else if (ec->mouse.current.my < (ec->y + ec->h / 2)) { ec->resize_mode = E_POINTER_RESIZE_TL; } else { ec->resize_mode = E_POINTER_RESIZE_BL; } } else { if ((ec->mouse.current.my > (ec->y + ec->h / 5)) && (ec->mouse.current.my < (ec->y + ec->h * 4 / 5))) { ec->resize_mode = E_POINTER_RESIZE_R; } else if (ec->mouse.current.my < (ec->y + ec->h / 2)) { ec->resize_mode = E_POINTER_RESIZE_TR; } else { ec->resize_mode = E_POINTER_RESIZE_BR; } } if (!e_client_resize_begin(ec)) return; _e_client_action_init(ec); e_pointer_mode_push(ec, ec->resize_mode); } E_API void e_client_act_resize_end(E_Client *ec, E_Binding_Event_Mouse_Button *ev EINA_UNUSED) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (e_client_util_resizing_get(ec)) { _e_client_resize_end(ec); ec->changes.reset_gravity = 1; if (!e_object_is_del(E_OBJECT(ec))) EC_CHANGED(ec); } _e_client_action_finish(); } E_API void e_client_act_menu_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev, int key) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->border_menu) return; if (ev) e_int_client_menu_show(ec, ev->canvas.x, ev->canvas.y, key, ev->timestamp); else { int x, y; evas_pointer_canvas_xy_get(e_comp->evas, &x, &y); e_int_client_menu_show(ec, x, y, key, 0); } } E_API void e_client_act_close_begin(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->lock_close) return; if (ec->icccm.delete_request) { ec->delete_requested = 1; evas_object_smart_callback_call(ec->frame, "delete_request", NULL); } else if (e_config->kill_if_close_not_possible) { e_client_act_kill_begin(ec); } } E_API void e_client_act_kill_begin(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (ec->internal) return; if (ec->lock_close) return; if (ec->netwm.pid == getpid()) return; if ((ec->netwm.pid > 1) && (e_config->kill_process)) { kill(ec->netwm.pid, SIGINT); ec->kill_timer = ecore_timer_loop_add(e_config->kill_timer_wait, _e_client_cb_kill_timer, ec); } else evas_object_smart_callback_call(ec->frame, "kill_request", NULL); } //////////////////////////////////////////////// E_API Evas_Object * e_client_icon_add(E_Client *ec, Evas *evas) { Evas_Object *o; E_OBJECT_CHECK_RETURN(ec, NULL); E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, NULL); o = NULL; if (ec->internal) { if (!ec->internal_icon) { o = e_icon_add(evas); e_util_icon_theme_set(o, "enlightenment"); } else { if (!ec->internal_icon_key) { char *ext; ext = strrchr(ec->internal_icon, '.'); if ((ext) && ((!strcmp(ext, ".edj")))) { o = edje_object_add(evas); if (!edje_object_file_set(o, ec->internal_icon, "icon")) e_util_icon_theme_set(o, "enlightenment"); } else if (ext) { o = e_icon_add(evas); e_icon_file_set(o, ec->internal_icon); } else { o = e_icon_add(evas); if (!e_util_icon_theme_set(o, ec->internal_icon)) e_util_icon_theme_set(o, "enlightenment"); } } else { o = edje_object_add(evas); edje_object_file_set(o, ec->internal_icon, ec->internal_icon_key); } } return o; } #ifndef HAVE_WAYLAND_ONLY if ((e_config->use_app_icon) && (ec->icon_preference != E_ICON_PREF_USER)) { if (ec->netwm.icons) { o = e_icon_add(evas); e_icon_data_set(o, ec->netwm.icons[0].data, ec->netwm.icons[0].width, ec->netwm.icons[0].height); e_icon_alpha_set(o, 1); return o; } } #endif if (!o) { if ((ec->desktop) && (ec->icon_preference != E_ICON_PREF_NETWM)) { o = e_util_desktop_icon_add(ec->desktop, 64, evas); if (o) return o; } #ifndef HAVE_WAYLAND_ONLY else if (ec->netwm.icons) { o = e_icon_add(evas); e_icon_data_set(o, ec->netwm.icons[0].data, ec->netwm.icons[0].width, ec->netwm.icons[0].height); e_icon_alpha_set(o, 1); return o; } #endif } o = e_icon_add(evas); e_util_icon_theme_set(o, "unknown"); return o; } //////////////////////////////////////////// E_API void e_client_ping(E_Client *ec) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!e_config->ping_clients) return; ec->ping_ok = 0; evas_object_smart_callback_call(ec->frame, "ping", NULL); ec->ping = ecore_loop_time_get(); if (ec->ping_poller) ecore_poller_del(ec->ping_poller); ec->ping_poller = ecore_poller_add(ECORE_POLLER_CORE, e_config->ping_clients_interval, _e_client_cb_ping_poller, ec); } //////////////////////////////////////////// E_API void e_client_move_cancel(void) { if (!ecmove) return; if (ecmove->cur_mouse_action) { E_Client *ec; ec = ecmove; e_object_ref(E_OBJECT(ec)); _e_client_mouse_action_end(ec); e_object_unref(E_OBJECT(ec)); } else _e_client_move_end(ecmove); } E_API void e_client_resize_cancel(void) { if (!ecresize) return; if (ecresize->cur_mouse_action) { E_Client *ec; ec = ecresize; e_object_ref(E_OBJECT(ec)); _e_client_mouse_action_end(ec); e_object_unref(E_OBJECT(ec)); } else _e_client_resize_end(ecresize); } E_API Eina_Bool e_client_resize_begin(E_Client *ec) { if ((ec->shaded) || (ec->shading) || (ec->fullscreen) || (ec->lock_user_size)) goto error; if (!_e_client_action_input_win_new()) goto error; if (!ec->lock_user_stacking) { if (e_config->border_raise_on_mouse_action) evas_object_raise(ec->frame); } ecresize = ec; _e_client_hook_call(E_CLIENT_HOOK_RESIZE_BEGIN, ec); if (!e_client_util_resizing_get(ec)) { if (ecresize == ec) ecresize = NULL; _e_client_action_input_win_del(); return EINA_FALSE; } E_FREE_FUNC(ec->raise_timer, ecore_timer_del); _e_client_action_event_grabber_init(ec); return EINA_TRUE; error: ec->resize_mode = E_POINTER_RESIZE_NONE; return EINA_FALSE; } //////////////////////////////////////////// E_API void e_client_frame_recalc(E_Client *ec) { EINA_SAFETY_ON_NULL_RETURN(ec); if (!ec->frame) return; evas_object_smart_callback_call(ec->frame, "frame_recalc", NULL); } //////////////////////////////////////////// E_API void e_client_signal_move_begin(E_Client *ec, const char *sig, const char *src EINA_UNUSED) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (e_client_util_resizing_get(ec) || (ec->moving)) return; _e_client_moveinfo_gather(ec, sig); if (!_e_client_move_begin(ec)) return; _e_client_action_init(ec); e_pointer_mode_push(ec, E_POINTER_MOVE); } E_API void e_client_signal_move_end(E_Client *ec, const char *sig EINA_UNUSED, const char *src EINA_UNUSED) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->zone) return; if (!ec->moving) return; _e_client_move_end(ec); e_zone_edge_enable(); e_zone_flip_coords_handle(ec->zone, -1, -1); _e_client_action_finish(); } E_API void e_client_signal_resize_begin(E_Client *ec, const char *dir, const char *sig, const char *src EINA_UNUSED) { int resize_mode = E_POINTER_RESIZE_BR; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (e_client_util_resizing_get(ec) || (ec->moving)) return; if (!strcmp(dir, "tl")) { resize_mode = E_POINTER_RESIZE_TL; } else if (!strcmp(dir, "t")) { resize_mode = E_POINTER_RESIZE_T; } else if (!strcmp(dir, "tr")) { resize_mode = E_POINTER_RESIZE_TR; } else if (!strcmp(dir, "r")) { resize_mode = E_POINTER_RESIZE_R; } else if (!strcmp(dir, "br")) { resize_mode = E_POINTER_RESIZE_BR; } else if (!strcmp(dir, "b")) { resize_mode = E_POINTER_RESIZE_B; } else if (!strcmp(dir, "bl")) { resize_mode = E_POINTER_RESIZE_BL; } else if (!strcmp(dir, "l")) { resize_mode = E_POINTER_RESIZE_L; } ec->resize_mode = resize_mode; _e_client_moveinfo_gather(ec, sig); if (!e_client_resize_begin(ec)) return; _e_client_action_init(ec); e_pointer_mode_push(ec, ec->resize_mode); } E_API void e_client_signal_resize_end(E_Client *ec, const char *dir EINA_UNUSED, const char *sig EINA_UNUSED, const char *src EINA_UNUSED) { E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!e_client_util_resizing_get(ec)) return; _e_client_resize_handle(ec); _e_client_resize_end(ec); ec->changes.reset_gravity = 1; EC_CHANGED(ec); _e_client_action_finish(); } //////////////////////////////////////////// E_API void e_client_resize_limit(const E_Client *ec, int *w, int *h) { int l = 0, r = 0, t = 0, b = 0; int dw = 0, dh = 0; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); e_comp_object_frame_geometry_get(ec->frame, &l, &r, &t, &b); if ((ec->frame) && e_comp_object_frame_allowed(ec->frame)) { e_comp_object_frame_wh_unadjust(ec->frame, ec->w, ec->h, &dw, &dh); e_comp_object_frame_wh_unadjust(ec->frame, *w, *h, w, h); } else { *w -= l + r; *h -= t + b; dw = ec->w; dh = ec->h; } dw = abs(*w - dw); dh = abs(*h - dh); if (*h < 1) *h = 1; if (*w < 1) *w = 1; if ((ec->icccm.max_w > 0) && (*w > ec->icccm.max_w)) *w = ec->icccm.max_w; else if (*w < ec->icccm.min_w) *w = ec->icccm.min_w; if ((ec->icccm.max_h > 0) && (*h > ec->icccm.max_h)) *h = ec->icccm.max_h; else if (*h < ec->icccm.min_h) *h = ec->icccm.min_h; if (ec->icccm.step_w > 0) { int bw = ec->icccm.base_w ?: ec->icccm.min_w; bw = bw + (((*w - bw) / ec->icccm.step_w) * ec->icccm.step_w); if ((bw > ec->icccm.min_w) && ((ec->icccm.max_w < 1) || (bw < ec->icccm.max_w))) *w = bw; } if (ec->icccm.step_h > 0) { int bh = ec->icccm.base_h ?: ec->icccm.min_h; bh = bh + (((*h - bh) / ec->icccm.step_h) * ec->icccm.step_h); if ((bh > ec->icccm.min_h) && ((ec->icccm.max_h < 1) || (bh < ec->icccm.max_h))) *h = bh; } if (EINA_DBL_NONZERO(ec->icccm.min_aspect) || EINA_DBL_NONZERO(ec->icccm.max_aspect)) { double a = (double)*w / *h; Evas_Aspect_Control aspect; int aw, ah; double val; if (a < ec->icccm.min_aspect) { if (dw) aspect = EVAS_ASPECT_CONTROL_HORIZONTAL; else if (dh) aspect = EVAS_ASPECT_CONTROL_VERTICAL; else aspect = EVAS_ASPECT_CONTROL_BOTH; switch (aspect) { case EVAS_ASPECT_CONTROL_HORIZONTAL: val = ((*h - (*w / ec->icccm.min_aspect)) * ec->icccm.step_h) / ec->icccm.step_h; if (val > 0) ah = ceil(val); else ah = floor(val); if (*h - ah > ec->icccm.min_h) { *h -= ah; break; } EINA_FALLTHROUGH; /* No break */ default: val = (((*h * ec->icccm.min_aspect) - *w) * ec->icccm.step_w) / ec->icccm.step_w; if (val > 0) aw = ceil(val); else aw = floor(val); if (*w + aw < ec->icccm.max_w) *w += aw; break; } } a = (double)*w / *h; if (a < ec->icccm.min_aspect) abort(); if (a > ec->icccm.max_aspect) { if (dw) aspect = EVAS_ASPECT_CONTROL_HORIZONTAL; else if (dh) aspect = EVAS_ASPECT_CONTROL_VERTICAL; else aspect = EVAS_ASPECT_CONTROL_BOTH; switch (aspect) { case EVAS_ASPECT_CONTROL_HORIZONTAL: val = (((*w / ec->icccm.max_aspect) - *h) * ec->icccm.step_h) / ec->icccm.step_h; if (val > 0) ah = ceil(val); else ah = floor(val); if (*h + ah > ec->icccm.max_h) { *h += ah; break; } EINA_FALLTHROUGH; /* No break */ default: val = ((*w - (*h * ec->icccm.max_aspect)) * ec->icccm.step_w) / ec->icccm.step_w; if (val > 0) aw = ceil(val); else aw = floor(val); if (*w - aw > ec->icccm.min_w) *w -= aw; break; } } } if (*h < 1) *h = 1; if (*w < 1) *w = 1; if ((ec->frame) && e_comp_object_frame_allowed(ec->frame)) { if (ec->frame) e_comp_object_frame_wh_adjust(ec->frame, *w, *h, w, h); } else { *w += l + r; *h += t + b; } } //////////////////////////////////////////// E_API E_Client * e_client_under_pointer_get(E_Desk *desk, E_Client *exclude) { int x, y; /* We need to ensure that we can get the comp window for the * zone of either the given desk or the desk of the excluded * window, so return if neither is given */ if (desk) ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); else if (exclude) ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); else return NULL; if (!desk) { desk = exclude->desk; if (!desk) { if (exclude->zone) desk = e_desk_current_get(exclude->zone); else desk = e_desk_current_get(e_zone_current_get()); } } return _e_client_under_pointer_helper(desk, exclude, x, y); } //////////////////////////////////////////// E_API int e_client_pointer_warp_to_center_now(E_Client *ec) { if (e_config->disable_all_pointer_warps) return 0; if (warp_client == ec) { ecore_evas_pointer_warp(e_comp->ee, warp_to_x, warp_to_y); warp_to = 0; _e_client_pointer_warp_to_center_timer(NULL); } else { if (e_client_pointer_warp_to_center(ec)) e_client_pointer_warp_to_center_now(ec); } return 1; } E_API int e_client_pointer_warp_to_center(E_Client *ec) { int x, y; E_Client *cec = NULL; ec = e_client_stack_active_adjust(ec); if (ec->override) return 0; if (!ec->zone) return 0; if (e_config->disable_all_pointer_warps) return 0; /* Only warp the pointer if it is not already in the area of * the given border */ ecore_evas_pointer_xy_get(e_comp->ee, &x, &y); if ((x >= ec->x) && (x <= (ec->x + ec->w)) && (y >= ec->y) && (y <= (ec->y + ec->h))) { cec = _e_client_under_pointer_helper(ec->desk, ec, x, y); if (cec) cec = e_client_stack_active_adjust(cec); if (cec == ec) return 0; } warp_to_x = ec->x + (ec->w / 2); if (warp_to_x < (ec->zone->x + 1)) warp_to_x = ec->zone->x + ((ec->x + ec->w - ec->zone->x) / 2); else if (warp_to_x > (ec->zone->x + ec->zone->w)) warp_to_x = (ec->zone->x + ec->zone->w + ec->x) / 2; warp_to_y = ec->y + (ec->h / 2); if (warp_to_y < (ec->zone->y + 1)) warp_to_y = ec->zone->y + ((ec->y + ec->h - ec->zone->y) / 2); else if (warp_to_y > (ec->zone->y + ec->zone->h)) warp_to_y = (ec->zone->y + ec->zone->h + ec->y) / 2; /* TODO: handle case where another border is over the exact center, * find a place where the requested border is not overlapped? * if (!cec) cec = _e_client_under_pointer_helper(ec->desk, ec, x, y); if (cec != ec) { } */ warp_to = 1; warp_client = ec; ecore_evas_pointer_xy_get(e_comp->ee, &warp_x[0], &warp_y[0]); if (warp_timer) ecore_timer_del(warp_timer); warp_timer = ecore_timer_loop_add(0.01, _e_client_pointer_warp_to_center_timer, ec); return 1; } //////////////////////////////////////////// E_API void e_client_redirected_set(E_Client *ec, Eina_Bool set) { EINA_SAFETY_ON_NULL_RETURN(ec); if (ec->input_only) return; set = !!set; if (ec->redirected == set) return; if (set) { e_client_frame_recalc(ec); if (!_e_client_hook_call(E_CLIENT_HOOK_REDIRECT, ec)) return; } else { if (!_e_client_hook_call(E_CLIENT_HOOK_UNREDIRECT, ec)) return; } e_comp_object_redirected_set(ec->frame, set); ec->redirected = !!set; } //////////////////////////////////////////// E_API void e_client_next_mouse_action_ignore(E_Client *ec) { EINA_SAFETY_ON_NULL_RETURN(ec); ec->next_mouse_action_ignore = EINA_TRUE; } //////////////////////////////////////////// E_API Eina_Bool e_client_is_stacking(const E_Client *ec) { return e_comp->layers[e_comp_canvas_layer_map(ec->layer)].obj == ec->frame; } E_API Eina_Bool e_client_has_xwindow(const E_Client *ec) { #ifdef HAVE_WAYLAND_ONLY (void)ec; return EINA_FALSE; #else # ifdef HAVE_WAYLAND if (!e_pixmap_is_x(ec->pixmap)) return !!e_comp_wl_client_xwayland_pixmap(ec); # endif return e_pixmap_is_x(ec->pixmap); #endif } //////////////////////////////////////////// E_API void e_client_layout_cb_set(E_Client_Layout_Cb cb) { if (_e_client_layout_cb && cb) CRI("ATTEMPTING TO OVERWRITE EXISTING CLIENT LAYOUT HOOK!!!"); _e_client_layout_cb = cb; } //////////////////////////////////////////// E_API void e_client_parent_set(E_Client *ec, E_Client *parent) { // E_Client *prev; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (parent) { E_OBJECT_CHECK(parent); E_OBJECT_TYPE_CHECK(parent, E_CLIENT_TYPE); } if (ec == parent) { ERR("refusing to set client as its own parent"); return; } if (parent && (parent->parent == ec)) { ERR("refusing to set client as its parent's parent"); return; } if (ec->parent == parent) return; /* If we already have a parent, remove it */ if (ec->parent) { ec->parent->transients = eina_list_remove(ec->parent->transients, ec); if (ec->parent->modal == ec) ec->parent->modal = NULL; ec->parent = NULL; } if (parent) { // prev = eina_list_last_data_get(parent->transients); parent->transients = eina_list_append(parent->transients, ec); ec->parent = parent; } if (ec->parent) { evas_object_layer_set(ec->frame, ec->parent->layer); /* complex stacking right above one dialog after the other hurts the * simple assumptions that the window should be on top by clients so * be dumber and disable this if (prev) evas_object_stack_above(ec->frame, prev->frame); else evas_object_stack_above(ec->frame, parent->frame); */ evas_object_raise(ec->frame); if (e_client_util_ignored_get(ec)) return; if (e_pixmap_usable_get(ec->pixmap) && (!ec->lock_user_location)) e_comp_object_util_center_on(ec->frame, parent->frame); if ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) || (ec->parent->focused && (e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED))) ec->take_focus = 1; } } static void _e_client_stack_top(E_Client *ec_base EINA_UNUSED, E_Client *ec_stack, E_Client *ec_above) { /* complex stacking right above one dialog after the other hurts the * simple assumptions that the window should be on top by clients so * be dumber and disable this if (!ec_above) { evas_object_raise(ec_stack->frame); evas_object_stack_above(ec_base->frame, ec_stack->frame); } else { if (ec_above->transients) { e_client_transients_restack(ec_above); E_Client *ec_last = eina_list_last_data_get(ec_above->transients); if (ec_last) evas_object_stack_above(ec_stack->frame, ec_last->frame); else evas_object_stack_above(ec_stack->frame, ec_above->frame); } else evas_object_stack_above(ec_stack->frame, ec_above->frame); } */ if (!ec_above) evas_object_raise(ec_stack->frame); else { if (ec_above->transients) e_client_transients_restack(ec_above); evas_object_raise(ec_stack->frame); } } E_API void e_client_transients_restack(E_Client *ec) { E_Client *child, *below = NULL; Eina_List *list; E_OBJECT_CHECK(ec); E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE); if (!ec->transients) return; list = eina_list_clone(ec->transients); EINA_LIST_FREE(list, child) { // Don't stack iconic transients. If the user wants these shown, // that's another option. if (child->iconic) continue; _e_client_stack_top(ec, child, below); below = child; } }