Skip to content

Conversation

@marcusbritanicus
Copy link
Contributor

@marcusbritanicus marcusbritanicus commented Sep 10, 2025

@ammen99 I did an initial test of this plugin. I am able to list the toplevels, and get their titles and app-ids, which is what this is supposed to do. Please let me know if there are any changes required.

Copy link
Member

@ammen99 ammen99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things I noticed

@marcusbritanicus marcusbritanicus marked this pull request as draft September 11, 2025 19:20
@marcusbritanicus
Copy link
Contributor Author

I am marking this as draft because I seem to encounter a crash when both plugins are active. Just closing a toplevel is sufficient to trigger this crash. I think there is some memory corruption that's taking place. I'll mark this as ready once I can fix the crash.

@ammen99 Here is the ASAN output if it helps:

==27507==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000008 (pc 0x7fce79178fa4 bp 0x7ffea02ed610 sp 0x7ffea02ed5e8 T0)
==27507==The signal is caused by a READ memory access.                                                                                                                                                                                                                      
==27507==Hint: address points to the zero page.
    #0 0x7fce79178fa4 in wl_list_insert (/usr/lib64/libwayland-server.so.0+0x7fa4) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #1 0x7fce79179389 in wl_signal_emit_mutable (/usr/lib64/libwayland-server.so.0+0x8389) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #2 0x7fce7894c6d0 in wlr_ext_foreign_toplevel_handle_v1_destroy (/usr/lib64/libwlroots-0.19.so+0xae6d0) (BuildId: ea3fa192d7ba550ef40691537c3dfd25f46f59ae)
    #3 0x7bce52683f37 in std::_Rb_tree<nonstd::observer_ptr<wf::toplevel_view_interface_t>, std::pair<nonstd::observer_ptr<wf::toplevel_view_interface_t> const, std::unique_ptr<wayfire_foreign_toplevel, std::default_delete<wayfire_foreign_toplevel> > >, std::_Select1st<std::pair<nonstd::observer_ptr<wf::toplevel_view_interface_t> const, std::unique_ptr<wayfire_foreign_toplevel, std::default_delete<wayfire_foreign_toplevel> > > >, std::less<nonstd::observer_ptr<wf::toplevel_view_interface_t> >, std::allocator<std::pair<nonstd::observer_ptr<wf::toplevel_view_interface_t> const, std::unique_ptr<wayfire_foreign_toplevel, std::default_delete<wayfire_foreign_toplevel> > > > >::_M_erase(std::_Rb_tree_node<std::pair<nonstd::observer_ptr<wf::toplevel_view_interface_t> const, std::unique_ptr<wayfire_foreign_toplevel, std::default_delete<wayfire_foreign_toplevel> > > >*) [clone .isra.0] (/usr/lib/wayfire/libforeign-toplevel.so+0xcf37) (BuildId: 8289d70075a23a8ad536d58a3c05f61e4ed19914)
    #4 0x7bce52687442 in std::_Function_handler<void (wf::view_unmapped_signal*), wayfire_foreign_toplevel_protocol_impl::on_view_unmapped::{lambda(wf::view_unmapped_signal*)#1}>::_M_invoke(std::_Any_data const&, wf::view_unmapped_signal*&&) (/usr/lib/wayfire/libforeign-toplevel.so+0x10442) (BuildId: 8289d70075a23a8ad536d58a3c05f61e4ed19914)
    #5 0x561cb0d01b89 in std::_Function_handler<void (wf::signal::connection_base_t*), wf::signal::provider_t::emit<wf::view_unmapped_signal>(wf::view_unmapped_signal*)::{lambda(wf::signal::connection_base_t*)#1}>::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x4d0b89) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #6 0x561cb0aa139b in std::_Function_handler<void (wf::signal::connection_base_t*&), std::function<void (wf::signal::connection_base_t*)> >::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x27039b) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #7 0x561cb0a9d1cf in wf::signal::provider_t::for_each_connection(std::type_index, std::function<void (wf::signal::connection_base_t*)>) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x26c1cf) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #8 0x561cb0cf251c in wf::view_interface_t::emit_view_unmap() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x4c151c) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #9 0x561cb0d5365d in wf::xdg_toplevel_view_base_t::unmap() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x52265d) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #10 0x561cb0d52eb4 in wf::xdg_toplevel_view_t::handle_toplevel_state_changed(wf::toplevel_state_t) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x521eb4) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #11 0x561cb0d533d5 in std::_Function_handler<void (wf::xdg_toplevel_applied_state_signal*), wf::xdg_toplevel_view_t::xdg_toplevel_view_t(wlr_xdg_toplevel*)::{lambda(wf::xdg_toplevel_applied_state_signal*)#1}>::_M_invoke(std::_Any_data const&, wf::xdg_toplevel_applied_state_signal*&&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x5223d5) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #12 0x561cb0d3a4c9 in std::_Function_handler<void (wf::signal::connection_base_t*), wf::signal::provider_t::emit<wf::xdg_toplevel_applied_state_signal>(wf::xdg_toplevel_applied_state_signal*)::{lambda(wf::signal::connection_base_t*)#1}>::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x5094c9) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #13 0x561cb0aa139b in std::_Function_handler<void (wf::signal::connection_base_t*&), std::function<void (wf::signal::connection_base_t*)> >::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x27039b) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #14 0x561cb0a9d1cf in wf::signal::provider_t::for_each_connection(std::type_index, std::function<void (wf::signal::connection_base_t*)>) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x26c1cf) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #15 0x561cb0d35ac5 in wf::xdg_toplevel_t::apply() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x504ac5) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #16 0x561cb0b608f5 in wf::txn::transaction_t::apply(bool) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x32f8f5) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #17 0x561cb0b62a26 in std::_Function_handler<void (wf::txn::object_ready_signal*), wf::txn::transaction_t::transaction_t(unsigned long, std::function<void (unsigned long, std::function<void ()>)>)::{lambda(wf::txn::object_ready_signal*)#1}>::_M_invoke(std::_Any_data const&, wf::txn::object_ready_signal*&&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x331a26) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #18 0x561cb0b63229 in std::_Function_handler<void (wf::signal::connection_base_t*), wf::signal::provider_t::emit<wf::txn::object_ready_signal>(wf::txn::object_ready_signal*)::{lambda(wf::signal::connection_base_t*)#1}>::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x332229) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #19 0x561cb0aa139b in std::_Function_handler<void (wf::signal::connection_base_t*&), std::function<void (wf::signal::connection_base_t*)> >::_M_invoke(std::_Any_data const&, wf::signal::connection_base_t*&) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x27039b) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #20 0x561cb0a9d1cf in wf::signal::provider_t::for_each_connection(std::type_index, std::function<void (wf::signal::connection_base_t*)>) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x26c1cf) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #21 0x561cb0b5d7f1 in wf::txn::emit_object_ready(wf::txn::transaction_object_t*) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x32c7f1) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #22 0x561cb0d39be9 in wf::xdg_toplevel_t::commit() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x508be9) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #23 0x561cb0b6136d in wf::txn::transaction_t::commit() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x33036d) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #24 0x561cb0b883a4 in wf::txn::transaction_manager_t::impl::consider_commit() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x3573a4) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #25 0x561cb0b9137f in wf::txn::transaction_manager_t::impl::schedule_transaction(std::unique_ptr<wf::txn::transaction_t, std::default_delete<wf::txn::transaction_t> >) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x36037f) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #26 0x561cb0b8325a in wf::txn::transaction_manager_t::schedule_transaction(std::unique_ptr<wf::txn::transaction_t, std::default_delete<wf::txn::transaction_t> >) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x35225a) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #27 0x561cb0b83aa1 in wf::txn::transaction_manager_t::schedule_object(std::shared_ptr<wf::txn::transaction_object_t>) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x352aa1) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #28 0x561cb0d553e0 in wf::xdg_toplevel_view_t::start_unmap_tx() (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x5243e0) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #29 0x561cb09a630c in wf::wl_listener_wrapper::emit(void*) (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x17530c) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #30 0x7fce791793bd in wl_signal_emit_mutable (/usr/lib64/libwayland-server.so.0+0x83bd) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #31 0x7fce78941848 in wlr_surface_unmap (/usr/lib64/libwlroots-0.19.so+0xa3848) (BuildId: ea3fa192d7ba550ef40691537c3dfd25f46f59ae)
    #32 0x7fce789435d4 in surface_commit_state (/usr/lib64/libwlroots-0.19.so+0xa55d4) (BuildId: ea3fa192d7ba550ef40691537c3dfd25f46f59ae)
    #33 0x7fce77cb8a65  (/usr/lib64/libffi.so.8+0x7a65) (BuildId: 1578162d9ffff7c563238ef5b8c2778423f8d3e7)
    #34 0x7fce77cb576a  (/usr/lib64/libffi.so.8+0x476a) (BuildId: 1578162d9ffff7c563238ef5b8c2778423f8d3e7)
    #35 0x7fce77cb802d in ffi_call (/usr/lib64/libffi.so.8+0x702d) (BuildId: 1578162d9ffff7c563238ef5b8c2778423f8d3e7)
    #36 0x7fce791774c9  (/usr/lib64/libwayland-server.so.0+0x64c9) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #37 0x7fce7917c9fb  (/usr/lib64/libwayland-server.so.0+0xb9fb) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #38 0x7fce7917afc1 in wl_event_loop_dispatch (/usr/lib64/libwayland-server.so.0+0x9fc1) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #39 0x7fce7917ceb6 in wl_display_run (/usr/lib64/libwayland-server.so.0+0xbeb6) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f)
    #40 0x561cb0968f39 in main (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x137f39) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)
    #41 0x7fce77e2773a  (/usr/lib64/libc.so.6+0x2773a) (BuildId: 72c4460e8f271e2904abd29bc05452b3d808c62e)
    #42 0x7fce77e277fb in __libc_start_main (/usr/lib64/libc.so.6+0x277fb) (BuildId: 72c4460e8f271e2904abd29bc05452b3d808c62e)
    #43 0x561cb096d974 in _start (/home/cosmos/Softwares/Projects/Wayfire/Wayfire/.build/src/wayfire+0x13c974) (BuildId: 2e01ee3e2935a94a155ac1e7c7815471164f40d1)

==27507==Register values:
rax = 0x00007cde74b8e808  rbx = 0x00007cde74b8e800  rcx = 0x00007ffea02ed6c0  rdx = 0x00007d5e74bab980  
rdi = 0x0000000000000000  rsi = 0x00007ffea02ed5f0  rbp = 0x00007ffea02ed610  rsp = 0x00007ffea02ed5e8  
 r8 = 0x00007c0e74aeb390   r9 = 0x0000000000000000  r10 = 0x0000000000000000  r11 = 0x00007bce52699ac0  
r12 = 0x00007cde74b8e7c0  r13 = 0x0000000000000000  r14 = 0x00007fce791761c0  r15 = 0x00000f79ce721880  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/usr/lib64/libwayland-server.so.0+0x7fa4) (BuildId: cd1e5fe00f495b330c9752148f5071790f804c9f) in wl_list_insert
==27507==ABORTING

@marcusbritanicus marcusbritanicus marked this pull request as ready for review September 11, 2025 19:49
@ammen99
Copy link
Member

ammen99 commented Sep 12, 2025

@marcusbritanicus I think you are trying to reuse too much between the plugins, I meant simply the app-id generation function. If you truly want to reuse the rest as well, I think you will need to use something like the strategy pattern:

  • Have an interface toplevel_handle_interface_t or whatever you want to name it. It has functions which map directly onto wlroots functions for sending app-id, title, etc.
  • The wlr and the ext protocols each have a different implementation for the interface
  • Have a shared class which takes a unique_ptr<toplevel_handle_interface_t> (i.e the concrete implementation).

What you have currently definitely is not a good idea if you have both plugins enabled: they implement the same class in two different ways, that is generally not allowed and will lead to crashes.

Considering that there is also ext-toplevel-management, I think it is worth thinking how it will play with these abstractions ... Maybe we need to split the big shared class into two parts, each responsible for either the listing or for the management. The wlr protocol implementation then would simply instantiate both shared classes, and the ext protocols will instantiate one of each.

@marcusbritanicus
Copy link
Contributor Author

@ammen99 Thanks!! I was under the (incorrect) impression that since they're different plugins it will not matter. Anyways, I decided to make subclasses for wlr and ext plugins. All the junk values I was getting in the previous tests are now gone.

I have tested for both ext-foreign-toplevel-list and wlr-foreign-toplevel-management protocols. Both work seamlessly and no crashes.

Please have a look and tell me if is what you meant.

@marcusbritanicus
Copy link
Contributor Author

@ammen99 Incorporated all your suggestions. They were all murmurs from the old code. I have also rebased - I think I have done it properly. Please do check and tell me if everything is proper.

@ammen99
Copy link
Member

ammen99 commented Sep 29, 2025

Something seems to have gone wrong in the rebase, github reports many more changes in this PR than actually intended ..

@marcusbritanicus
Copy link
Contributor Author

@ammen99 I force-pushed the changes on top the latest master. I think now everything should be proper.

toplevel_send_state();
};

wf::wl_listener_wrapper toplevel_handle_v1_maximize_request;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that just like the void *handle field, these belong in the concrete implementations, as they are not used in the base class at all. I suppose we could also drop the function for initializing them from the interface.

Rationale: I get what you like with the current implementation, it seems that we share some code and code structure. But in reality I feel that by adding these abstractions here, we are simply removing 'duplicate' code by adding complex abstractions which are not 'natural' and make the code hard to read (to understand what is going on you have to go back and forth between the base and the derived classes).

A bit late, but if you are really keen on perfecting this, I would even suggest using the strategy design pattern. As people say, 'prefer composition over inheritance'. Have the base class which connects to signals, then pass a pointer to an interface implementation in the constructor. The interface would contain just the methods that are to be overridden (send_initial_state, toplevel_send_state, etc.). That would be best from a design perspective, but as we have been going back and forth I would also be ok with the current state, just move the listener_wrappers and their initialization functions to the derived classes.

@marcusbritanicus marcusbritanicus deleted the ext-toplevel-list branch November 11, 2025 06:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants