Upto: Table of Contents of full book "Programming Wayland Clients"

This project hasn't been updated since mid-2017. Wayland has moved on since then, so the information here may be out of date, and there is no guarantee the programs still work.

GTK

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.

Resources

Fedora RPM wayland-devel, wayland-protocols-devel

Installing

Under Fedora, install gtk3-devel mesa-libegl-devel

A simple GTK application

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 backends

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;
}
	
      

Running under X or under Wayland?

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
	
      

List of GDK Wayland calls

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: