Category: Whisker Menu (Page 8 of 8)

One of the things I had not given a second thought to when writing Whisker Menu was my use of C++11. The new standard makes C++ much cleaner, and any Linux distro that has Xfce 4.8 or 4.10 mostly likely also has a version of GCC at least as recent as 4.6 (which is the earliest version of GCC that can compile the bits of C++11 I used).

However, not everybody is using Linux. There are a lot of BSD users out there who don’t yet have LLVM/Clang, and so in an effort to make my menu usable by as many people as possible I have downgraded to C++98. Some of the things I will miss most from C++11 are rvalue references (no more passing objects by const reference! yay!), range-based for, and the repurposed auto keyword:

C++11:

LauncherModel example(std::map<std::string, Launcher*> sorted_items)
{
	LauncherModel model;
	for (auto i : sorted_items)
	{
		model.append_item(i.second);
	}
	return model;
}

C++98:

void example(const std::map<std::string, Launcher*>& sorted_items, LauncherModel& model)
{
	for (std::map<std::string, Launcher*>::const_iterator i = sorted_items.begin(), end = sorted_items.end(); i != end; ++i)
	{
		model.append_item(i->second);
	}
}

For those who have never seen the new C++11 constructs, auto is a variable whose type is determined at compile time. That makes the code for dealing with STL iterators a lot cleaner. The range-based for is also nice for reducing code, and it even works with anything that provides begin() and end() functions!

Another change I have made is how I handle the signal callbacks. I had previously decided to use macros to hook up the GTK+ signals to C++ classes. However, I have since found the obfuscation of what is really going on to be frustrating at times. Because of that I have taken the macros out and replaced them with what they were actually doing, even if it does bloat the header files somewhat.

When I wrote Whisker Menu I was viewing it as a pet project. I wasn’t planning to share it, and once I decided to make it public I was not expecting that many people to use it. However, I was very much mistaken about how many people would like to try it out, and this is going to require me to reconsider some assumptions I made when writing it!

There is one thing I have changed already. I had come up with a simple hack to get GTK+ signals to connect to C++ member functions by using variadic templates to generate functions that could be called by C. I was quite pleased with how straightforward it was:

template <typename T, typename R, typename... Args>
struct SlotArgs
{
	T* instance;
	R (T::*member)(Args...);
};

template <typename T, typename R, typename... Args>
R invoke_slot_args(Args... args, SlotArgs<T,R,Args...>* slot)
{
	return (slot->instance->*slot->member)(args...);
}

template <typename T, typename R, typename... Args>
void delete_slot_args(SlotArgs<T,R,Args...>* slot)
{
	delete slot;
}

template<typename T, typename R, typename... Args>
gulong g_signal_connect_slot(gpointer signal_obj, const gchar* detailed_signal, R (T::*member)(Args...), T* instance)
{
	return g_signal_connect_data(signal_obj, detailed_signal,
		(GCallback)&invoke_slot_args<T,R,Args...>,
		new SlotArgs<T,R,Args...>{instance, member},
		(GClosureNotify)&delete_slot_args<T,R,Args...>,
		GConnectFlags(0));
}

class Example
{
public:
	Example();
	gboolean on_button_press_event(GtkWidget* widget, GdkEventButton* event);
};

Example::Example()
{
	g_signal_connect_slot(widget, "button-press-event", &Test::on_button_press_event, this);
}

This works because the calling convention between C and C++ on Linux is the same by default and the only thing you have to worry about is name mangling. I tested it on both GCC and Clang, and it worked perfectly. On my very up-to-date Arch system, that is. Cue dramatic music. 😛 So of course there is a bug in GCC 4.6 and earlier where it does not properly handle the name mangling of template functions. Oops! I have rewritten things to use macros and static member functions instead:

#define SLOT_CALLBACK(klassmember) G_CALLBACK(klassmember ## _slot)

#define SLOT_2(R, klass, member, A1, A2) \
	static R member ## _slot(A1 arg1, A2 arg2, klass* obj) \
		{ return obj->member(arg1, arg2); } \
	R member(A1 arg1, A2 arg2)

class Example
{
public:
	Example();
	SLOT_2(gboolean, Test, on_button_press_event, GtkWidget*, GdkEventButton*);
};

Example::Example()
{
	g_signal_connect(widget, "button-press-event", SLOT_CALLBACK(Test::on_button_press_event), this);
}

It is not as easy to read, though, because instead of having actual member functions in the header file they are generated by a macro. And it does require a different macro for each number of arguments I want to support. Still, it works out to be the same thing in the end, and it does have the potential to be slightly more efficient.

Obviously, I could write out the code generated by the macro, but that would get repetitive and boring quite fast. And then if I want to change a function’s signature I would have to make sure to update both functions to match. Annoying! So, macros it is.

Announcing Whisker Menu

Posted on June 20, 2013, under Whisker Menu

8 comments

I have just released a brand new project named Whisker Menu. It will only appeal to some of my users as the project is for Linux and other UNIX-like OSs, but I thought I would share it anyway. I have made an alternate menu for Xfce inspired by other menus like KDE’s Kickoff. I have been using it as my main menu for the past month or so and I have found it to be quite comfortable.

The menu shows you a list of favorite applications when you open it. You can configure what goes in the list, and how they are ordered. It also has a list of the ten programs most recently launched from the menu. Along the side is a series buttons for top-level categories that allow you to quickly browse all of you installed applications.

Whisker Menu

This is the first time I have ever used GTK+ in a program, and it will probably be the only time I do so. I do not like it (to put it mildly), and I do not understand how anybody could possibly prefer it to Qt. Still, it was necessary to write a native Xfce panel plugin. However, I did use C++11 instead of straight C, because there is a limit to how much I am willing to torture myself. 😛

Categories