diff --git a/plugins/animate/animate.cpp b/plugins/animate/animate.cpp index 845e9ba51..e7c657992 100644 --- a/plugins/animate/animate.cpp +++ b/plugins/animate/animate.cpp @@ -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& anim_type, wayfire_view view) + + view_animation_t get_animation_for_view(const std::string& anim_type, + wf::option_wrapper_t& 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 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 type_prop = view->get_property(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 duration_prop_str = + view->get_property(anim_duration_prop); + if (auto duration_prop_str = view->get_property(anim_duration_prop)) { - if (!effects_registry->effects.count(anim_type)) + auto duration_prop = + wf::option_type::from_string(*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) @@ -374,7 +408,7 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_ /* TODO: enhance - add more animations */ wf::signal::connection_t 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); }; @@ -382,7 +416,7 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_ wf::signal::connection_t 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); }; @@ -390,7 +424,7 @@ class wayfire_animation : public wf::plugin_interface_t, private wf::per_output_ wf::signal::connection_t 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); diff --git a/plugins/animate/animate.hpp b/plugins/animate/animate.hpp index 08f5c7fce..c4b02d8d0 100644 --- a/plugins/animate/animate.hpp +++ b/plugins/animate/animate.hpp @@ -69,6 +69,11 @@ class animate_effects_registry_t effects.erase(name); } + bool has_effect(std::string name) + { + return effects.count(name); + } + std::map effects; }; } diff --git a/plugins/ipc-rules/ipc-events.hpp b/plugins/ipc-rules/ipc-events.hpp index a6cad6ed2..7cb0409f5 100644 --- a/plugins/ipc-rules/ipc-events.hpp +++ b/plugins/ipc-rules/ipc-events.hpp @@ -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(); } @@ -51,6 +63,7 @@ class ipc_rules_events_methods_t : public wf::per_output_tracker_mixin_t<> std::function register_output = [] (wf::output_t*) {}; std::function unregister = [] () {}; int connected_count = 0; + bool auto_register = true; void increase_count() { @@ -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); + } } } @@ -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; + struct ipc_delay_custom_data_t : public wf::custom_data_t + { + ipc_delay_object_sptr delay_obj; + }; + + wf::signal::connection_t on_new_tx = + [=] (wf::txn::new_transaction_signal *ev) + { + std::vector mapping_views; + for (auto& obj : ev->tx->get_objects()) + { + if (auto toplevel = std::dynamic_pointer_cast(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()) + { + auto delay_data = std::make_unique(); + delay_data->delay_obj = std::make_shared( + "ipc-delay-object-for-view-" + std::to_string(view->get_id())); + view->store_data(std::move(delay_data)); + } + + auto delay_obj = view->get_data()->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()) + { + return wf::ipc::json_error("View with id " + std::to_string(id) + + " is not being delayed for mapping"); + } + + auto delay_obj = view->get_data()->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)); + }; }; } diff --git a/plugins/ipc-rules/ipc-rules.cpp b/plugins/ipc-rules/ipc-rules.cpp index d38f7f11a..048abbe6d 100644 --- a/plugins/ipc-rules/ipc-rules.cpp +++ b/plugins/ipc-rules/ipc-rules.cpp @@ -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()); @@ -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()); @@ -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(property, value.as_bool()); + } else if (value.is_uint64()) + { + view->set_property(property, value.as_uint64()); + } else if (value.is_int64()) + { + view->set_property(property, value.as_int64()); + } else if (value.is_string()) + { + view->set_property(property, value.as_string()); + } else if (value.is_double()) + { + view->set_property(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(); diff --git a/plugins/single_plugins/place.cpp b/plugins/single_plugins/place.cpp index 80ffb9940..38b84afee 100644 --- a/plugins/single_plugins/place.cpp +++ b/plugins/single_plugins/place.cpp @@ -1,96 +1,140 @@ #include "wayfire/signal-provider.hpp" #include "wayfire/toplevel-view.hpp" -#include +#include +#include "wayfire/txn/transaction-manager.hpp" +#include "wayfire/plugin.hpp" #include #include #include #include #include -class wayfire_place_window : public wf::per_output_plugin_instance_t +class wayfire_place_cascade_data : public wf::custom_data_t { - wf::signal::connection_t on_view_mapped = [=] (wf::view_mapped_signal *ev) + public: + int x = 0; + int y = 0; +}; + +class wayfire_place_window : public wf::plugin_interface_t +{ + wf::signal::connection_t on_new_tx = + [=] (wf::txn::new_transaction_signal *ev) { - auto toplevel = wf::toplevel_cast(ev->view); + // For each transaction, we need to consider what happens with participating views + for (const auto& obj : ev->tx->get_objects()) + { + auto toplevel = std::dynamic_pointer_cast(obj); + if (!toplevel || !map_pending(toplevel)) + { + continue; + } + + auto view = wf::find_view_for_toplevel(toplevel); + if (view && should_place(view)) + { + do_place(view); + } + } + }; - if (!toplevel || toplevel->parent || toplevel->pending_fullscreen() || - toplevel->pending_tiled_edges() || ev->is_positioned) + bool map_pending(std::shared_ptr toplevel) + { + return !toplevel->current().mapped && toplevel->pending().mapped; + } + + bool should_place(wayfire_toplevel_view toplevel) + { + if (toplevel->parent) { - return; + return false; + } + + if (toplevel->pending_fullscreen() || toplevel->pending_tiled_edges()) + { + return false; + } + + if (toplevel->has_property("startup-x") || toplevel->has_property("startup-y")) + { + return false; } - ev->is_positioned = true; + if (!toplevel->get_output()) + { + return false; + } + + return true; + } + + void do_place(wayfire_toplevel_view view) + { + auto output = view->get_output(); auto workarea = output->workarea->get_workarea(); std::string mode = placement_mode; if (mode == "cascade") { - cascade(toplevel, workarea); + cascade(view, workarea); } else if (mode == "maximize") { - maximize(toplevel, workarea); + maximize(view, workarea); } else if (mode == "random") { - random(toplevel, workarea); + random(view, workarea); } else { - center(toplevel, workarea); + center(view, workarea); } - }; + } - wf::signal::connection_t workarea_changed_cb = [=] (auto) + void adjust_cascade_for_workarea(nonstd::observer_ptr cascade, + wf::geometry_t workarea) { - auto workarea = output->workarea->get_workarea(); - if ((cascade_x < workarea.x) || - (cascade_x > workarea.x + workarea.width)) + if ((cascade->x < workarea.x) || (cascade->x > workarea.x + workarea.width)) { - cascade_x = workarea.x; + cascade->x = workarea.x; } - if ((cascade_y < workarea.y) || - (cascade_y > workarea.y + workarea.height)) + if ((cascade->y < workarea.y) || (cascade->y > workarea.y + workarea.height)) { - cascade_y = workarea.y; + cascade->y = workarea.y; } - }; + } wf::option_wrapper_t placement_mode{"place/mode"}; - int cascade_x, cascade_y; - public: void init() override { - auto workarea = output->workarea->get_workarea(); - cascade_x = workarea.x; - cascade_y = workarea.y; - - output->connect(&workarea_changed_cb); - output->connect(&on_view_mapped); + wf::get_core().tx_manager->connect(&on_new_tx); } - void cascade(wayfire_toplevel_view & view, wf::geometry_t workarea) + void cascade(wayfire_toplevel_view view, wf::geometry_t workarea) { wf::geometry_t window = view->get_pending_geometry(); + auto cascade = view->get_output()->get_data_safe(); + adjust_cascade_for_workarea(cascade, workarea); - if ((cascade_x + window.width > workarea.x + workarea.width) || - (cascade_y + window.height > workarea.y + workarea.height)) + if ((cascade->x + window.width > workarea.x + workarea.width) || + (cascade->y + window.height > workarea.y + workarea.height)) { - cascade_x = workarea.x; - cascade_y = workarea.y; + cascade->x = workarea.x; + cascade->y = workarea.y; } - view->move(cascade_x, cascade_y); + view->toplevel()->pending().geometry.x = cascade->x; + view->toplevel()->pending().geometry.y = cascade->y; - cascade_x += workarea.width * .03; - cascade_y += workarea.height * .03; + cascade->x += workarea.width * .03; + cascade->y += workarea.height * .03; } void random(wayfire_toplevel_view & view, wf::geometry_t workarea) { wf::geometry_t window = view->get_pending_geometry(); wf::geometry_t area; - int pos_x, pos_y; area.x = workarea.x; area.y = workarea.y; @@ -104,18 +148,15 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t return; } - pos_x = rand() % area.width + area.x; - pos_y = rand() % area.height + area.y; - - view->move(pos_x, pos_y); + view->toplevel()->pending().geometry.x = rand() % area.width + area.x; + view->toplevel()->pending().geometry.y = rand() % area.height + area.y; } void center(wayfire_toplevel_view & view, wf::geometry_t workarea) { wf::geometry_t window = view->get_pending_geometry(); - window.x = workarea.x + (workarea.width / 2) - (window.width / 2); - window.y = workarea.y + (workarea.height / 2) - (window.height / 2); - view->move(window.x, window.y); + view->toplevel()->pending().geometry.x = workarea.x + (workarea.width / 2) - (window.width / 2); + view->toplevel()->pending().geometry.y = workarea.y + (workarea.height / 2) - (window.height / 2); } void maximize(wayfire_toplevel_view & view, wf::geometry_t workarea) @@ -124,4 +165,4 @@ class wayfire_place_window : public wf::per_output_plugin_instance_t } }; -DECLARE_WAYFIRE_PLUGIN(wf::per_output_plugin_t); +DECLARE_WAYFIRE_PLUGIN(wayfire_place_window); diff --git a/src/api/wayfire/object.hpp b/src/api/wayfire/object.hpp index 0b09d2165..0f065b823 100644 --- a/src/api/wayfire/object.hpp +++ b/src/api/wayfire/object.hpp @@ -2,6 +2,7 @@ #define OBJECT_HPP #include +#include #include #include #include @@ -24,6 +25,13 @@ class custom_data_t custom_data_t& operator =(const custom_data_t& other) = default; }; +template +class property_data_t : public custom_data_t +{ + public: + T value; +}; + /** * A base class for "objects". Objects provide signals and ways for plugins to * store custom data about the object. @@ -114,6 +122,44 @@ class object_base_t virtual ~object_base_t(); + bool has_property(std::string name) + { + return has_data(name); + } + + template + std::optional get_property(std::string name) + { + auto data = get_data>(name); + if (!data) + { + return {}; + } + + return data->value; + } + + template + void set_property(std::string name, T value) + { + auto data = get_data>(name); + if (has_data(name) && !data) + { + _warn_wrong_type(name); + return; + } + + if (!data) + { + auto prop = std::make_unique>(); + prop->value = value; + store_data>(std::move(prop), name); + } else + { + data->value = value; + } + } + object_base_t(const object_base_t &) = delete; object_base_t(object_base_t &&) = delete; object_base_t& operator =(const object_base_t&) = delete; @@ -135,6 +181,8 @@ class object_base_t /** Store the given data under the given name */ void _store_data(std::unique_ptr data, std::string name); + void _warn_wrong_type(std::string name); + class obase_impl; std::unique_ptr obase_priv; }; diff --git a/src/api/wayfire/plugin.hpp b/src/api/wayfire/plugin.hpp index aaca162bb..16b4eeb44 100644 --- a/src/api/wayfire/plugin.hpp +++ b/src/api/wayfire/plugin.hpp @@ -107,7 +107,7 @@ using wayfire_plugin_load_func = wf::plugin_interface_t * (*)(); /** * The version is defined as macro as well, to allow conditional compilation. */ -#define WAYFIRE_API_ABI_VERSION_MACRO 2025'09'09 +#define WAYFIRE_API_ABI_VERSION_MACRO 2025'09'11 /** * The version of Wayfire's API/ABI diff --git a/src/api/wayfire/signal-definitions.hpp b/src/api/wayfire/signal-definitions.hpp index 800068171..a565352d4 100644 --- a/src/api/wayfire/signal-definitions.hpp +++ b/src/api/wayfire/signal-definitions.hpp @@ -348,6 +348,7 @@ struct view_mapped_signal wayfire_view view; /* Indicates whether the position already has its initial position */ + [[deprecated("Use startup-x and startup-y properties instead")]] bool is_positioned = false; }; diff --git a/src/api/wayfire/view-helpers.hpp b/src/api/wayfire/view-helpers.hpp index f975b1434..9331f2d25 100644 --- a/src/api/wayfire/view-helpers.hpp +++ b/src/api/wayfire/view-helpers.hpp @@ -5,6 +5,7 @@ #include "wayfire/scene.hpp" #include #include +#include // This file contains helper functions which are helpful when working with views. Most of the operations are // simply wrappers around more low-level functionality provided by views, scenegraph, etc. @@ -47,7 +48,9 @@ wayfire_toplevel_view find_topmost_parent(wayfire_toplevel_view v); namespace view_implementation { void emit_toplevel_state_change_signals(wayfire_toplevel_view view, const wf::toplevel_state_t& old_state); +[[deprecated("has position has been deprecated, use emit_view_map_signal(wayfire_view) instead")]] void emit_view_map_signal(wayfire_view view, bool has_position); +void emit_view_map_signal(wayfire_view view); void emit_ping_timeout_signal(wayfire_view view); void emit_geometry_changed_signal(wayfire_toplevel_view view, wf::geometry_t old_geometry); void emit_title_changed_signal(wayfire_view view); diff --git a/src/core/object.cpp b/src/core/object.cpp index 2c3c62cc5..8f698b840 100644 --- a/src/core/object.cpp +++ b/src/core/object.cpp @@ -2,6 +2,7 @@ #include #include #include +#include struct wf::signal::provider_t::impl { @@ -139,3 +140,9 @@ void wf::object_base_t::_clear_data() erase_data(key); } } + +void wf::object_base_t::_warn_wrong_type(std::string name) +{ + LOGW("Tried to access data with name '", name, "' using the wrong type. Actual type: ", + typeid(_fetch_data(name)).name()); +} diff --git a/src/core/txn/transaction-manager-impl.hpp b/src/core/txn/transaction-manager-impl.hpp index a795c101a..805dfa99e 100644 --- a/src/core/txn/transaction-manager-impl.hpp +++ b/src/core/txn/transaction-manager-impl.hpp @@ -41,19 +41,26 @@ struct wf::txn::transaction_manager_t::impl { while (true) { - const size_t start_size = tx->get_objects().size(); + bool size_changed = false; for (auto& existing : pending) { if (transactions_intersect(existing, tx)) { + const size_t start_size = tx->get_objects().size(); for (auto& obj : existing->get_objects()) { tx->add_object(obj); } + + if (start_size != tx->get_objects().size()) + { + LOGC(TXN, "Merged transaction ", existing.get(), " into ", tx.get()); + size_changed = true; + } } } - if (start_size == tx->get_objects().size()) + if (!size_changed) { // No new objects were added in the last iteration => done break; diff --git a/src/view/compositor-view.cpp b/src/view/compositor-view.cpp index 0c3bda807..620c0637f 100644 --- a/src/view/compositor-view.cpp +++ b/src/view/compositor-view.cpp @@ -119,7 +119,7 @@ std::shared_ptr wf::color_rect_view_t::create(view_role_t } } - wf::view_implementation::emit_view_map_signal(self, true); + wf::view_implementation::emit_view_map_signal(self); return self; } diff --git a/src/view/view-impl.cpp b/src/view/view-impl.cpp index 207875a1f..d95702b55 100644 --- a/src/view/view-impl.cpp +++ b/src/view/view-impl.cpp @@ -11,7 +11,7 @@ #include "wayfire/output-layout.hpp" #include "wayfire/window-manager.hpp" #include "wayfire/workarea.hpp" -#include "wayfire/workspace-set.hpp" +#include "wayfire/workspace-set.hpp" // IWYU pragma: keep #include #include #include @@ -32,6 +32,20 @@ void wf::view_implementation::emit_view_map_signal(wayfire_view view, bool has_p wf::get_core().emit(&data); } +void wf::view_implementation::emit_view_map_signal(wayfire_view view) +{ + wf::view_mapped_signal data; + data.view = view; + + view->emit(&data); + if (view->get_output()) + { + view->get_output()->emit(&data); + } + + wf::get_core().emit(&data); +} + void wf::view_implementation::emit_ping_timeout_signal(wayfire_view view) { wf::view_ping_timeout_signal data; @@ -56,7 +70,7 @@ void wf::view_implementation::emit_geometry_changed_signal(wayfire_toplevel_view void wf::view_interface_t::emit_view_map() { - view_implementation::emit_view_map_signal(self(), false); + view_implementation::emit_view_map_signal(self()); } void wf::view_interface_t::emit_view_unmap() diff --git a/src/view/xwayland/xwayland-toplevel-view.hpp b/src/view/xwayland/xwayland-toplevel-view.hpp index 2f7252c58..38369d779 100644 --- a/src/view/xwayland/xwayland-toplevel-view.hpp +++ b/src/view/xwayland/xwayland-toplevel-view.hpp @@ -53,9 +53,12 @@ class wayfire_xwayland_view : public wf::toplevel_view_interface_t, public wayfi wlr_xwayland_surface_configure(xw, ev->x, ev->y, ev->width, ev->height); if ((ev->mask & XCB_CONFIG_WINDOW_X) && (ev->mask & XCB_CONFIG_WINDOW_Y)) { - this->self_positioned = true; - toplevel->pending().geometry.x = ev->x - output_origin.x; - toplevel->pending().geometry.y = ev->y - output_origin.y; + int sx = ev->x - output_origin.x; + int sy = ev->y - output_origin.y; + this->set_property("startup-x", sx); + this->set_property("startup-y", sy); + toplevel->pending().geometry.x = sx; + toplevel->pending().geometry.y = sy; } return; @@ -305,8 +308,7 @@ class wayfire_xwayland_view : public wf::toplevel_view_interface_t, public wayfi * manager determine this. We try to heuristically guess which of the * two cases we're dealing with by checking whether we have received * a valid ConfigureRequest before mapping */ - bool client_self_positioned = self_positioned; - wf::view_implementation::emit_view_map_signal(self(), client_self_positioned); + wf::view_implementation::emit_view_map_signal(self()); } void store_xw_geometry_unmapped() diff --git a/src/view/xwayland/xwayland-view-base.hpp b/src/view/xwayland/xwayland-view-base.hpp index 150f105f3..5f468c130 100644 --- a/src/view/xwayland/xwayland-view-base.hpp +++ b/src/view/xwayland/xwayland-view-base.hpp @@ -19,9 +19,6 @@ class wayfire_xwayland_view_internal_base : public wf::xwayland_view_base_t protected: wf::wl_listener_wrapper on_configure; - /** The geometry requested by the client */ - bool self_positioned = false; - public: wayfire_xwayland_view_internal_base(wlr_xwayland_surface *xww) : xwayland_view_base_t(xww) {}