Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 57 additions & 23 deletions plugins/animate/animate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,46 +295,80 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_
wf::animation_description_t duration;
};

view_animation_t get_animation_for_view(
wf::option_wrapper_t<std::string>& anim_type, wayfire_view view)

view_animation_t get_animation_for_view(const std::string& anim_type,
wf::option_wrapper_t<std::string>& anim_type_cfg, wayfire_view view)
{
/* Determine the animation for the given view.
* Note that the matcher plugin might not have been loaded, so
* we need to have a fallback algorithm */
std::string desired_effect_name;
std::optional<wf::animation_description_t> desired_duration;

// Default animation options (open/close/minimize_animation + enabled_for)
if (animation_enabled_for.matches(view))
{
desired_effect_name = (std::string)anim_type_cfg;
}

// Specialized animation options (fade/zoom/fire enabled for specific view classes)
if (fade_enabled_for.matches(view))
{
return {"fade", fade_duration};
desired_effect_name = "fade";
desired_duration = fade_duration.value();
}

if (zoom_enabled_for.matches(view))
{
return {"zoom", zoom_duration};
desired_effect_name = "zoom";
desired_duration = zoom_duration.value();
}

if (fire_enabled_for.matches(view))
{
return {"fire", fire_duration};
desired_effect_name = "fire";
desired_duration = fire_duration.value();
}

if (animation_enabled_for.matches(view))
// Lastly, check view properties. They take the highest priority.
const std::string anim_type_prop = anim_type + "-animation-type";
std::optional<std::string> type_prop = view->get_property<std::string>(anim_type_prop);
desired_effect_name = type_prop.value_or(desired_effect_name);

const std::string anim_duration_prop = anim_type + "-animation-duration";
std::optional<std::string> duration_prop_str =
view->get_property<std::string>(anim_duration_prop);
if (auto duration_prop_str = view->get_property<std::string>(anim_duration_prop))
{
if (!effects_registry->effects.count(anim_type))
auto duration_prop =
wf::option_type::from_string<wf::animation_description_t>(*duration_prop_str);

if (duration_prop)
{
if ((std::string)anim_type != "none")
{
LOGE("Unknown animation type: \"", (std::string)anim_type, "\"");
}
desired_duration = duration_prop;
} else
{
LOGE("Invalid animation duration property \"", *duration_prop_str, "\"");
}
}

return {"none", wf::animation_description_t{0, {}, ""}};
if (!effects_registry->effects.count(desired_effect_name))
{
if ((std::string)desired_effect_name != "none")
{
LOGE("Unknown animation type: \"", desired_effect_name, "\"");
}

return {
.animation_name = anim_type,
.duration = effects_registry->effects[anim_type].default_duration().value_or(default_duration)
};
return {"none", wf::animation_description_t{0, {}, ""}};
}

if (!desired_duration.has_value())
{
desired_duration =
effects_registry->effects[desired_effect_name].default_duration().value_or(default_duration);
}

return {"none", wf::animation_description_t{0, {}, ""}};
return {
.animation_name = desired_effect_name,
.duration = desired_duration.value(),
};
}

std::string get_map_animation_cdata_name(std::string animation_name)
Expand Down Expand Up @@ -374,23 +408,23 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_
/* TODO: enhance - add more animations */
wf::signal::connection_t<wf::view_mapped_signal> on_view_mapped = [=] (wf::view_mapped_signal *ev)
{
auto animation = get_animation_for_view(open_animation, ev->view);
auto animation = get_animation_for_view("open", open_animation, ev->view);
set_animation(ev->view, animation.animation_name,
wf::animate::ANIMATION_TYPE_MAP, animation.duration);
};

wf::signal::connection_t<wf::view_pre_unmap_signal> on_view_pre_unmap =
[=] (wf::view_pre_unmap_signal *ev)
{
auto animation = get_animation_for_view(close_animation, ev->view);
auto animation = get_animation_for_view("close", close_animation, ev->view);
set_animation(ev->view, animation.animation_name,
wf::animate::ANIMATION_TYPE_UNMAP, animation.duration);
};

wf::signal::connection_t<wf::view_minimize_request_signal> on_minimize_request =
[=] (wf::view_minimize_request_signal *ev)
{
auto animation = get_animation_for_view(minimize_animation, ev->view);
auto animation = get_animation_for_view("minimize", minimize_animation, ev->view);
set_animation(ev->view, minimize_animation,
ev->state ? wf::animate::ANIMATION_TYPE_MINIMIZE : wf::animate::ANIMATION_TYPE_RESTORE,
animation.duration);
Expand Down
5 changes: 5 additions & 0 deletions plugins/animate/animate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ class animate_effects_registry_t
effects.erase(name);
}

bool has_effect(std::string name)
{
return effects.count(name);
}

std::map<std::string, effect_description_t> effects;
};
}
Expand Down
149 changes: 147 additions & 2 deletions plugins/ipc-rules/ipc-events.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,34 @@

// private API, used to make it easier to serialize output state
#include "src/core/output-layout-priv.hpp"
#include "wayfire/txn/transaction-manager.hpp"

namespace wf
{
class ipc_rules_events_methods_t : public wf::per_output_tracker_mixin_t<>
{
static constexpr const char *PRE_MAP_EVENT = "view-pre-map";

public:
void init_events(ipc::method_repository_t *method_repository)
{
method_repository->register_method("window-rules/events/watch", on_client_watch);
method_repository->register_method("window-rules/unblock-map", on_client_unblock_map);
method_repository->connect(&on_client_disconnected);

signal_map[PRE_MAP_EVENT] = signal_registration_handler{
.register_core = [=] () { wf::get_core().tx_manager->connect(&on_new_tx); },
.unregister = [=] () { on_new_tx.disconnect(); },
.auto_register = false,
};

init_output_tracking();
}

void fini_events(ipc::method_repository_t *method_repository)
{
method_repository->unregister_method("window-rules/events/watch");
method_repository->unregister_method("window-rules/unblock-map");
fini_output_tracking();
}

Expand All @@ -51,6 +63,7 @@ class ipc_rules_events_methods_t : public wf::per_output_tracker_mixin_t<>
std::function<void(wf::output_t*)> register_output = [] (wf::output_t*) {};
std::function<void()> unregister = [] () {};
int connected_count = 0;
bool auto_register = true;

void increase_count()
{
Expand Down Expand Up @@ -168,9 +181,12 @@ class ipc_rules_events_methods_t : public wf::per_output_tracker_mixin_t<>
}
} else
{
for (auto& [ev_name, _] : signal_map)
for (auto& [ev_name, ev] : signal_map)
{
subscribed_to.insert(ev_name);
if (ev.auto_register)
{
subscribed_to.insert(ev_name);
}
}
}

Expand Down Expand Up @@ -431,5 +447,134 @@ class ipc_rules_events_methods_t : public wf::per_output_tracker_mixin_t<>
ev->output ? wset_to_json(ev->output->wset().get()) : json_t::null();
send_event_to_subscribes(data, data["event"]);
};

class ipc_delay_object_t : public txn::transaction_object_t
{
public:
ipc_delay_object_t(std::string name) : obj_name(name)
{}

std::string stringify() const override
{
return obj_name;
}

void commit() override
{
if (delay_count < 1)
{
wf::txn::emit_object_ready(this);
}
}

void apply() override
{
delay_count = 0;
}

void set_delay(int count)
{
delay_count = count;
}

void reduce_delay()
{
delay_count--;
if (delay_count == 0)
{
wf::txn::emit_object_ready(this);
}
}

bool currently_blocking() const
{
return delay_count > 0;
}

private:
std::string obj_name;
int delay_count = 0;
};

using ipc_delay_object_sptr = std::shared_ptr<ipc_delay_object_t>;
struct ipc_delay_custom_data_t : public wf::custom_data_t
{
ipc_delay_object_sptr delay_obj;
};

wf::signal::connection_t<wf::txn::new_transaction_signal> on_new_tx =
[=] (wf::txn::new_transaction_signal *ev)
{
std::vector<wayfire_toplevel_view> mapping_views;
for (auto& obj : ev->tx->get_objects())
{
if (auto toplevel = std::dynamic_pointer_cast<wf::toplevel_t>(obj))
{
if (!toplevel->current().mapped && toplevel->pending().mapped)
{
mapping_views.push_back(wf::toplevel_cast(wf::find_view_for_toplevel(toplevel)));
wf::dassert(mapping_views.back() != nullptr,
"Mapping a toplevel means there must be a corresponding view!");
}
}
}

for (auto& view : mapping_views)
{
if (!view->has_data<ipc_delay_custom_data_t>())
{
auto delay_data = std::make_unique<ipc_delay_custom_data_t>();
delay_data->delay_obj = std::make_shared<ipc_delay_object_t>(
"ipc-delay-object-for-view-" + std::to_string(view->get_id()));
view->store_data<ipc_delay_custom_data_t>(std::move(delay_data));
}

auto delay_obj = view->get_data<ipc_delay_custom_data_t>()->delay_obj;
if (delay_obj->currently_blocking())
{
// Already blocked for mapping.
// This can happen if we have another transaction while the map is being blocked (for example
// IPC client adjusts the geometry of the view, we shouldn't block again).
continue;
}

// View is about to be mapped. We should notify the IPC clients which care about this
// that a view is ready for mapping.
send_view_to_subscribes(view, PRE_MAP_EVENT);

// Aside from sending a notification, we artificially delay the mapping of the view until
// the IPC clients notify us that they are ready with the view setup.
// Detail: we first commit the delay obj on its own. This should be a new tx which
// goes immediately to commit.
const int connected_count = signal_map[PRE_MAP_EVENT].connected_count;
delay_obj->set_delay(connected_count);
wf::get_core().tx_manager->schedule_object(delay_obj);

// Next, we add the delay obj to the current transaction, so that the toplevel map is
// delayed until the delay obj is ready (and it becomes ready when all IPC clients say
// they have set up the view).
ev->tx->add_object(delay_obj);
}
};

wf::ipc::method_callback_full on_client_unblock_map =
[=] (wf::json_t data, wf::ipc::client_interface_t *client)
{
auto id = wf::ipc::json_get_uint64(data, "id");
if (auto view = wf::ipc::find_view_by_id(id))
{
if (!view->has_data<ipc_delay_custom_data_t>())
{
return wf::ipc::json_error("View with id " + std::to_string(id) +
" is not being delayed for mapping");
}

auto delay_obj = view->get_data<ipc_delay_custom_data_t>()->delay_obj;
delay_obj->reduce_delay();
return wf::ipc::json_ok();
}

return wf::ipc::json_error("No such view with id " + std::to_string(id));
};
};
}
41 changes: 41 additions & 0 deletions plugins/ipc-rules/ipc-rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ipc_rules_t : public wf::plugin_interface_t,
method_repository->register_method("window-rules/get-focused-view", get_focused_view);
method_repository->register_method("window-rules/get-focused-output", get_focused_output);
method_repository->register_method("window-rules/close-view", close_view);
method_repository->register_method("window-rules/set-view-property", set_view_property);

init_input_methods(method_repository.get());
init_utility_methods(method_repository.get());
Expand All @@ -57,6 +58,7 @@ class ipc_rules_t : public wf::plugin_interface_t,
method_repository->unregister_method("window-rules/get-focused-output");
method_repository->unregister_method("window-rules/get-cursor-position");
method_repository->unregister_method("window-rules/close-view");
method_repository->unregister_method("window-rules/set-view-property");

fini_input_methods(method_repository.get());
fini_utility_methods(method_repository.get());
Expand Down Expand Up @@ -151,6 +153,45 @@ class ipc_rules_t : public wf::plugin_interface_t,
return wf::ipc::json_error("no such view");
};

wf::ipc::method_callback set_view_property = [=] (wf::json_t data)
{
auto id = wf::ipc::json_get_uint64(data, "id");
auto view = wf::ipc::find_view_by_id(id);
if (!view)
{
return wf::ipc::json_error("no such view");
}

auto property = wf::ipc::json_get_string(data, "property");
if (!data.has_member("value"))
{
return wf::ipc::json_error("missing value field");
}

auto value = data["value"];
if (value.is_bool())
{
view->set_property<bool>(property, value.as_bool());
} else if (value.is_uint64())
{
view->set_property<uint64_t>(property, value.as_uint64());
} else if (value.is_int64())
{
view->set_property<int64_t>(property, value.as_int64());
} else if (value.is_string())
{
view->set_property<std::string>(property, value.as_string());
} else if (value.is_double())
{
view->set_property<double>(property, value.as_double());
} else
{
return wf::ipc::json_error("unsupported value type");
}

return wf::ipc::json_ok();
};

wf::ipc::method_callback list_outputs = [=] (wf::json_t)
{
wf::json_t response = wf::json_t::array();
Expand Down
Loading