diff options
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | src/desktop.c | 14 | ||||
-rw-r--r-- | src/ivi-compositor.h | 1 | ||||
-rw-r--r-- | src/layout.c | 8 | ||||
-rw-r--r-- | src/policy.c | 333 | ||||
-rw-r--r-- | src/policy.h | 170 |
6 files changed, 527 insertions, 0 deletions
diff --git a/meson.build b/meson.build index 9d6e800..cfe446d 100644 --- a/meson.build +++ b/meson.build @@ -120,6 +120,7 @@ srcs_agl_compositor = [ 'src/main.c', 'src/desktop.c', 'src/layout.c', + 'src/policy.c', 'src/shell.c', 'shared/option-parser.c', 'shared/os-compatibility.c', diff --git a/src/desktop.c b/src/desktop.c index eaace19..7c6c19a 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -24,6 +24,7 @@ */ #include "ivi-compositor.h" +#include "policy.h" #include <libweston/libweston.h> #include <libweston-desktop/libweston-desktop.h> @@ -80,6 +81,13 @@ desktop_surface_added(struct weston_desktop_surface *dsurface, void *userdata) surface->dsurface = dsurface; surface->role = IVI_SURFACE_ROLE_NONE; + if (ivi->policy && ivi->policy->api.surface_create && + !ivi->policy->api.surface_create(surface, ivi)) { + free(surface); + wl_client_post_no_memory(client); + return; + } + weston_desktop_surface_set_user_data(dsurface, surface); if (ivi->shell_client.ready) { @@ -131,6 +139,12 @@ desktop_committed(struct weston_desktop_surface *dsurface, { struct ivi_surface *surface = weston_desktop_surface_get_user_data(dsurface); + struct ivi_policy *policy = surface->ivi->policy; + + if (policy && policy->api.surface_commited && + !policy->api.surface_commited(surface, surface->ivi)) + return; + weston_compositor_schedule_repaint(surface->ivi->compositor); switch (surface->role) { diff --git a/src/ivi-compositor.h b/src/ivi-compositor.h index 446390d..4da12bc 100644 --- a/src/ivi-compositor.h +++ b/src/ivi-compositor.h @@ -82,6 +82,7 @@ struct ivi_compositor { struct wl_list surfaces; /* ivi_surface.link */ struct weston_desktop *desktop; + struct ivi_policy *policy; struct wl_list pending_surfaces; diff --git a/src/layout.c b/src/layout.c index 0c65b6d..b531ed1 100644 --- a/src/layout.c +++ b/src/layout.c @@ -24,6 +24,7 @@ */ #include "ivi-compositor.h" +#include "policy.h" #include <assert.h> #include <string.h> @@ -345,10 +346,17 @@ ivi_layout_activate(struct ivi_output *output, const char *app_id) struct weston_desktop_surface *dsurf; struct weston_view *view; struct weston_geometry geom; + struct ivi_policy *policy = output->ivi->policy; surf = ivi_find_app(ivi, app_id); if (!surf) return; + + if (policy && policy->api.surface_activate && + !policy->api.surface_activate(surf, surf->ivi)) { + return; + } + #ifdef AGL_COMP_DEBUG weston_log("Found app_id %s\n", app_id); #endif diff --git a/src/policy.c b/src/policy.c new file mode 100644 index 0000000..78994fa --- /dev/null +++ b/src/policy.c @@ -0,0 +1,333 @@ +/* + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <string.h> +#include <libweston/zalloc.h> +#include <assert.h> + +#include "shared/helpers.h" +#include "ivi-compositor.h" + +#include "policy.h" + +static void +ivi_policy_remove_state_event(struct state_event *st_ev) +{ + free(st_ev->name); + wl_list_remove(&st_ev->link); + free(st_ev); +} + +static void +ivi_policy_destroy_state_event(struct wl_list *list) +{ + struct state_event *st_ev, *tmp_st_ev; + wl_list_for_each_safe(st_ev, tmp_st_ev, list, link) + ivi_policy_remove_state_event(st_ev); +} + +static struct state_event * +ivi_policy_state_event_create(uint32_t val, const char *value) +{ + struct state_event *ev_st = zalloc(sizeof(*ev_st)); + size_t value_len = strlen(value); + + ev_st->value = val; + ev_st->name = zalloc(sizeof(char) * value_len + 1); + memcpy(ev_st->name, value, value_len); + + return ev_st; +} + +void +ivi_policy_add_state(struct ivi_policy *policy, uint32_t state, const char *value) +{ + struct state_event *ev_st; + if (!policy) + return; + + ev_st = ivi_policy_state_event_create(state, value); + wl_list_insert(&policy->states, &ev_st->link); +} + +void +ivi_policy_add_event(struct ivi_policy *policy, uint32_t ev, const char *value) +{ + struct state_event *ev_st; + if (!policy) + return; + + ev_st = ivi_policy_state_event_create(ev, value); + wl_list_insert(&policy->events, &ev_st->link); +} + +static void +ivi_policy_add_default_states(struct ivi_policy *policy) +{ + const char *default_states[] = { "invalid", "start", "stop", "reverse" }; + if (!policy) + return; + + for (uint32_t i = 0; i < ARRAY_LENGTH(default_states); i ++) { + struct state_event *ev_st = + ivi_policy_state_event_create(i, default_states[i]); + wl_list_insert(&policy->states, &ev_st->link); + } +} + +static void +ivi_policy_add_default_events(struct ivi_policy *policy) +{ + const char *default_events[] = { "show", "hide" }; + if (!policy) + return; + + for (uint32_t i = 0; i < ARRAY_LENGTH(default_events); i ++) { + struct state_event *ev_st = + ivi_policy_state_event_create(i, default_events[i]); + wl_list_insert(&policy->events, &ev_st->link); + } +} + +static void +ivi_policy_try_event(struct ivi_a_policy *a_policy) +{ + struct ivi_policy *policy = a_policy->policy; + + if (policy->api.policy_rule_try_event) + return policy->api.policy_rule_try_event(a_policy); +} + +static int +ivi_policy_try_event_timeout(void *user_data) +{ + struct ivi_a_policy *a_policy = user_data; + ivi_policy_try_event(a_policy); + return 0; +} + +static void +ivi_policy_setup_event_timeout(struct ivi_policy *ivi_policy, + struct ivi_a_policy *a_policy) +{ + struct ivi_compositor *ivi = ivi_policy->ivi; + struct wl_display *wl_display = ivi->compositor->wl_display; + struct wl_event_loop *loop = wl_display_get_event_loop(wl_display); + + a_policy->timer = wl_event_loop_add_timer(loop, + ivi_policy_try_event_timeout, + a_policy); + + wl_event_source_timer_update(a_policy->timer, a_policy->timeout); +} + +static void +ivi_policy_check_policies(struct wl_listener *listener, void *data) +{ + struct ivi_a_policy *a_policy; + struct ivi_policy *ivi_policy = + wl_container_of(listener, ivi_policy, listener_check_policies); + + ivi_policy->state_change_in_progress = true; + wl_list_for_each(a_policy, &ivi_policy->policies, link) { + if (ivi_policy->current_state == a_policy->state) { + /* check the timeout first to see if there's a timeout */ + if (a_policy->timeout > 0) + ivi_policy_setup_event_timeout(ivi_policy, + a_policy); + else + ivi_policy_try_event(a_policy); + } + } + + ivi_policy->previous_state = ivi_policy->current_state; + ivi_policy->state_change_in_progress = false; +} + + +struct ivi_policy * +ivi_policy_create(struct ivi_compositor *ivi, + const struct ivi_policy_api *api, void *user_data) +{ + struct ivi_policy *policy = zalloc(sizeof(*policy)); + + policy->user_data = user_data; + policy->ivi = ivi; + policy->state_change_in_progress = false; + + policy->api.struct_size = + MIN(sizeof(struct ivi_policy_api), api->struct_size); + /* install the hooks */ + memcpy(&policy->api, api, policy->api.struct_size); + + /* to trigger a check for policies use */ + wl_signal_init(&policy->signal_state_change); + + policy->listener_check_policies.notify = ivi_policy_check_policies; + wl_signal_add(&policy->signal_state_change, + &policy->listener_check_policies); + + policy->current_state = AGL_SHELL_POLICY_STATE_INVALID; + policy->previous_state = AGL_SHELL_POLICY_STATE_INVALID; + + /* policy rules */ + wl_list_init(&policy->policies); + + wl_list_init(&policy->events); + wl_list_init(&policy->states); + + /* add the default states and enums */ + ivi_policy_add_default_states(policy); + ivi_policy_add_default_events(policy); + + return policy; +} + +void +ivi_policy_destroy(struct ivi_policy *ivi_policy) +{ + struct ivi_a_policy *a_policy, *a_policy_tmp; + + if (!ivi_policy) + return; + + wl_list_for_each_safe(a_policy, a_policy_tmp, + &ivi_policy->policies, link) { + free(a_policy->app_id); + wl_list_remove(&a_policy->link); + free(a_policy); + } + + ivi_policy_destroy_state_event(&ivi_policy->states); + ivi_policy_destroy_state_event(&ivi_policy->events); + + free(ivi_policy); +} + + +/* verifies if the state is one has been added */ +static bool +ivi_policy_state_is_known(uint32_t state, struct ivi_policy *policy) +{ + struct state_event *ev_st; + + wl_list_for_each(ev_st, &policy->states, link) { + if (ev_st->value == state) { + return true; + } + } + return false; +} + +/* + * The generic way would be the following: + * + * - 'car' is in 'state' -> + * { do 'event' for app 'app_id' at 'timeout' time if same state as 'car_state' } + * + * a 0 timeout means, immediately, a timeout > 0, means to install timer an + * execute when timeout expires + * + * The following happens: + * 'car' changes its state -> verify what policy needs to be run + * 'car' in same state -> no action + * + */ +int +ivi_policy_add(struct ivi_policy *policy, const char *app_id, uint32_t state, + uint32_t event, uint32_t timeout, struct wl_resource *output_res) +{ + size_t app_id_len; + struct weston_head *head = weston_head_from_resource(output_res); + struct weston_output *woutput = weston_head_get_output(head); + struct ivi_output *output = to_ivi_output(woutput); + struct ivi_a_policy *a_policy; + + if (!policy) { + weston_log("Failed to retrieve policy!\n"); + return -1; + } + + a_policy = zalloc(sizeof(*a_policy)); + if (!a_policy) + return -1; + + if (policy->state_change_in_progress) + return -1; + + /* we should be allow to do this in the first place, only if the + * hooks allows us to */ + if (policy->api.policy_rule_allow_to_add && + !policy->api.policy_rule_allow_to_add(policy)) + return -1; + + if (!ivi_policy_state_is_known(state, policy)) + return -1; + + app_id_len = strlen(app_id); + a_policy->app_id = zalloc(sizeof(char) * app_id_len + 1); + memcpy(a_policy->app_id, app_id, app_id_len); + + a_policy->state = state; + a_policy->event = event; + a_policy->timeout = timeout; + a_policy->output = output; + a_policy->policy = policy; + + wl_list_insert(&policy->policies, &a_policy->link); + + return 0; +} + +/* we start with 'invalid' state, so a initial state to even 'stop' should + * trigger a check of policies + */ +int +ivi_policy_state_change(struct ivi_policy *policy, uint32_t state) +{ + bool found_state = false; + if (!policy) { + weston_log("Failed to retrieve policy!\n"); + return -1; + } + + if (policy->current_state == state) { + return -1; + } + + /* if we don't know the state, make sure it is first added */ + found_state = ivi_policy_state_is_known(state, policy); + if (!found_state) { + return -1; + } + + /* current_state is actually the new state */ + policy->current_state = state; + + /* signal that we need to check the current policies */ + wl_signal_emit(&policy->signal_state_change, policy); + + return 0; +} diff --git a/src/policy.h b/src/policy.h new file mode 100644 index 0000000..947c326 --- /dev/null +++ b/src/policy.h @@ -0,0 +1,170 @@ +/* + * Copyright © 2020 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef POLICY_H +#define POLICY_H + +#include "ivi-compositor.h" + +/* default state, invalid should at least be in order + * to signal states */ +#ifndef AGL_SHELL_POLICY_STATE_INVALID +#define AGL_SHELL_POLICY_STATE_INVALID 0 +#endif + +/* default events */ +#ifndef AGL_SHELL_POLICY_EVENT_SHOW +#define AGL_SHELL_POLICY_EVENT_SHOW 0 +#endif + +#ifndef AGL_SHELL_POLICY_EVENT_HIDE +#define AGL_SHELL_POLICY_EVENT_HIDE 1 +#endif + +struct ivi_policy; + +struct state_event { + uint32_t value; + char *name; + struct wl_list link; /* ivi_policy::states or ivi_policy::events */ +}; + +struct ivi_a_policy { + struct ivi_policy *policy; + + char *app_id; + uint32_t state; + uint32_t event; + uint32_t timeout; + struct ivi_output *output; + struct wl_event_source *timer; /* for policies that have a timeout */ + + struct wl_list link; /* ivi_policy::ivi_policies */ +}; + +struct ivi_policy_api { + size_t struct_size; + + bool (*surface_create)(struct ivi_surface *surf, void *user_data); + bool (*surface_commited)(struct ivi_surface *surf, void *user_data); + bool (*surface_activate)(struct ivi_surface *surf, void *user_data); + + bool (*surface_activate_by_default)(struct ivi_surface *surf, void *user_data); + + /** see also ivi_policy_add(). If set this will be executed before + * adding a new policy rule */ + bool (*policy_rule_allow_to_add)(void *user_data); + + /** this callback will be executed when a there's a policy state change */ + void (*policy_rule_try_event)(struct ivi_a_policy *a_policy); +}; + +struct ivi_policy { + struct ivi_compositor *ivi; + /* user-defined hooks */ + struct ivi_policy_api api; + void *user_data; + + /* represents the policy rules */ + struct wl_list policies; /* ivi_a_policy::link */ + + /* no state update chnage is being done as long as we have the same + * state */ + uint32_t current_state; + uint32_t previous_state; + + /* guards against current in change in progress */ + bool state_change_in_progress; + + /* additional states which can be verified in + * ivi_policy_api::policy_rule_try_event() */ + struct wl_list states; /* state_event::link */ + struct wl_list events; /* state_event::link */ + + /* necessary to for signaling the state change */ + struct wl_listener listener_check_policies; + struct wl_signal signal_state_change; +}; + + +/** Initialize the policy setup + * + * Policy engine should call ivi_policy_create() with its own ivi_policy_api + * setup. + */ +struct ivi_policy * +ivi_policy_create(struct ivi_compositor *compositor, + const struct ivi_policy_api *api, void *user_data); + +/** Destroys the policy setup + * + */ +void +ivi_policy_destroy(struct ivi_policy *ivi_policy); + +/** Add a policy rule. + * + * ivi_policy_api::policy_rule_allow_to_add() can be used to limit adding + * policy rules. + * + * Returns 0 in case of success, or -1 in case of failure. + * + */ +int +ivi_policy_add(struct ivi_policy *policy, const char *app_id, uint32_t state, + uint32_t event, uint32_t timeout, struct wl_resource *output_res); + +/** Trigger a state change. This should be called **each time** there is a need + * to apply the policy rules. + * + * Returns 0 in case of success, or -1 in case of failure. + */ +int +ivi_policy_state_change(struct ivi_policy *policy, uint32_t state); + + +/** Add a new state. The state can be verified in ivi_policy_api::policy_rule_try_event() + * + */ +void +ivi_policy_add_state(struct ivi_policy *policy, uint32_t state, const char *value); + +/** Add a new event. The event can be verified in ivi_policy_api::policy_rule_try_event() + * + */ +void +ivi_policy_add_event(struct ivi_policy *policy, uint32_t state, const char *value); + +/** Initialize the policy. Not implemented. + * + * Should be implemented by the policy engine. A single policy engine can be used + * at one time. + * + * Returns 0 in case of success, or -1 in case of failure. + */ +int +ivi_policy_init(struct ivi_compositor *ivi); + +#endif |