Radek Chalupa   konzultace a školení programování, vývoj software na zakázku

Základní aplikace v GDK

7.10.2020

Knihovna GDK představuje relativně nízkoúrovňovou mezivrstvu mezi knihovnou GTK a okenním serverem. To však neznamená že v určitých případech nemůže být použita samostatně, pokud nechceme nebo nemůžeme mít aplikaci závislou na relativně velké a komplexní knihovně GTK a na druhé straně psát uživatelské rozhraní v Xlib nebo xcb nám přijde příliš nízkoúrovňové.

Okno vytvoříme pomocí funkce gdk_window_new, která nám v případě úspěchu vrátí ukazatel na typ GdkWindow. Parametry funkce jsou rodičovské okno, adresa struktury v jejíchž prvcích nastavíme požadované parametry okna a maska ve které určíme, které z parametrů této struktury jsou platné a mají být použity. Po vytvoření okno zobrazíme pomocí funkce gdk_window_show.

GdkWindow* window; /*globální proměnná*/
GdkWindowAttr attributes;
gint attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_TITLE | GDK_WA_WMCLASS;
if (!gdk_init_check(&argc, &argv))
	return EXIT_FAILURE;
memset(&attributes, 0, sizeof(attributes));
attributes.window_type = GDK_WINDOW_TOPLEVEL;
attributes.x = 100;
attributes.y = 50;
attributes.event_mask = GDK_ALL_EVENTS_MASK;
attributes.width = 440;
attributes.height = 480;
attributes.title = "GDK základní okno";
attributes.wclass = GDK_INPUT_OUTPUT;
gdk_event_handler_set(on_gdk_event, NULL, NULL);
window = gdk_window_new(NULL, &attributes, attr_mask);
gdk_window_show(window);

Po vytvoření a zobrazení okna musíme realizovat smyčku událostí, ve které aplikace poběží dokud z této smyčky nevyskočíme nebo aplikaci neukončíme "natvrdo". K tomu využijeme knihovnu GLib, nad kterou je GDK postavena. Zbývající kód funkce main bude vypadat následovně.

main_loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(main_loop);
g_main_loop_unref(main_loop);
return EXIT_SUCCESS;

kde globální proměnná main_loop je definovaná takto:

GMainLoop* main_loop;

Nyní zbývá napsat obslužnou funkci událostí nastavenou ve výše uvedeném kódu funkcí gdk_event_handler_set.

void on_gdk_event(GdkEvent* event, gpointer data)
{
	if (event->type == GDK_EXPOSE)
	{
		on_expose((GdkEventExpose*)event);
	}
	else if (event->type == GDK_DELETE)
	{
		on_end_app();
	}
}

Jak vidíme, v tomto nejjednodušším případě stačí obsloužit událost překreslení okna (GDK_EXPOSE) a zrušení okna (GDK_DELETE).

Při požadavku na překreslení okna zatím nebudeme nic vykreslovat (to si necháme na další pokračování), ale měli bychom zavolat funkce gdk_window_begin_draw_frame a gdk_window_end_draw_frame, čímž řekneme správci oken (jako je X11) že je okno překresleno.

void on_expose(GdkEventExpose* ev)
{
	if (ev->region == NULL)
		return;
	GdkDrawingContext* dc = gdk_window_begin_draw_frame(
		ev->window, ev->region);
	gdk_window_end_draw_frame(ev->window, dc);
}

V reakci na zrušení hlavního okna aplikace musíme korektně ukončit smyčku událostí, jinak by totiž i po zrušení okna proces zůstal běžet až do jeho ukončení zvenku.

void on_end_app()
{
	gdk_window_destroy(window);
	g_main_loop_quit(main_loop);
}

Tím máme kompletní základ aplikace v GDK. V příštím pokračovaní si ukážeme jak kreslit a vypisovat text do okna a následně si vytvoříme jednoduchý a rychlý prohlížeč obrázků ve stylu programu feh.

Na závěr celý výpis programu, který lze sestavit překladačem GCC následujícím příkazem (pro ladicí sestavení):

gcc gdk-okno.c -Wall -g `pkg-config --cflags --libs gdk-3.0` -ogdk-okno
#include <gdk/gdk.h>

GMainLoop* main_loop;
GdkWindow* window;

void on_end_app()
{
	gdk_window_destroy(window);
	g_main_loop_quit(main_loop);
}

void on_expose(GdkEventExpose* ev)
{
	if (ev->region == NULL)
		return;
	GdkDrawingContext* dc = gdk_window_begin_draw_frame(
		ev->window, ev->region);
	gdk_window_end_draw_frame(ev->window, dc);
}

void on_gdk_event(GdkEvent* event, gpointer data)
{
	if (event->type == GDK_EXPOSE)
	{
		on_expose((GdkEventExpose*)event);
	}
	else if (event->type == GDK_DELETE)
	{
		on_end_app();
	}
}

int main(int argc, char** argv)
{
	GdkWindowAttr attributes;
	gint attr_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_TITLE | GDK_WA_WMCLASS;
	if (!gdk_init_check(&argc, &argv))
		return EXIT_FAILURE;
	memset(&attributes, 0, sizeof(attributes));
	attributes.window_type = GDK_WINDOW_TOPLEVEL;
	attributes.x = 100;
	attributes.y = 50;
	attributes.event_mask = GDK_ALL_EVENTS_MASK;
	attributes.width = 440;
	attributes.height = 480;
	attributes.title = "GDK základní okno";
	attributes.wclass = GDK_INPUT_OUTPUT;
	gdk_event_handler_set(on_gdk_event, NULL, NULL);
	window = gdk_window_new(NULL, &attributes, attr_mask);
	gdk_window_show(window);
	main_loop = g_main_loop_new(NULL, FALSE);
	g_main_loop_run(main_loop);
	g_main_loop_unref(main_loop);
	return EXIT_SUCCESS;
}