Why does gtk3 call the ClosureNotify function in the example code, but gkt4 doesn't? - gtk

When this example program
#include <gtk/gtk.h>
static void draw_func(
GtkDrawingArea* drawing_area,
cairo_t *cr,
#ifdef GTK3
#else
int width,
int height,
#endif
gpointer user_data
) {
printf("draw!\n");
}
struct Ctx {
char *text;
GtkWidget *drawing_area;
};
static void ctx_destroy_notify(
gpointer data
#ifdef GTK3
, GClosure* closure
#endif
) {
struct Ctx *ctx = data;
printf("drop!\n");
free(ctx->text);
g_object_unref(ctx->drawing_area);
free(ctx);
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *drawing_area = gtk_drawing_area_new();
GtkWidget *window = gtk_application_window_new (app);
struct Ctx *ctx = malloc(sizeof(struct Ctx));
ctx->text = strdup("Hallo");
ctx->drawing_area = g_object_ref(drawing_area);
#ifdef GTK3
g_signal_connect_data(G_OBJECT(drawing_area), "draw", G_CALLBACK(draw_func), ctx, ctx_destroy_notify, G_CONNECT_AFTER);
#else
gtk_drawing_area_set_draw_func( GTK_DRAWING_AREA(drawing_area), draw_func, ctx, ctx_destroy_notify);
#endif
#ifdef GTK3
gtk_container_add (GTK_CONTAINER (window), drawing_area);
#else
gtk_window_set_child (GTK_WINDOW(window), drawing_area);
#endif
#ifdef GTK3
gtk_widget_show_all(window);
#else
gtk_widget_show(window);
#endif
gtk_window_close(GTK_WINDOW(window));
}
int
main (int argc,
char **argv)
{
GtkApplication * app = gtk_application_new ("org.example.test", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
g_application_run (G_APPLICATION (app), argc, argv);
printf("end of main loop\n");
g_object_unref (app);
}
is run with this Makefile:
.PHONY: test
test: test3 test4
./test3
./test4
test3: test.c
gcc -g -O0 -DGTK3 `pkg-config --cflags gtk+-3.0` -o test3 test.c `pkg-config --libs gtk+-3.0`
test4: test.c
gcc -g -O0 -DGTK4 `pkg-config --cflags gtk4` -o test4 test.c `pkg-config --libs gtk4`
The output is:
./test3
drop!
end of main loop
./test4
end of main loop
So ctx_destroy_notify is called with gtk3 but not with gtk4. Note, that gtk_widget_show (window); gtk_window_close(GTK_WINDOW(window)); can be replaced with g_object_run_dispose(G_OBJECT(window)); with the same result.
Why do gtk3 and gtk4 behave differently here?
[ to be ignored: stackoverflow wants a little bit more text, so that the text to code ratio is acceptable to its heuristic... ]
Edit 31.12.2022: Add first few lines of source, which were missing because of copy-and-paste mistake.

Okay, as nobody jumped on this, I try to answer that myself.
The code, obviously, contains a circular reference: drawing_area -> draw callback -> ctx -> drawing_area
The reference ctx -> drawing_area is correctly created with 
g_object_ref(), the other references are implied by gtk.
The function ctx_destroy_notify() in the code would break the reference
cycle with g_object_unref() if it gets called.
It is true, that gtk3 calls ctx_destroy_notify() when the application window is closed. This is a result of the fact that gtk3 immediately disposes the child widgets when a window is destroyed. But this is just an implementation detail.
There were substantial changes in this area between gtk3 and gtk4. See "Life-cycle handling" at https://docs.gtk.org/gtk4/migrating-3to4.html.
Here is a related discussion: https://gitlab.gnome.org/GNOME/gtk/-/issues/3243
In short: While the destroy_data notify of signal_connect_data() can be used to clean up the user data of the callback when the object is destroyed, it can not reliably be used to break a reference cycle which prevents the object from being destroyed in the first place.
Possible approaches to clean up the reference cycle would be:
subclass one of the widgets and make the ctx data part of it
For gtk4: Use a "win.close" action
Use weak references in ctx

Related

Learning GUI programming with GTK+3

I am new to GUI programming. I recently installed Gtk+3 version on Linux. But, when I typed following code:
#include <gtk/gtk.h>
#include <stdio.h>
static int count = 0;
void button_clicked(GtkWidget *button, gpointer data)
{
printf(“%s pressed %d time(s) \n”, (char *) data, ++count);
}
int main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *button;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label(“Hello World!”);
gtk_container_add(GTK_CONTAINER(window), button);
g_signal_connect(GTK_OBJECT (button), “clicked”,
GTK_SIGNAL_FUNC (button_clicked),
“Button 1”);
gtk_widget_show(button);
gtk_widget_show(window);
gtk_main ();
return 0;
}
To run this code I used this command: $ gcc gtk1.c –o gtk1 pkg-config --cflags --libs gtk+-3.0
but I had error like this
undefined reference to GTK_OBJECT;
undefined reference to GTK_SIGNAL_FUNC;
This is because your code sample is for an old version of GTK+ 2. GTK_OBJECT was deprecated in the late GTK+ 2.x versions, and finally removed in GTK+ 3. Same for GTK_SIGNAL_FUNC. Both have been moved to the GObject library, where they now stand as G_OBJECT and G_CALLBACK.
To avoid using outdated code, just get started with the code samples from the GTK+ 3 documentation.

How to avoid blocking function g_application_run

I have an application with the following pattern (it's an embedded application) that I don't want to change:
void
main(int argc, char *argv[])
{
some_init_functions();
while(1) {
some_functions();
}
}
I'd like to simulate the application on Windows so I'd like to use Gtk+3 to show a window that simulate the display used in the original application.
The problem is the function g_application_run() that is blocking.
Is it possible to manually call a Gtk+/GLib function to process pending events only and returns immediately? I would call this function in the while(1) loop.
I read about gtk_main_iteration_do(), but I didn't understand how to use it without calling g_application_run().
It's your job to integrate your application in the GUI, not the other way around. What you should do is call g_application_run, and add an event source with g_timeout_add or g_idle_add that will call your callback. In that callback, just call some_functions() once. The GTK main loop will take care of calling it again and again.
It is possible to get out of the g_application_run loop. Events can be handled by g_main_context_iteration(GMainContext *context, gboolean may_block). With may_block = TRUE, it will only return to the main loop after an event has been handled. With may_block = FALSE, only the highest priority events will be handled after which control returns to the loop.
The code below is based upon the example-0.c code from the GTK Reference Manual, yet making the process flow from g_application_run explicit. Note that command line arguments are not processed, and several checks are omitted.
A timeout event is added such that, in combination with may_block = TRUE, the main loop advances at least once per second. Also, a callback is added on the destroy event, such that, in combination with a global variable, the main loop can stop if the window is closed.
As a direct answer to the question asked: you would need to make sure correct set-up and break-down is performed, and add g_main_context_iteration (context, may_block); to your while (1) loop.
#include <gtk/gtk.h>
static bool application_running; // As use_count is private
// Add close_window event watcher to tell our application loop we should stop
static void
close_window (void)
{
printf("Window is closed\n");
application_running = FALSE;
}
// Add timeout to make sure the application loop is unblocked once per second
static gboolean
timeout (gpointer data)
{
printf("timeout\n");
return true;
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new (app);
// Add destroy call-back so we know when window is closed.
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_widget_show (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
g_timeout_add_seconds (1, timeout, NULL);
// **** begin g_application_run alternative ****
// Setup
GMainContext *context;
gboolean acquired_context;
context = g_main_context_default ();
acquired_context = g_main_context_acquire (context);
g_return_val_if_fail (acquired_context, 0);
GError *error = NULL;
if (!g_application_register (G_APPLICATION (app), NULL, &error))
{
g_printerr ("Failed to register: %s\n", error->message);
g_error_free (error);
return 1;
}
g_application_activate (G_APPLICATION (app));
// Main event loop
application_running = true;
int loop = 0;
while (application_running) {
bool may_block = TRUE;
// may_block = TRUE: g_main_context_iteration blocks execution if no events are coming
// may_block = FALSE: g_main_context_iterations processes pending events and continues
g_main_context_iteration (context, may_block);
printf("Loop %i\n", loop++);
}
// Release
g_settings_sync ();
while (g_main_context_iteration (context, FALSE))
;
g_main_context_release (context);
// **** end g_application_run alternative ****
g_object_unref (app);
return status;
}

What could be wrong: GLib-GObject-WARNING **: cannot register existing type 'PangoCairoFont'

I spent many weeks trying to get gtk+ 3.22 to build on Visual Studio 2015. Finally I got it built but the small GUI program failed to initialize gtk. The error is shown as below:
gtk+_gtk_test.exe:15980): Gtk-WARNING **: Could not find the icon 'window-minimize-symbolic-ltr'. The 'hicolor' theme
was not found either, perhaps you need to install it.
You can get a copy from:
http://icon-theme.freedesktop.org/releases
(gtk+_gtk_test.exe:15980): GLib-GObject-WARNING **: cannot register existing type 'PangoCairoFont'
(gtk+_gtk_test.exe:15980): GLib-GObject-CRITICAL **: g_type_interface_add_prerequisite: assertion 'G_TYPE_IS_INTERFACE (interface_type)' failed
(gtk+_gtk_test.exe:15980): Glib-CRITICAL **: g_once_init_leave: assertion 'result != 0' failed
The first warning exists because the executable cannot locate the icon files. I have already figured out how to fix this. However, I am unable to get the second Warning fixed. This is my first gtk+ project. When I debug the code, it seems that PangoCairoFont type failed because of the existence of PangoCairoWin32Font type.
What could be wrong here? Appreciate your advice.
Source code of the program:
#include <gtk/gtk.h>
void hello(GtkWidget *widget, gpointer data)
{
g_print("Hello, World\n");
}
gint delete_event(GtkWidget *widget, GdkEvent event, gpointer data)
{
/* when this fucntion returns FALSE, the delete-event
signal becomes a destroy signal*/
return FALSE;
}
void end_program(GtkWidget *widget, gpointer data)
{
/* End the main loop */
gtk_main_quit();
}
int main(int argc, char **argv)
{
GtkWindow *window;
GtkButton *button;
/* initialize Gtk+ */
gtk_init(&argc, &argv);
/* create window, set default height and width to 200px */
window = g_object_new(GTK_TYPE_WINDOW,
"default-height", 200,
"default-width", 200,
"border-width", 12,
"title", "GtkHello",
NULL);
/* add signal handlers for window */
g_signal_connect(window, "delete-event", G_CALLBACK(delete_event),
NULL);
g_signal_connect(window,
"destroy", G_CALLBACK(end_program),
NULL);
/* create button */
button = g_object_new(GTK_TYPE_BUTTON,
"label", "_Hello, World!\nClick here.",
"use-underline", TRUE,
NULL);
g_signal_connect(button,
"clicked", G_CALLBACK(hello),
NULL);
g_signal_connect_swapped(button,
"clicked", G_CALLBACK(gtk_widget_destroy),
window);
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(button));
gtk_widget_show_all(GTK_WIDGET(window));
/* start main loop */
gtk_main();
return 0;
}
I figured out. I mistakenly built pangocairo module into a static lib instead of DLL. This lib is further linked into different DLLs so the global variable has several copies, which caused the problem.

blank dialogue box without my text nor OK button

I'm trying to popup an OK message box in my gtk browser, but all I'm getting is blank grey square patch without my text in it and even button is not visible.
PFB the function which I'm using to render message box :
Void DisplayOKPopup()
{
dialogue=gtk_message_dialogue_new(GTK_WINDOW(WebtBrowserWindow),
GTK_DIALOGUE_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_widget_show(dialogue);
LOGDEBUG(" 1");
gtk_dialogue_run(GTK_DIALOGUE (dialogue));
LOGDEBUG("2");
gtk_widget_destroy (dialogue);
}
As per my debug log, I can see that control is passing till LOGDEBUG("1") and after that it goes to gtk_dialogue_run after that UI is getting crashed, the line next to run i.e LOGDEBUG("2") is not getting executed.
Kindly provide your inputs as I'm working on this since 3 days:!
As concluded, the problem arises from the use of threads. There are several approaches to solve the issue but since there is not code, I'll try with a simple example so that you can recreate it on your code.
Take a global Boolean variable as a flag and on your thread, set it to true so that a "periodic" idle callback can check it and if the flag is TRUE then show the dialog. The logic is that the Gtk UI functions are called from the mainloop/main thread and not from the worker threads.
DisplayOkPopup just has a simple "counter" from 0 to MAX_INT (32 bits) and set's the global flag as TRUE.
check_for_dialog it's a callback that runs on mainloop idle time and check for the flag, if TRUE then runs the dialog.
exit is dirty and will output errors but the goal ain't that, its just a tip/hint for your solution.
Example:
#include <gtk/gtk.h>
gboolean dialog_active;
gboolean show_dialog_popup;
GtkWidget *window;
gpointer DisplayOKPopup (gpointer user_data) {
int i;
while (TRUE) {
for (i = 0; i < G_MAXINT32; i++) {
// nop
}
show_dialog_popup = TRUE;
}
return NULL;
}
gboolean check_for_dialog (gpointer user_data) {
if (show_dialog_popup == TRUE && dialog_active == FALSE) {
dialog_active = TRUE;
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_dialog_run(GTK_DIALOG (dialog));
show_dialog_popup = FALSE;
dialog_active = FALSE;
gtk_widget_destroy(dialog);
}
return TRUE;
}
static void app_activate(GtkApplication *app, gpointer user_data) {
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window Title Here");
gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
//gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show_all(window);
g_idle_add(check_for_dialog, NULL);
g_thread_new("my thread", DisplayOKPopup, NULL);
}
int main(int argc, char **argv) {
GtkApplication *app;
int status;
show_dialog_popup = FALSE;
dialog_active = FALSE;
app = gtk_application_new("your.application.id", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
Build and run:
$ gcc -o test test.c `pkg-config --cflags --libs gtk+-3.0`
$ ./test
Result:
EDIT:
Answer to your comment is:
To have custom buttons on your dialog then use:
gtk_dialog_new_with_buttons
gtk_dialog_add_button (if dialog exists, will add 1 button)
gtk_dialog_add_buttons (same as above but can add many buttons)
Example for 1):
dialog = gtk_dialog_new_with_buttons ("My dialog",
main_app_window,
flags,
"_OK",
GTK_RESPONSE_ACCEPT,
"_Cancel",
GTK_RESPONSE_REJECT,
NULL);
Example for 2):
gtk_dialog_add_button (GTK_DIALOG(dialog),
"This is my button",
GTK_RESPONSE_ACCEPT);
Example for 3) is same as 2) but can handle many buttons and terminates with NULL.

How to use GTK+ with ada

Anyone can show me some examples, simple, how to use GTK with Ada?
examples, like: How to use Glade with Ada, create an simple window....
an simple window, like this:
#include <gtk/gtk.h>
int main(int argc, char *argv[] )
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (window);
gtk_main ();
return 0;
}
And, It's possible to use GtkMM, with ada ?
Thanks...
A few of my favorite GtkAda programs:
Animation demo
Linxtris
Mine Detector
Also, don't overlook the examples and testgtk folders of GtkAda itself.
Additional resources:
GTK+ API Documentation
Rosetta Code Category: Ada
Glade