How a window gets created in this GTK application? - c

I'm following this tutorial. I'm currently at the Building applications part. There is one thing that baffles me, and that is how application window gets created in the first place. Let me explain.
This is how the program starts:
#include <gtk/gtk.h>
#include "exampleapp.h"
int
main (int argc, char *argv[])
{
return g_application_run (G_APPLICATION (example_app_new ()), argc, argv);
}
This is easy. The application is started using g_application_run function. The function takes three arguments: an app, argument count and argument vector. Let's see how this app is created.
#include <gtk/gtk.h>
#include "exampleapp.h"
#include "exampleappwin.h"
struct _ExampleApp
{
GtkApplication parent;
};
G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION);
static void
example_app_init (ExampleApp *app)
{
}
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
static void
example_app_class_init (ExampleAppClass *class)
{
G_APPLICATION_CLASS (class)->activate = example_app_activate;
G_APPLICATION_CLASS (class)->open = example_app_open;
}
ExampleApp *
example_app_new (void)
{
return g_object_new (EXAMPLE_APP_TYPE,
"application-id", "org.gtk.exampleapp",
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}
The line G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION); aliases ExampleApp to GtkApplication (but in a smart way, such that types, variables, etc. associated with GtkApplication are now associated with ExampleApp).
Let's now take a look at ExampleApp *example_app_new(void) function. This function actually returns GtkApplication *, since we associated ExampleApp with GtkApplication. Next, this function calls and returns a new object by calling g_object_new function. That function takes the following arguments:
EXAMPLE_APP_TYPE, which is just GTK_TYPE_APPLICATION
"application-id", which tells that the next argument is the ID of an applicaton
"org.gtk.exampleapp", the ID
"flags", which tells that the next argument is a flag
"G_APPLICATION_HANDLES_OPEN", a flag
NULL, terminator
g_object_new, called like this, returns GtkApplication with the ID of "org.gtk.exampleapp" and with a flag "G_APPLICATION_HANDLES_OPEN". After the program goes back to example_app_new, it exits it and returns a new object of type ExampleApp * A.K.A. GtkApplication * to main. The new app is cast to GApplication in g_application_run using G_APPLICATION macro.
You have now seen what I understand. Now you'll see what I don't understand.
The tutorial linked at the top of this question says that this creates an empty window. In earlier parts of the tutorial (like this one) we used g_signal_connect to call a function when an app is run. For example,
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
calls the function activate when the app app is run using g_application_run. Function activate will in turn usually create a window and populate it either by itself or by calling other functions. That is what baffles me: there is no such thing in our example app program. How then a window gets created?

The catch is in overriding activate and open functions. It says here:
To handle these two cases, we override the activate() vfunc, which gets called when the application is launched without commandline arguments, and the open() vfunc, which gets called when the application is launched with commandline arguments.
Therefore, the function
static void
example_app_activate (GApplication *app)
{
ExampleAppWindow *win;
win = example_app_window_new (EXAMPLE_APP (app));
gtk_window_present (GTK_WINDOW (win));
}
is called when the application is run without arguments.
static void
example_app_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
GList *windows;
ExampleAppWindow *win;
int i;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
if (windows)
win = EXAMPLE_APP_WINDOW (windows->data);
else
win = example_app_window_new (EXAMPLE_APP (app));
for (i = 0; i < n_files; i++)
example_app_window_open (win, files[i]);
gtk_window_present (GTK_WINDOW (win));
}
is called when there are given arguments.

Related

gtk3 on macos: window does not restore from the dock

Using GTK3 on MacOS, after minimizing the window, the dock icon does not restore the window. I have to use the "Show All Windows" selection from the dock icon menu.
Does anyone have a work-around or know something to make the dock icons work?
I would be happy with some simple objective-c code that would make the dock icons work.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <getopt.h>
#include <unistd.h>
#include <gtk/gtk.h>
static void testBuildUI (void);
gboolean testMainLoop (void);
int
main (int argc, char *argv[])
{
gtk_init (&argc, NULL);
testBuildUI ();
testMainLoop ();
return 0;
}
static void
testBuildUI (void)
{
GtkWidget *window;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
assert (window != NULL);
gtk_widget_show_all (window);
}
int
testMainLoop (void)
{
while (1) {
gtk_main_iteration_do (FALSE);
while (gtk_events_pending ()) {
gtk_main_iteration_do (FALSE);
}
sleep (1);
}
return 0;
}
There is no special code or Objective-C needed if you use gtk_application_new instead of gtk_init and start your main loop with g_application_run.
The documentation states the following:
Currently, GtkApplication handles GTK+ initialization, application uniqueness, session management, provides some basic scriptability and desktop shell integration by exporting actions and menus and manages a list of toplevel windows whose life-cycle is automatically tied to the life-cycle of your application.
see https://docs.gtk.org/gtk3/class.Application.html
Self contained test program
Your code slightly adapted to the above points might look like the following:
#include <gtk/gtk.h>
#include <assert.h>
static void testBuildUI(GApplication *app, gpointer data);
int main(int argc, char *argv[]) {
printf("gtk version: %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
GtkApplication *app = gtk_application_new("com.example.MyApp",G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(testBuildUI), NULL);
g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return 0;
}
static void testBuildUI(GApplication *app, gpointer data) {
GtkWidget *window;
window = gtk_application_window_new(GTK_APPLICATION(app));
assert (window != NULL);
gtk_widget_show_all(GTK_WIDGET(window));
}
If you click on the dock icon of this test program, the window will restore as expected.
Alternative solution
If you want to avoid GApplication/GtkApplication, you could also use the gtk-mac-integration library instead, see https://github.com/jralls/gtk-mac-integration.
The library is rather small, only a few thousand lines of C code, so you can also easily take a look at how it works internally.
There is an important hint:
Older Gtk applications that aren't based around GApplication can be adapted to use the macOS menu bar and to integrate with the Dock using GtkOSXApplication. The Gtk API that GtkOSXApplication uses is deprecated in Gtk3 and has been removed from Gtk4. Since macOS integration is available directly in Gtk using GApplication GtkOSXIntegration will not be updated for the new menu API and is deprecated. Application maintainers are strongly encouraged to redesign their programs around GApplication, particularly before migrating to Gtk4.
see https://wiki.gnome.org/Projects/GTK/OSX/Integration
The integration is easy: after gtk_init create an application object. After setting up the window call gtkosx_application_ready with the application object.
As a side note, there is also a slightly more complex example in the src folder called test-integration.c under the Github link mentioned above that shows a more advanced menu and dock integration.
Finally, your program only minimally changed to work with the gtk-mac-integration lib:
#include <gtk/gtk.h>
#include <gtkosxapplication.h>
#include <assert.h>
static void testBuildUI(void);
static void testMainLoop(void);
static void
testBuildUI(void) {
GtkWidget *window;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
assert (window != NULL);
gtk_widget_show_all(window);
}
void
testMainLoop(void) {
while (1) {
gtk_main_iteration_do(FALSE);
while (gtk_events_pending()) {
gtk_main_iteration_do(FALSE);
}
sleep(1);
}
}
int
main(int argc, char *argv[]) {
printf("gtk version: %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
gtk_init(&argc, &argv);
GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
testBuildUI();
gtkosx_application_ready(theApp);
testMainLoop();
g_object_unref(theApp);
return 0;
}
I installed the library using brew install gtk-mac-integration and linked the C program with libgtkmacintegration-gtk3.dylib. Also in this variant, the window is restored as expected. when you click on the dock icon of the program.

GtkWindow is not loading widgets

I am playing with gtk4 and have the following
static void startapp(GtkApplication *app)
{
GtkWindow *window = (GtkWindow*)gtk_window_new();
gtk_window_set_application(window, app);
gtk_window_set_titlebar(window, gtk_header_bar_new());
gtk_header_bar_set_show_title_buttons(
(GtkHeaderBar*)gtk_window_get_titlebar(window), TRUE);
gtk_window_set_default_widget(window, gtk_button_new_with_label("bar"));
gtk_window_present(window);
}
int main (int response, char **name)
{
GtkApplication *app = gtk_application_new ("org.foo", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", (GCallback)startapp, NULL);
response = g_application_run((GApplication*)app, response, name);
g_object_unref(app);
return response;
}
What works
The program starts
The window can set widget via title bar
What isn't
The program is not showing the button widget inside the window
I dont think it has something to do with the int response variable. I did create another variable inside the code body and replaced response = ... ; return response with int foo = ...; return foo
So why isn't the window loading widgets?
In GTK4, you want gtk_window_set_child to set a child of a window (like GTK3, only one child is allowed).
So you want gtk_window_set_child(window, gtk_button_new_with_label("bar")); instead of the line gtk_window_set_default_widget(window, gtk_button_new_with_label("bar"));

Weird behaviour with g_signal_connect on GTK

I'm trying to send a pointer to a structure I created to a function using g_signal_connect but was getting a segmentation fault. After some testing, I decided to print the pointer's address inside the function that was being called (this function is responsible for drawing on a drawing_area, which means it is called regularly via g_timeout_add) and, alternating, it prints "(nil)" or the correct address. I have no clue what could be causing this.
typedef struct{
...
}prog_info
gboolean on_draw_event(GtkWidget *draw_area ,
cairo_t *cr ,
prog_info *info)
{
printf("\ninfo: %p",info); //this will give "(nil)" or the correct
address, alternating
...
}
gboolean time_handler (GtkWidget *draw_area)
{
if ((!GTK_IS_WIDGET(draw_area)) || (!gtk_widget_get_window (draw_area)))
return FALSE;
gtk_widget_queue_draw(draw_area);
return TRUE;
}
int main(int argc, char *argv[]) {
...
prog_info *info;
info =(prog_info *) calloc (1,sizeof(prog_info));
...
g_signal_connect(G_OBJECT(draw_area), "draw", G_CALLBACK(on_draw_event), info);
g_timeout_add (10, (GSourceFunc) time_handler, draw_area);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
Solved
Had two different g_signal_connect, one of which was ruining everything.

GTK gtk_label_set_text segmentation fault

I am learning GTK+ and this simple application crashes every time I run it.
It creates a label in the main window, and every time a button is clicked (the key_press_event) the label and the title should swap.
If I comment out the gtk_label_set_text in the change_title function the title alternates correctly and the app doesn't crash. Why does gtk_label_set_text crash my app?
#include <gtk/gtk.h>
#include <string.h>
const gchar first[]="FIRST";
const gchar last[]="LAST";
static void destroy(GtkWidget *window,gpointer data)
{
gtk_main_quit();
}
static gboolean change_title(GtkWidget *widget,GtkLabel *data)
{
if(strcmp(last,gtk_window_get_title(GTK_WINDOW(widget)))){
gtk_window_set_title(GTK_WINDOW(widget),last);
gtk_label_set_text(data,first);
} else {
gtk_window_set_title(GTK_WINDOW(widget),first);
gtk_label_set_text(data,last);
}
return FALSE;
}
int main(int argc,char **argv)
{
GtkWidget *window, *label;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),last);
gtk_widget_set_size_request(window,300,100);
g_signal_connect(window,"destroy_event",G_CALLBACK(destroy),NULL);
label = gtk_label_new("caasdasdjadnjadjahadjad");
gtk_container_add(GTK_CONTAINER(window),label);
g_signal_connect(window,"key_press_event",G_CALLBACK(change_title),GTK_LABEL(label));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
EDIT: I found the problem using GDB, the label pointer isn't passed correctly to the change_title function. I don't know why. (Ex: in main() label = 0xb6406608 , in change_title() label = 0x807bda8)
After doing a simple Google search on key_press_event I saw that the callback to that event have another argument between the widget and the user-data pointer. The prototype is this:
gboolean key_event_handler(GtkWidget *widget,GdkEventKey *event, gpointer data);
So simple change your function to this:
static gboolean change_title(GtkWidget *widget, GdkEventKey *event, GtkLabel *data)
and it should work.
Your change_title function has the wrong prototype.
See the documentation for the proper prototype. Most *-event signals pass the actual event as an argument in the handler function, since the handler typically needs to inspect the event in order to execute. For instance, here the GdkEventKey event will contain information about which key was pressed (or released).

How to draw / render to a widget using the widgets Xid in GTK/Cairo in 'C'

I have a need to write a GTK application in C that does some animation using Cairo that will render into a GTK widget that exists in another running application. The idea is to do the very same thing you can do with VLC and Mplayer. For example Mplayer has the -wid option:
-wid (also see -guiwid) (X11, OpenGL and DirectX only)
This tells MPlayer to attach to an existing window. Useful to embed MPlayer in a browser (e.g. the plugger extension). This option
fills the given window completely, thus aspect scaling, panscan, etc
are no longer handled by MPlayer but must be managed by the
application that created the window.
With this Mplayer option you can create a GTK application with a GTKImage widget, get it's Xid and then play a movie in the GTK application using Mplayer with the Xid specified.
I'm trying to do the same thing except render/draw into the external window using Cairo. Anybody have suggestions or better yet a small code sample?
Take a look to the GtkSocket and GtkPlug classes.
The main program will create a GtkSocket and the XID you can pass to the other program will be returned by the function gtk_socket_get_id(). Then the other program will use it as argument to the gtk_plug_new() function. All the render will be done in children of this new GtkPlug object.
UPDATE: Well, if you want... here it is a minimal example of GtkSocket/GtkPlug. You don't say if you are using GTK+2 or GTK+3, so I'm assuming version 2.
server.c:
#include <gtk/gtk.h>
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GtkWidget *wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *sck = gtk_socket_new();
gtk_container_add(GTK_CONTAINER(wnd), sck);
gtk_window_set_default_size(GTK_WINDOW(wnd), 400, 300);
gtk_widget_show_all(wnd);
GdkNativeWindow nwnd = gtk_socket_get_id(GTK_SOCKET(sck));
g_print("%lu\n", nwnd);
gtk_main();
return 0;
}
client.c:
#include <stdlib.h>
#include <gtk/gtk.h>
#include <cairo/cairo.h>
#include <math.h>
gboolean OnDraw(GtkWidget *w, GdkEvent *ev, gpointer data)
{
GtkAllocation size;
gtk_widget_get_allocation(w, &size);
cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(w));
cairo_set_source_rgb(cr, 1, 0, 0);
cairo_arc(cr, size.width/2, size.height/2, size.height/2, 0, 2*M_PI);
cairo_fill(cr);
cairo_destroy(cr);
return TRUE;
}
int main(int argc, char **argv)
{
gtk_init(&argc, &argv);
GdkNativeWindow nwnd = strtoul(argv[1], NULL, 10);
GtkWidget *plug = gtk_plug_new(nwnd);
GtkWidget *canvas = gtk_drawing_area_new();
gtk_container_add(GTK_CONTAINER(plug), canvas);
g_signal_connect(canvas, "expose-event", (GCallback)OnDraw, NULL);
gtk_widget_show_all(plug);
gtk_main();
return 0;
}
The XID to be used is printed by the server and has to be copied/pasted as argument to the client:
$ ./server
60817441
^Z
[1]+ Stopped ./server
$ bg
$ ./client 60817441

Resources