Consider the following two programs, glossing over what compiler flags we need to get them running. (See the Makefile
).
Lines 1 to 27 in c53be75
Lines 6 to 19 in c53be75
On their face, the programs should have the same behaviour: both the entry point of demo
and dl
call on_app_activate
.
What we expect to happen is a window to open, containing a button we can click to quit the program. demo
works as expected.
On running dl
, however, we are faced with a warning from Gtk
(dl:2875124): Gtk-WARNING **: 14:19:58.729: Could not find signal handler 'on_quit_btn_clicked'
and indeed clicking the button has no effect.
On the internet, many people have had issues compiling and linking against Gtk+.
We have to use the right compiler flags (from pkg-config
and -rdynamic -export-dynamic
).
We have to flag the callback with extern "C"
to avoid name mangling.
The issue here is not one of these.
To understand what's wrong, let's look the documentation for gtk_builder_connect_signals
:
This method is a simpler variation of
gtk_builder_connect_signals_full()
. It uses GModule's introspective features (by opening the moduleNULL
) to look at the application's symbol table. From here it tries to match the signal handler names given in the interface description with symbols in the application and connects the signals.
The issue is that when called from dl
, GModule tries to find on_quit_btn_clicked
in the symbol table of dl
.
But on_quit_button_clicked
is in demo.so
, and we have to tell GModule to look there.
Can we do that? The source for gtk_builder_connect_signals()
contains these important lines
args = g_slice_new0 (connect_args);
args->module = g_module_open (NULL, G_MODULE_BIND_LAZY);
args->data = user_data;
gtk_builder_connect_signals_full (builder,
gtk_builder_connect_signals_default,
args);
It looks like we just need to change out the NULL
in the call to g_module_open
.
The documentation tells us we can just pass the file name as a C string.
We change the signature of on_app_activate
so that we can pass in argument that tells us if we're in the main program, or being dynamically loaded.
If this argument is a char*
it can be NULL
in the former case and double as the name of the .so
in the latter.
Lines 40 to 51 in 0f5df9b
gtk_builder_connect_signals_default
is not exported in gtkbuilder.h
, so we have to provide the implementation, i.e. copy-paste from source.
Lines 15 to 16 in 0f5df9b
All that remains now is modifying dl.c
to account for the new signature.
Line 6 in 0f5df9b
Line 17 in 0f5df9b
After recompiling, dl
now works: clicking the quit button does, in fact, quit.
Instead of passing the filename as a string, we can deduce it through introspection.
With the backtrace()
and backtrace_symbols()
functions from execinfo.h
(see backtrace(3) man page), we can obtain backtraces that look something like
./demo.so(on_app_activate+0x202) [0x7fc6c66c1752]
The first stack frame from backtrace()
must be in the file containing on_app_activate
.
We can then just cut out the file name, and pass that to gmodule
Lines 45 to 50 in 0e73a2d
(
.)
For some reason that I do not understand, when loading dynamically the leading ./
must be present;
but when running as the main program, it must be absent.
Therefore the third argument to on_app_activate
is not dropped, but changed to a flag
Lines 41 to 44 in 0e73a2d
NULL
if running as the main program.