Wayfire Window Decorations Gone

Hi everyone!

Yesterday I updated my Pocket Reform and after the next reboot wayfire did not have window decorations anymore.

So I redirected the output to a logfile and found this line:

EE 15-10-24 17:16:09.246 - [src/core/plugin-loader.cpp:105] /usr/lib/aarch64-linux-gnu/wayfire/libfiredecor.so: API/ABI version mismatch: Wayfire is 20240826, plugin built with 20240311

The dates are both too old, but I guess it took some time for wayfire 0.9.0-2 to get published…

…or am I completely mistaken?

Thanks, georg

Hi,

wayfire 0.9.0 reached unstable 2 days ago. The reform-firedecor package was last rebuilt in april and it was built with version 0.8.0 of wayfire from back then. It probably just needs to be rebuilt with the new wayfire version. I scheduled a binNMU here: #1085169 - nmu: reform-firedecor_2023-10-23-4 - Debian Bug report logs

If you are experiencing a bug in Debian unstable, please do not hesitate to report it to the Debian bug tracker. There are not many Debian Developers reading this forum. :slight_smile:

5 Likes

Hi @josch,

thanks a lot! :grinning:

Whoops… Before, I did not realize that reform-firedecor is an official Debian package and thought it was pulled from the mntre apt repo… :man_facepalming: :grimacing: Since I am also pretty sure, that a rebuild will do the trick, unfortunately, I stopped investigating a bit too early. Thanks for the pointer and the report!

1 Like

Hello @josch,

I am just wondering what would you suggest a Pocket Reform user to do… Should wait to have reform-firedecor rebuild at official Debian package repo? Should rebuilt on the machine on hand?

How long do you expect repo to take for it to be updated? This is kind of new situation for me and I have no reference for an idea of timeline on this kind.

Thank you.

1 Like

I’m trying to fix the situation but I’m currently stuck with shaving this yak. Particularly, compiling and running this used to work:

#include <wayfire/plugin.hpp>
#include <errno.h>
#include <dlfcn.h>
#include <stdio.h>
#include <inttypes.h>
int main(int argc, char* argv[]) {
	void *handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
	if (handle == NULL) {
                fprintf(stderr, "dlopen failed: %s\n", dlerror());
		return 1;
	}
	uint32_t (*version_func)() = (uint32_t (*)())dlsym(handle, "getWayfireVersion");
	if (version_func == NULL) {
		perror("dlsym failed");
		dlclose(handle);
		return 1;
	}
	int32_t plugin_abi_version = version_func();
	if (WAYFIRE_API_ABI_VERSION != plugin_abi_version) {
		fprintf(stderr, "API/ABI version mismatch:\n");
		fprintf(stderr, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
		fprintf(stderr, "plugin is: %" PRIu32 "\n", plugin_abi_version);
		dlclose(handle);
		return 1;
	}
	fprintf(stderr, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
	fprintf(stderr, "plugin is: %" PRIu32 "\n", plugin_abi_version);
	dlclose(handle);
	return 0;
}
g++ test.c -o test -Wall -ldl $(pkg-config --cflags pixman-1) -DWLR_USE_UNSTABLE

But this now fails:

$ ./test /usr/lib/aarch64-linux-gnu/wayfire/libfiredecor.so
dlopen failed: /usr/lib/aarch64-linux-gnu/wayfire/libfiredecor.so: undefined symbol: _ZTIN2wf3txn20transaction_object_tE

Evidently, the dlopen() works when wayfire does it, so what is missing from my test program which makes this fail?

2 Likes

Thank you! I assume you have succeeded. The reform-firedecor was updated with apt upgrade a moment ago and the Window Decorations are all back.

Appreciate very much.

1 Like

I would speculate that you haven’t included something that is being referenced. But as shigeru mentioned this seems to have been resolved. If this is because of your efforts, would you care to share what the issue was?

No, the problem still persists. The firedecor plugin works fine but the test still does not. To build it, I just disabled the test.

3 Likes

I think this means the plugin host (= your test program here) needs to export some symbols that libfiredecor.so expects. In this case, you would need to provide an implementation of class wf::txn::transaction_object_t.

1 Like

Yes, unfortunately, the plugin host includes headers that define wf::txn::transaction_object_t which itself cannot be instantiated because it’s an abstract class. Creating objects of classes derived from wf::txn::transaction_object_t does not seem to have the desired effect. I’m suspecting that I am missing some certain linker options.

2 Likes

You don’t happen to have this in a branch in debian/reform-firedecor.git that I could just check out?

Ah I see, it’s in debian/tests on master. Nvm.

I gave this a stab, and IMO: no linker flags are needed, but test.c (misnomer btw) needs to export the virtual, synthesized symbols for transaction_object_t. AFAIK this would be done if test.c would declare and implement a public class implementing transaction_object_t. However g++ also has internal rules when and where to emit these symbols, and I just can’t get it to emit them.

1 Like

Thanks a lot for looking into this. Hearing that you did not find a straight forward solution either makes me feel a bit better. I mean, I’m giving university classes on C++ and I didn’t find the solution (yet)… don’t tell my students… :joy:

2 Likes

Okay, so for those who are interested in the solution to the C++ dlopen problem. There are two “solutions”. I like neither of them but here you go:

  1. link the plugin with the gold linker and pass --weak-unresolved-symbols. This will make everything work magically out-of-the-box at the cost of using a non-default linker. This solution comes from wayfire developer Scott Moreau. It can be done by putting in debian/rules:
    export DEB_LDFLAGS_MAINT_APPEND = -fuse-ld=gold -Wl,--weak-unresolved-symbols
    
  2. the other option is to link test.cc with -rdynamic. This was the flag I was missing. Without -rdynamic it does not matter how many symbols I try to provide in test.cc. The clue to provide this flag came from stackoverflow user Employed Russian. For those curios, here is a version of test.cc that will just work if compiled with -rynamic:
#include <wayfire/plugin.hpp>
#include <errno.h>
#include <dlfcn.h>
#include <memory>
#include <stdexcept>
#include <stdio.h>
#include <inttypes.h>

namespace nonstd {
	template<class W> class observer_ptr {};
}

namespace wf {
	class toplevel_t {};
	class output_t {};
	class pointf_t {};
	class custom_data_t {};
	class object_base_t {
		virtual ~object_base_t() = default;
		void erase_data(std::string);
		void _fetch_data(std::string);
		void _store_data(std::unique_ptr<custom_data_t>, std::string);
	};
	class toplevel_view_interface_t {
		virtual ~toplevel_view_interface_t() = default;
		void toplevel() const;

	};
	class view_interface_t {
		virtual ~view_interface_t() = default;
		void get_surface_root_node() const;
	};
	using wayfire_view = nonstd::observer_ptr<wf::view_interface_t>;
	class view_matcher_t {
		view_matcher_t(const std::string&);
		~view_matcher_t();
		void matches(wayfire_view);
	};
	class render_target_t {
		void logic_scissor(wlr_box) const;
		void get_orthographic_projection() const;
	};
	struct point_t {};
	struct region_t {
		region_t();
		region_t(region_t const&);
		region_t(region_t &&);
		region_t(const wlr_box&);
		region_t& operator=(const region_t&);
		region_t& operator=(region_t&&);
		region_t operator+(const point_t&) const;
		region_t operator&(const region_t&) const;
		region_t& operator|=(const wlr_box&);
		void contains_point(const point_t&) const;
		void contains_pointf(const pointf_t&) const;
		void begin() const;
		void end() const;
		void clear();
		void empty() const;
		~region_t();
	};
	class compositor_core_t {
		void get_all_views();
	};
	class wl_idle_call {
		using callback_t = std::function<void ()>;
		wl_idle_call();
		~wl_idle_call();
		void run_once(callback_t);
	};
	class output_workarea_manager_t {
		void get_workarea();
	};
	class texture_t {
		texture_t(unsigned int);
	};
	struct framebuffer_t {};
	struct color_t {};
	struct dimensions_t {};
	using geometry_t = wlr_box;
	geometry_t clamp(geometry_t, geometry_t);
	void print_trace(bool);
	void dimensions(wlr_box const&);
	void get_core();
	void find_view_for_toplevel(std::shared_ptr<wf::toplevel_t>);
	void construct_box(wf::point_t const&, wf::dimensions_t const&);
	template<bool A> class wl_timer {
		using callback_t = std::function<std::conditional_t<A, bool, void>()>;
		~wl_timer();
		void is_connected();
		void set_timeout(uint32_t timeout_ms, callback_t call);
	};
	template class wl_timer<true>;
	template class wl_timer<false>;
	namespace detail {
		void option_wrapper_debug_message(const std::string&, const std::logic_error&);
		void option_wrapper_debug_message(const std::string&, const std::runtime_error&);
	};
	namespace txn {
		class transaction_object_t {
			virtual ~transaction_object_t() = default;
		};
		using transaction_object_sptr = std::shared_ptr<transaction_object_t>;
		class transaction_t {
			void get_objects() const;
		};
		class transaction_manager_t {
			void schedule_object(transaction_object_sptr);
		};
	};
	namespace scene {
		class node_t {
		public:
			node_t();
			node_t(bool);
			virtual ~node_t() = default;
			void keyboard_refocus(wf::output_t*);
			void to_local(wf::pointf_t const&);
			void to_global(wf::pointf_t const&);
			std::string stringify() const;
			void optimize_update(unsigned int);
		};
		using node_ptr = std::shared_ptr<node_t>;
		class floating_inner_node_t {
			void set_children_list(std::vector<node_ptr>);
			virtual ~floating_inner_node_t() = default;
		};
		void update(node_ptr changed_node, uint32_t flags);
	};
	namespace signal {
		class connection_base_t {
			void disconnect();
		};
	};
};

namespace glm {
	enum qualifier {};
	template<int A, typename B, qualifier C> class vec { virtual ~vec() = default; };
	typedef vec<4, float, qualifier(0)> vec4;
	template<int A, int B, typename C, qualifier D> class mat { virtual ~mat() = default; };
	typedef mat<4, 4, float, qualifier(0)> mat4;
}

namespace OpenGL {
	void render_rectangle(wf::geometry_t, wf::color_t, glm::mat4);
	void render_texture(wf::texture_t, const wf::render_target_t&, const wf::geometry_t&, glm::vec4 color, uint32_t bits);
	void render_begin(wf::framebuffer_t const&);
	void render_begin();
	void render_end();
};

wf::texture_t::texture_t(unsigned int) {};
void wf::construct_box(wf::point_t const&, wf::dimensions_t const&) {};
void wf::output_workarea_manager_t::get_workarea() {};
void wf::find_view_for_toplevel(std::shared_ptr<wf::toplevel_t>) {};
template<bool A> wf::wl_timer<A>::~wl_timer() {};
template<bool A> void wf::wl_timer<A>::is_connected() {};
template<bool A> void wf::wl_timer<A>::set_timeout(uint32_t, wf::wl_timer<A>::callback_t) {};
void wf::detail::option_wrapper_debug_message(const std::string&, const std::logic_error&) {};
void wf::detail::option_wrapper_debug_message(const std::string&, const std::runtime_error&) {};
void wf::get_core() {};
wf::wl_idle_call::wl_idle_call() {};
wf::wl_idle_call::~wl_idle_call() {};
void wf::wl_idle_call::run_once(wf::wl_idle_call::callback_t) {};
void operator+(wlr_box const&, wf::point_t const&) {};
void operator&(wlr_box const&, wf::point_t const&) {};
void wf::signal::connection_base_t::disconnect() {};
void wf::compositor_core_t::get_all_views() {};
void wlr_box_from_pixman_box(pixman_box32 const&) {};
wf::region_t::region_t() { };
wf::region_t::region_t(wf::region_t const&) { };
wf::region_t::region_t(wf::region_t &&) { };
wf::region_t::region_t(const wlr_box&) { };
wf::region_t& wf::region_t::operator=(const wf::region_t& v) { return *this; };
wf::region_t& wf::region_t::operator=(wf::region_t&& v) { return v; };
wf::region_t wf::region_t::operator+(const point_t& v) const { return wf::region_t(); };
wf::region_t wf::region_t::operator&(const region_t& v) const { return v; };
wf::region_t& wf::region_t::operator|=(const wlr_box& v) { return *this; };
wf::region_t::~region_t() { };
void wf::region_t::contains_point(const point_t& point) const {};
void wf::region_t::contains_pointf(const pointf_t& point) const {};
void wf::region_t::begin() const { };
void wf::region_t::end() const { };
void wf::region_t::clear() { };
void wf::region_t::empty() const { };
void OpenGL::render_texture(wf::texture_t, const wf::render_target_t&, const wf::geometry_t&, glm::vec4 color, uint32_t bits) {};
void OpenGL::render_rectangle(wlr_box, wf::color_t, glm::mat4) {};
void OpenGL::render_begin(wf::framebuffer_t const&) {};
void OpenGL::render_begin() {};
void OpenGL::render_end() {};
wf::geometry_t wf::clamp(wf::geometry_t, wf::geometry_t) { return wlr_box(); };
void wf::print_trace(bool) { };
void wf::dimensions(wlr_box const&) {};
void wf::render_target_t::logic_scissor(wlr_box) const {};
void wf::render_target_t::get_orthographic_projection() const {};
wf::view_matcher_t::view_matcher_t(const std::string&) {};
void wf::view_matcher_t::matches(wf::wayfire_view) {};
wf::view_matcher_t::~view_matcher_t() {};
void wf::plugin_interface_t::init() {};
void wf::plugin_interface_t::fini() {};
wf::scene::node_t::node_t() {};
wf::scene::node_t::node_t(bool) {};
void wf::scene::update(node_ptr changed_node, uint32_t flags) {};
void wf::scene::node_t::keyboard_refocus(wf::output_t*) {};
void wf::scene::node_t::to_local(wf::pointf_t const&) {};
void wf::scene::node_t::to_global(wf::pointf_t const&) {};
std::string wf::scene::node_t::stringify() const { return ""; };
void wf::scene::node_t::optimize_update(unsigned int) { };
void wf::view_interface_t::get_surface_root_node() const {};
void wf::txn::transaction_t::get_objects() const {};
void wf::txn::transaction_manager_t::schedule_object(wf::txn::transaction_object_sptr) {};
void gl_call(const char*, uint32_t, const char*) {};
void wf::toplevel_view_interface_t::toplevel() const {};
void wf::scene::floating_inner_node_t::set_children_list(std::vector<node_ptr>) {};
void wf::object_base_t::erase_data(std::string) {};
void wf::object_base_t::_fetch_data(std::string) {};
void wf::object_base_t::_store_data(std::unique_ptr<custom_data_t>, std::string) {};

// Create one instance to make sure the vtable is not optimized out.
static wf::object_base_t *ptr1 = new wf::object_base_t();
static wf::txn::transaction_object_t *ptr = new wf::txn::transaction_object_t();
static wf::scene::node_t *ptr2 = new wf::scene::node_t();
static wf::toplevel_view_interface_t *ptr3 = new wf::toplevel_view_interface_t();
static wf::scene::floating_inner_node_t *ptr4 = new wf::scene::floating_inner_node_t();
static wf::view_interface_t *ptr5 = new wf::view_interface_t();


int main(int argc, char* argv[]) {
	void *handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
	if (handle == NULL) {
		fprintf(stderr, "dlopen failed: %s\n", dlerror());
		return 1;
	}
	uint32_t (*version_func)() = (uint32_t (*)())dlsym(handle, "getWayfireVersion");
	if (version_func == NULL) {
		perror("dlsym failed");
		dlclose(handle);
		return 1;
	}
	int32_t plugin_abi_version = version_func();
	dlclose(handle);
	fprintf(stdout, "wayfire is: %" PRIu32 "\n", WAYFIRE_API_ABI_VERSION);
	fprintf(stdout, "plugin is: %" PRIu32 "\n", plugin_abi_version);
	if (WAYFIRE_API_ABI_VERSION != plugin_abi_version) {
		fprintf(stderr, "API/ABI version mismatch:\n");
		return 1;
	}
	return 0;
}
3 Likes