GTK (formerly known as the the Gimp Toolkit) is a large environment of widgets and connecting glue that is used to build a large number of applications and environments such as Gnome. There are several books on GTK programming as well as many tutorials. This chapter only considers the Wayland aspects of GTK.
Fedora RPM wayland-devel, wayland-protocols-devel
Under Fedora, install gtk3-devel mesa-libegl-devel
A simple GTK application has two buttons '+' and '-'
and a label to show the current value. It looks like
The code for basic.c is
#include <gtk/gtk.h> gint count = 0; char buf[5]; void increase(GtkWidget *widget, gpointer label) { count++; sprintf(buf, "%d", count); gtk_label_set_text(GTK_LABEL(label), buf); } void decrease(GtkWidget *widget, gpointer label) { count--; sprintf(buf, "%d", count); gtk_label_set_text(GTK_LABEL(label), buf); } int main(int argc, char** argv) { GtkWidget *label; GtkWidget *window; GtkWidget *frame; GtkWidget *plus; GtkWidget *minus; gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_window_set_default_size(GTK_WINDOW(window), 250, 180); gtk_window_set_title(GTK_WINDOW(window), "+-"); frame = gtk_fixed_new(); gtk_container_add(GTK_CONTAINER(window), frame); plus = gtk_button_new_with_label("+"); gtk_widget_set_size_request(plus, 80, 35); gtk_fixed_put(GTK_FIXED(frame), plus, 50, 20); minus = gtk_button_new_with_label("-"); gtk_widget_set_size_request(minus, 80, 35); gtk_fixed_put(GTK_FIXED(frame), minus, 50, 80); label = gtk_label_new("0"); gtk_fixed_put(GTK_FIXED(frame), label, 190, 58); gtk_widget_show_all(window); g_signal_connect(window, "destroy", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect(plus, "clicked", G_CALLBACK(increase), label); g_signal_connect(minus, "clicked", G_CALLBACK(decrease), label); gtk_main(); return 0; }
GTK is designed to transparently support a number of
display backends. These include Win32, Quartz,
Mir, X11, Broadway and Wayland. A choice can be forced
by setting the environment variable
GDK_BACKEND
to a list of options such as
export GDK_BACKEND="x11,wayland,*"
If the special value of help
is used, a GTK application
will print the possible values it knows
$ GDK_BACKEND=help my-gtk-app
Supported GDK backends: wayland x11 broadway
This means that the default is to try Wayland first, then X11 and finally
Broadway.
The technique used to choose a backend is to try to open
the display for each type in turn. For Wayland, the code in
gdk/wayland/gdkdisplay-wayland.c
is
_gdk_wayland_display_open (const gchar *display_name)
{
struct wl_display *wl_display;
GdkDisplay *display;
GdkWaylandDisplay *display_wayland;
GDK_NOTE (MISC, g_message ("opening display %s", display_name ? display_name : ""));
/* If this variable is unset then wayland initialisation will surely
* fail, logging a fatal error in the process. Save ourselves from
* that.
*/
if (g_getenv ("XDG_RUNTIME_DIR") == NULL)
return NULL;
wl_log_set_handler_client (log_handler);
wl_display = wl_display_connect (display_name);
if (!wl_display)
return NULL;
display = g_object_new (GDK_TYPE_WAYLAND_DISPLAY, NULL);
display->device_manager = _gdk_wayland_device_manager_new (display);
display_wayland = GDK_WAYLAND_DISPLAY (display);
display_wayland->wl_display = wl_display;
display_wayland->screen = _gdk_wayland_screen_new (display);
display_wayland->event_source = _gdk_wayland_display_event_source_new (display);
display_wayland->known_globals =
g_hash_table_new_full (NULL, NULL, NULL, g_free);
_gdk_wayland_display_init_cursors (display_wayland);
_gdk_wayland_display_prepare_cursor_themes (display_wayland);
display_wayland->wl_registry = wl_display_get_registry (display_wayland->wl_display);
wl_registry_add_listener (display_wayland->wl_registry, ®istry_listener, display_wayland);
_gdk_wayland_display_async_roundtrip (display_wayland);
/* Wait for initializing to complete. This means waiting for all
* asynchrounous roundtrips that were triggered during initial roundtrip. */
while (display_wayland->async_roundtrips != NULL)
{
if (wl_display_dispatch (display_wayland->wl_display) < 0)
{
g_object_unref (display);
return NULL;
}
}
/* Make sure we have xdg_shell at least */
if (display_wayland->xdg_shell == NULL)
{
g_warning ("Wayland compositor does not support xdg_shell interface,"
" not using Wayland display");
g_object_unref (display);
return NULL;
}
display_wayland->selection = gdk_wayland_selection_new ();
g_signal_emit_by_name (display, "opened");
return display;
}
Not all applications have been converted to Wayland.
There are many still running under X Windows.
In an X world, Wayland applications run under a compositor
such as Weston.
In a Wayland world, X applications run under
XWayland
. How do you tell whether an application
is running as an X application or a Wayland one?
Sergey Bugaev came up with a neat and simple test at
How to easily determine if an app runs on XWayland or on Wayland natively.
One of the earliest X applications was xeyes
.
This follows the cursor around, pointing the pair of eyes
to wherever the cursor is. It is in the Fedora package
xorg-x11-apps
. But Wayland apps aren't
X apps, so it won't follow the cursor into them!
The xeyes
test runs fine under a Wayland system
such as Fedora 25. It shows, for example that an arbitrary
GTK application such as
basic.c runs as a Wayland application
on Fedora 25, while emacs
runs as an X
application under XWayland
.
However, under an X system such as Ubuntu 16.04, this test
breaks down. The program runs as an X application and
xeyes
follows it. But if you fire up
a compositor such as Weston
,
you discover that Weston
also runs as an
X application, because it is. And if you start up
basic.c
(either from a Weston
terminal or an
Ubuntu terminal) then it runs within the
Weston
window as it should, but is also
followed by xeyes
. It is running as a Wayland
application (which constrains it to the Weston
window), but has an X window (which xeyes
follows).
A more rigorous test is given by checking the
calls gdk_display_get_name()
and the macros
such as GDK_IS_WAYLAND_DISPLAY()
.
A simple program is
which_display.c:
#include <gtk/gtk.h> #include <gdk/gdkdisplay.h> #include <gdk/gdkwayland.h> #include <gdk/gdkx.h> int main(int argc, char** argv) { gtk_init(&argc, &argv); GdkDisplay *display = gdk_display_get_default (); printf("Display name %s\n", gdk_display_get_name(display)); if (GDK_IS_WAYLAND_DISPLAY (display)) printf("Is Wayland\n"); else if (GDK_IS_X11_DISPLAY (display)) printf("Is X11\n"); return 0; }This has output such as
Display name Wayland
Is Wayland
You won't often need to make Wayland calls or the GTK wrappers around such calls. GTK has made it effectively invisible so that application source code is independent of the backend (Wayland, X11, Windows, ...), the compile process is independent of the backend (at least on Linux) and it is only at runtime that the choice of backend is made.
However, for those interested, here is a list of public GTK Wayland functions:
gboolean gdk_wayland_display_init_gl (GdkDisplay *display)
GdkGLContext * gdk_wayland_window_create_gl_context (GdkWindow *window,
gboolean attach,
GdkGLContext *share,
GError **error);
gboolean gdk_wayland_display_make_gl_context_current (GdkDisplay *display,
GdkGLContext *context);
GType gdk_wayland_device_get_type (void);
struct wl_seat *gdk_wayland_device_get_wl_seat (GdkDevice *device);
struct wl_pointer *gdk_wayland_device_get_wl_pointer (GdkDevice *device);
struct wl_keyboard *gdk_wayland_device_get_wl_keyboard (GdkDevice *device);
struct wl_seat *gdk_wayland_seat_get_wl_seat (GdkSeat *seat);
const gchar *gdk_wayland_device_get_node_path (GdkDevice *device);
void gdk_wayland_device_pad_set_feedback (GdkDevice *device,
GdkDevicePadFeature element,
guint idx,
const gchar *label);
GDK_AVAILABLE_IN_ALL
GType gdk_wayland_display_get_type (void);
GDK_AVAILABLE_IN_ALL
struct wl_display *gdk_wayland_display_get_wl_display (GdkDisplay *display);
GDK_AVAILABLE_IN_ALL
struct wl_compositor *gdk_wayland_display_get_wl_compositor (GdkDisplay *display);
GDK_AVAILABLE_IN_3_10
void gdk_wayland_display_set_cursor_theme (GdkDisplay *display,
const gchar *theme,
gint size);
GDK_AVAILABLE_IN_3_22
void gdk_wayland_display_set_startup_notification_id (GdkDisplay *display,
const char *startup_id);
GType gdk_wayland_gl_context_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
GType gdk_wayland_monitor_get_type (void) G_GNUC_CONST;
GDK_AVAILABLE_IN_3_22
struct wl_output *gdk_wayland_monitor_get_wl_output (GdkMonitor *monitor);
GDK_AVAILABLE_IN_ALL
void
gdk_wayland_selection_add_targets (GdkWindow *window,
GdkAtom selection,
guint ntargets,
GdkAtom *targets);
#define gdk_wayland_selection_clear_targets gdk_wayland_selection_clear_targets_libgtk_only
GDK_AVAILABLE_IN_ALL
void
gdk_wayland_selection_clear_targets (GdkDisplay *display, GdkAtom selection);
GDK_AVAILABLE_IN_ALL
GType gdk_wayland_window_get_type (void);
GDK_AVAILABLE_IN_3_90
GdkWindow * gdk_wayland_window_new_subsurface (GdkDisplay *display,
int event_mask,
const GdkRectangle *position);
GDK_AVAILABLE_IN_ALL
struct wl_surface *gdk_wayland_window_get_wl_surface (GdkWindow *window);
GDK_AVAILABLE_IN_ALL
void gdk_wayland_window_set_use_custom_surface (GdkWindow *window);
GDK_AVAILABLE_IN_3_10
void gdk_wayland_window_set_dbus_properties_libgtk_only (GdkWindow *window,
const char *application_id,
const char *app_menu_path,
const char *menubar_path,
const char *window_object_path,
const char *application_object_path,
const char *unique_bus_name);
typedef void (*GdkWaylandWindowExported) (GdkWindow *window,
const char *handle,
gpointer user_data);
GDK_AVAILABLE_IN_3_22
gboolean gdk_wayland_window_export_handle (GdkWindow *window,
GdkWaylandWindowExported callback,
gpointer user_data,
GDestroyNotify destroy_func);
GDK_AVAILABLE_IN_3_22
void gdk_wayland_window_unexport_handle (GdkWindow *window);
GDK_AVAILABLE_IN_3_22
gboolean gdk_wayland_window_set_transient_for_exported (GdkWindow *window,
char *parent_handle_str);
void translate_wm_button_layout_to_gtk (char *layout);
Copyright © Jan Newmarch, jan@newmarch.name
If you like this book, please contribute using Flattr
or donate using PayPal