text: #define _POSIX_C_SOURCE 200112L
  #include "wl.h"
  
  #include <assert.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdbool.h>
  #include <wayland-client.h>
  #include <wayland-client-protocol.h>
  #include <errno.h>
  #include <fcntl.h>
  #include <sys/mman.h>
  #include <time.h>
  #include <unistd.h>
  #include <linux/input-event-codes.h>
  #include <string.h>
  
  #include "protocols/xdg-shell-client-header.h"
  #include "protocols/xdg-shell.h"
  #include "protocols/wlr-layer-shell-unstable-v1-client-header.h"
  #include "protocols/wlr-layer-shell-unstable-v1.h"
  #include "protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h"
  #include "protocols/wlr-foreign-toplevel-management-unstable-v1.h"
  #include "protocols/idle-client-header.h"
  #include "protocols/idle.h"
  #include "pool-buffer.h"
  
  
  #include "../log.h"
  #include "../settings.h"
  #include "../queues.h"
  #include "../input.h"
  #include "libgwater-wayland.h"
  
  #define MAX_TOUCHPOINTS 10
  
  struct window_wl {
          cairo_surface_t *c_surface;
          cairo_t * c_ctx;
  
          GWaterWaylandSource *esrc;
  };
  
  struct wl_ctx {
          struct wl_display *display;
          struct wl_registry *registry;
          struct wl_compositor *compositor;
          struct wl_shm *shm;
          struct zwlr_layer_shell_v1 *layer_shell;
          struct wl_seat *seat;
  
          struct wl_list outputs;
  
          struct wl_surface *surface;
          struct dunst_output *surface_output;
          struct zwlr_layer_surface_v1 *layer_surface;
          struct dunst_output *layer_surface_output;
          struct wl_callback *frame_callback;
          struct org_kde_kwin_idle *idle_handler;
          struct org_kde_kwin_idle_timeout *idle_timeout;
          struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager;
          bool configured;
          bool dirty;
          bool is_idle;
          bool has_idle_monitor;
  
          struct {
                  struct wl_pointer *wl_pointer;
                  int32_t x, y;
          } pointer;
  
  	struct {
  		struct wl_touch *wl_touch;
  		struct {
  			int32_t x, y;
  		} pts[MAX_TOUCHPOINTS];
  	} touch;
  
          struct dimensions cur_dim;
  
          int32_t width, height;
          struct pool_buffer buffers[2];
          struct pool_buffer *current_buffer;
  };
  
  struct dunst_output {
          uint32_t global_name;
          char *name;
          struct wl_output *wl_output;
          struct wl_list link;
  
          uint32_t scale;
          uint32_t subpixel; // TODO do something with it
          bool fullscreen;
          struct zwlr_foreign_toplevel_handle_v1 *fullscreen_toplevel; // the toplevel that is fullscreened on this output
  };
  
  struct wl_ctx ctx;
  
  static void noop() {
          // This space intentionally left blank
  }
  
  void set_dirty();
  
  static void output_handle_geometry(void *data, struct wl_output *wl_output,
                  int32_t x, int32_t y, int32_t phy_width, int32_t phy_height,
                  int32_t subpixel, const char *make, const char *model,
                  int32_t transform) {
          //TODO do something with the subpixel data
          struct dunst_output *output = data;
          output->subpixel = subpixel;
  }
  
  static void output_handle_scale(void *data, struct wl_output *wl_output,
                  int32_t factor) {
          struct dunst_output *output = data;
          output->scale = factor;
  
          wake_up();
  }
  
  static const struct wl_output_listener output_listener = {
          .geometry = output_handle_geometry,
          .mode = noop,
          .done = noop,
          .scale = output_handle_scale,
  };
  
  static void create_output( struct wl_output *wl_output, uint32_t global_name) {
          struct dunst_output *output = g_malloc0(sizeof(struct dunst_output));
          if (output == NULL) {
                  LOG_E("allocation failed");
                  return;
          }
          static int number = 0;
          LOG_I("New output found - id %i", number);
          output->global_name = global_name;
          output->wl_output = wl_output;
          output->scale = 1;
          output->fullscreen = false;
          wl_list_insert(&ctx.outputs, &output->link);
  
          wl_output_set_user_data(wl_output, output);
          wl_output_add_listener(wl_output, &output_listener, output);
          number++;
  }
  
  static void destroy_output(struct dunst_output *output) {
          if (ctx.surface_output == output) {
                  ctx.surface_output = NULL;
          }
          if (ctx.layer_surface_output == output) {
                  ctx.layer_surface_output = NULL;
          }
          wl_list_remove(&output->link);
          wl_output_destroy(output->wl_output);
          free(output->name);
          free(output);
  }
  
  static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
                  uint32_t time, int32_t id,
                  wl_fixed_t surface_x, wl_fixed_t surface_y) {
          if (id >= MAX_TOUCHPOINTS) {
                  return;
          }
          ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
          ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
  }
  
  static void touch_handle_down(void *data, struct wl_touch *wl_touch,
                  uint32_t serial, uint32_t time, struct wl_surface *sfc, int32_t id,
                  wl_fixed_t surface_x, wl_fixed_t surface_y) {
          if (id >= MAX_TOUCHPOINTS) {
                  return;
          }
          ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
          ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
  }
  
  static void touch_handle_up(void *data, struct wl_touch *wl_touch,
                  uint32_t serial, uint32_t time, int32_t id) {
          if (id >= MAX_TOUCHPOINTS) {
                  return;
          }
          input_handle_click(BTN_TOUCH, false,
                          ctx.touch.pts[id].x, ctx.touch.pts[id].y);
  
  }
  
  static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
                  uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
          ctx.pointer.x = wl_fixed_to_int(surface_x);
          ctx.pointer.y = wl_fixed_to_int(surface_y);
  }
  
  static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
                  uint32_t serial, uint32_t time, uint32_t button,
                  uint32_t button_state) {
          input_handle_click(button, button_state, ctx.pointer.x, ctx.pointer.y);
  }
  
  static const struct wl_pointer_listener pointer_listener = {
          .enter = noop,
          .leave = noop,
          .motion = pointer_handle_motion,
          .button = pointer_handle_button,
          .axis = noop,
  };
  
  static const struct wl_touch_listener touch_listener = {
          .down = touch_handle_down,
          .up = touch_handle_up,
          .motion = touch_handle_motion,
          .frame = noop,
          .cancel = noop,
  };
  
  static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
                  uint32_t capabilities) {
  
          if (ctx.pointer.wl_pointer != NULL) {
                  wl_pointer_release(ctx.pointer.wl_pointer);
                  ctx.pointer.wl_pointer = NULL;
          }
          if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
                  ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat);
                  wl_pointer_add_listener(ctx.pointer.wl_pointer,
                          &pointer_listener, ctx.seat);
          }
          if (ctx.touch.wl_touch != NULL) {
                  wl_touch_release(ctx.touch.wl_touch);
                  ctx.touch.wl_touch = NULL;
          }
          if (capabilities & WL_SEAT_CAPABILITY_TOUCH) {
                  ctx.touch.wl_touch = wl_seat_get_touch(wl_seat);
                  wl_touch_add_listener(ctx.touch.wl_touch,
                          &touch_listener, ctx.seat);
          }
  }
  
  static const struct wl_seat_listener seat_listener = {
          .capabilities = seat_handle_capabilities,
          .name = noop,
  };
  
  
  static void surface_handle_enter(void *data, struct wl_surface *surface,
                  struct wl_output *wl_output) {
          // Don't bother keeping a list of outputs, a layer surface can only be on
          // one output a a time
          ctx.surface_output = wl_output_get_user_data(wl_output);
          set_dirty();
  }
  
  static void surface_handle_leave(void *data, struct wl_surface *surface,
                  struct wl_output *wl_output) {
          ctx.surface_output = NULL;
  }
  
  static const struct wl_surface_listener surface_listener = {
          .enter = surface_handle_enter,
          .leave = surface_handle_leave,
  };
  
  
  static void schedule_frame_and_commit();
  static void send_frame();
  
  static void layer_surface_handle_configure(void *data,
                  struct zwlr_layer_surface_v1 *surface,
                  uint32_t serial, uint32_t width, uint32_t height) {
          ctx.configured = true;
          ctx.width = width;
          ctx.height = height;
  
          // not needed as it is set somewhere else
          /* zwlr_layer_surface_v1_set_size(surface, width, height);  */
          zwlr_layer_surface_v1_ack_configure(surface, serial);
          send_frame();
  }
  
  static void layer_surface_handle_closed(void *data,
                  struct zwlr_layer_surface_v1 *surface) {
          LOG_I("Destroying layer");
          if (ctx.layer_surface)
                  zwlr_layer_surface_v1_destroy(ctx.layer_surface);
          ctx.layer_surface = NULL;
  
          if (ctx.surface)
                  wl_surface_destroy(ctx.surface);
          ctx.surface = NULL;
  
          if (ctx.frame_callback) {
                  wl_callback_destroy(ctx.frame_callback);
                  ctx.frame_callback = NULL;
                  ctx.dirty = true;
          }
  
          if (ctx.configured) {
                  ctx.configured = false;
                  ctx.width = ctx.height = 0;
                  ctx.dirty = true;
          }
  
          if (ctx.dirty) {
                  schedule_frame_and_commit();
          }
  }
  
  static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
          .configure = layer_surface_handle_configure,
          .closed = layer_surface_handle_closed,
  };
  
  
  static void idle_start (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
          ctx.is_idle = true;
          LOG_D("User went idle");
  }
  static void idle_stop (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
          ctx.is_idle = false;
          LOG_D("User isn't idle anymore");
  }
  
  static const struct org_kde_kwin_idle_timeout_listener idle_timeout_listener = {
          .idle = idle_start,
          .resumed = idle_stop,
  };
  
  static void add_seat_to_idle_handler(struct wl_seat *seat) {
          if (!ctx.idle_handler){
                  return;
          }
          if (settings.idle_threshold > 0) {
                  uint32_t timeout_ms = settings.idle_threshold/1000;
                  ctx.idle_timeout = org_kde_kwin_idle_get_idle_timeout(ctx.idle_handler, seat, timeout_ms);
                  org_kde_kwin_idle_timeout_add_listener(ctx.idle_timeout, &idle_timeout_listener, 0);
                  ctx.has_idle_monitor = true;
          }
  }
  
  // Warning, can return NULL
  static struct dunst_output *get_configured_output() {
          int n = 0;
          int target_monitor = settings.monitor;
  
          struct dunst_output *first_output = NULL, *configured_output = NULL,
                              *tmp_output = NULL;
          wl_list_for_each(tmp_output, &ctx.outputs, link) {
                  if (n == 0)
                          first_output = tmp_output;
                  if (n == target_monitor)
                          configured_output = tmp_output;
                  n++;
          }
  
          // There's only 1 output, so return that
          if (n == 1)
                  return first_output;
  
          switch (settings.f_mode){
                  case FOLLOW_NONE: ; // this semicolon is neccesary
                          if (!configured_output) {
                                  LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor);
                          }
                          return configured_output;
                  case FOLLOW_MOUSE:
                          // fallthrough
                  case FOLLOW_KEYBOARD:
                          // fallthrough
                  default:
                          return NULL;
          }
  }
  
  // does not do null checking
  static void dunst_output_set_fullscreen(struct dunst_output *output,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel,
                  bool fullscreen) {
          output->fullscreen = fullscreen;
          if (fullscreen)
                  output->fullscreen_toplevel = toplevel;
          else
                  output->fullscreen_toplevel = NULL;
  
          LOG_D("Set output %i fullscreen state %i", output->global_name, fullscreen);
          wake_up();
  }
  
  static void toplevel_output_leave(void *data,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel,
                  struct wl_output *output) {
          zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, NULL);
  }
  
  static void toplevel_output_enter(void *data,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel,
                  struct wl_output *output) {
          // FIXME toplevel can be on multiple outputs, so a list of outputs should be kept
          zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, output);
  }
  
  static void toplevel_closed(void *data,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel) {
          struct wl_output *output_wl = (struct wl_output*) data;
  
          if (output_wl == NULL) {
                  return;
          }
          struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
  
          if (output == NULL) {
                  return;
          }
          dunst_output_set_fullscreen(output, toplevel, false);
  }
  
  static void toplevel_state(void *data,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel,
                  struct wl_array *state) {
          struct wl_output *output_wl = (struct wl_output*) data;
          if (output_wl == NULL) {
                  return;
          }
          struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
          if (output == NULL) {
                  return;
          }
  
          bool fullscreen = false;
          bool activated = false;
          enum zwlr_foreign_toplevel_handle_v1_state* element;
          wl_array_for_each(element, state){
                  if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) {
                          fullscreen = true;
                  }
                  if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) {
                          activated = true;
                  }
          }
          if (fullscreen && activated) {
                  dunst_output_set_fullscreen(output, toplevel, true);
  
          } else {
                  if (output->fullscreen_toplevel == toplevel) {
                          // this toplevel was fullscreen, but isn't anymore
                          dunst_output_set_fullscreen(output, toplevel, false);
                  }
          }
  }
  
  static const struct zwlr_foreign_toplevel_handle_v1_listener foreign_toplevel_handle_listener = {
          .title = noop,
          .app_id = noop,
          .output_enter = toplevel_output_enter,
          .output_leave = toplevel_output_leave,
          .state = toplevel_state,
          .done = noop,
          .closed = toplevel_closed,
  };
  
  static void toplevel_created(void *data,
                  struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
                  struct zwlr_foreign_toplevel_handle_v1 *toplevel){
          zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &foreign_toplevel_handle_listener, NULL);
  }
  
  static void toplevel_finished(void *data,
                  struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1){
  }
  
  static const struct zwlr_foreign_toplevel_manager_v1_listener foreign_toplevel_manager_listener = {
          .toplevel = toplevel_created,
          .finished = toplevel_finished,
  };
  
  static void handle_global(void *data, struct wl_registry *registry,
                  uint32_t name, const char *interface, uint32_t version) {
          int *count = data;
          if (*count == 0)
          {
                  if (strcmp(interface, wl_compositor_interface.name) == 0) {
                          ctx.compositor = wl_registry_bind(registry, name,
                                          &wl_compositor_interface, 4);
                  } else if (strcmp(interface, wl_shm_interface.name) == 0) {
                          ctx.shm = wl_registry_bind(registry, name,
                                          &wl_shm_interface, 1);
                  } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
                          ctx.layer_shell = wl_registry_bind(registry, name,
                                          &zwlr_layer_shell_v1_interface, 1);
                  } else if (strcmp(interface, wl_seat_interface.name) == 0) {
                          ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
                          wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat);
                          add_seat_to_idle_handler(ctx.seat);
                  } else if (strcmp(interface, wl_output_interface.name) == 0) {
                          struct wl_output *output =
                                  wl_registry_bind(registry, name, &wl_output_interface, 3);
                          create_output(output, name);
                  } else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
                                  version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
                          ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
                  }
          } else {
                  if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0 &&
                                  version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) {
                          // Only bind after the second pass to bind after binding to all the outputs.
                          // This is because otherwise toplevel_enter evens won't be sent.
                          ctx.toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 2);
                          zwlr_foreign_toplevel_manager_v1_add_listener(ctx.toplevel_manager, &foreign_toplevel_manager_listener, NULL);
                  }
          }
  }
  
  static void handle_global_remove(void *data, struct wl_registry *registry,
                  uint32_t name) {
          struct dunst_output *output, *tmp;
          wl_list_for_each_safe(output, tmp, &ctx.outputs, link) {
                  if (output->global_name == name) {
                          destroy_output(output);
                          break;
                  }
          }
  }
  
  static const struct wl_registry_listener registry_listener = {
          .global = handle_global,
          .global_remove = handle_global_remove,
  };
  
  bool wl_init() {
          wl_list_init(&ctx.outputs);
          //wl_list_init(&ctx.seats); // TODO multi-seat support
  
          ctx.display = wl_display_connect(NULL);
  
          if (ctx.display == NULL) {
                  LOG_W("failed to create display");
                  return false;
          }
  
          int count = 0;
          ctx.registry = wl_display_get_registry(ctx.display);
          wl_registry_add_listener(ctx.registry, &registry_listener, &count);
          wl_display_roundtrip(ctx.display);
  
          count = 1;
          // we need a second pass to let for foreign_toplevel (look there for more info)
          ctx.registry = wl_display_get_registry(ctx.display);
          wl_registry_add_listener(ctx.registry, &registry_listener, &count);
          wl_display_roundtrip(ctx.display);
  
          if (ctx.compositor == NULL) {
                  LOG_W("compositor doesn't support wl_compositor");
                  return false;
          }
          if (ctx.shm == NULL) {
                  LOG_W("compositor doesn't support wl_shm");
                  return false;
          }
          if (ctx.layer_shell == NULL) {
                  LOG_W("compositor doesn't support zwlr_layer_shell_v1");
                  return false;
          }
          if (ctx.seat == NULL) {
                  LOG_W("no seat was found, so dunst cannot see input");
          } else {
                  if (ctx.idle_handler == NULL) {
                          LOG_I("compositor doesn't support org_kde_kwin_idle_interface");
                  }
                  else if (ctx.idle_timeout == NULL && settings.idle_threshold > 0) {
                          // something went wrong in setting the timeout
                          LOG_W("couldn't set idle timeout");
                  }
          }
  
          if (ctx.toplevel_manager == NULL) {
                  LOG_W("compositor doesn't support zwlr_foreign_toplevel_v1. Fullscreen detection won't work");
          }
  
          return true;
  }
  
  void wl_deinit() {
          // We need to check if any of these are NULL, since the initialization
          // could have been aborted half way through, or the compositor doesn't
          // support some of these features.
          if (ctx.layer_surface != NULL) {
                  zwlr_layer_surface_v1_destroy(ctx.layer_surface);
          }
          if (ctx.surface != NULL) {
                  wl_surface_destroy(ctx.surface);
          }
          finish_buffer(&ctx.buffers[0]);
          finish_buffer(&ctx.buffers[1]);
  
          // The output list is initialized at the start of init, so no need to
          // check for NULL
          struct dunst_output *output, *output_tmp;
          wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) {
                  destroy_output(output);
          }
  
          if (ctx.seat) {
                  if (ctx.pointer.wl_pointer)
                          wl_pointer_release(ctx.pointer.wl_pointer);
                  wl_seat_release(ctx.seat);
                  ctx.seat = NULL;
          }
  
          if (ctx.idle_handler)
                  org_kde_kwin_idle_destroy(ctx.idle_handler);
  
          if (ctx.idle_timeout)
                  org_kde_kwin_idle_timeout_release(ctx.idle_timeout);
  
          if (ctx.layer_shell)
                  zwlr_layer_shell_v1_destroy(ctx.layer_shell);
  
          if (ctx.compositor)
                  wl_compositor_destroy(ctx.compositor);
  
          if (ctx.shm)
                  wl_shm_destroy(ctx.shm);
  
          if (ctx.registry)
                  wl_registry_destroy(ctx.registry);
  
          if (ctx.display)
                  wl_display_disconnect(ctx.display);
  }
  
  static void schedule_frame_and_commit();
  
  // Draw and commit a new frame.
  static void send_frame() {
          int scale = wl_get_scale();
  
          struct dunst_output *output = get_configured_output();
          int height = ctx.cur_dim.h;
          int width = ctx.cur_dim.w;
  
          // There are two cases where we want to tear down the surface: zero
          // notifications (height = 0) or moving between outputs.
          if (height == 0 || ctx.layer_surface_output != output) {
                  if (ctx.layer_surface != NULL) {
                          zwlr_layer_surface_v1_destroy(ctx.layer_surface);
                          ctx.layer_surface = NULL;
                  }
                  if (ctx.surface != NULL) {
                          wl_surface_destroy(ctx.surface);
                          ctx.surface = NULL;
                  }
                  ctx.width = ctx.height = 0;
                  ctx.surface_output = NULL;
                  ctx.configured = false;
          }
  
          // If there are no notifications, there's no point in recreating the
          // surface right now.
          if (height == 0) {
                  ctx.dirty = false;
                  return;
          }
  
          // If we've made it here, there is something to draw. If the surface
          // doesn't exist (this is the first notification, or we moved to a
          // different output), we need to create it.
          if (ctx.layer_surface == NULL) {
                  struct wl_output *wl_output = NULL;
                  if (output != NULL) {
                          wl_output = output->wl_output;
                  }
                  ctx.layer_surface_output = output;
                  ctx.surface = wl_compositor_create_surface(ctx.compositor);
                  wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
  
                  ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
                          ctx.layer_shell, ctx.surface, wl_output,
                          settings.layer, "notifications");
                  zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
                          &layer_surface_listener, NULL);
  
                  // Because we're creating a new surface, we aren't going to draw
                  // anything into it during this call. We don't know what size the
                  // surface will be until we've asked the compositor for what we want
                  // and it has responded with what it actually gave us. We also know
                  // that the height we would _like_ to draw (greater than zero, or we
                  // would have bailed already) is different from our ctx.height
                  // (which has to be zero here), so we can fall through to the next
                  // block to let it set the size for us.
          }
  
          assert(ctx.layer_surface);
  
          // We now want to resize the surface if it isn't the right size. If the
          // surface is brand new, it doesn't even have a size yet. If it already
          // exists, we might need to resize if the list of notifications has changed
          // since the last time we drew.
          if (ctx.height != height || ctx.width != width) {
                  struct dimensions dim = ctx.cur_dim;
                  // Set window size
                  zwlr_layer_surface_v1_set_size(ctx.layer_surface,
                                  dim.w, dim.h);
  
                  // TODO Do this only once
                  uint32_t anchor = 0;
                  if (settings.geometry.negative_x) {
                          anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
                  } else {
                          anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
                  }
  
                  if (settings.geometry.negative_y) {
                          anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
                  } else {
                          anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
                  }
  
                  // Put the window at the right position
                  zwlr_layer_surface_v1_set_anchor(ctx.layer_surface,
                          anchor);
                  zwlr_layer_surface_v1_set_margin(ctx.layer_surface,
                                  abs(settings.geometry.y), // top
                                  abs(settings.geometry.x), // right
                                  abs(settings.geometry.y), // bottom
                                  abs(settings.geometry.x));// left
  
                  wl_surface_commit(ctx.surface);
  
                  // Now we're going to bail without drawing anything. This gives the
                  // compositor a chance to create the surface and tell us what size we
                  // were actually granted, which may be smaller than what we asked for
                  // depending on the screen size and layout of other layer surfaces.
                  // This information is provided in layer_surface_handle_configure,
                  // which will then call send_frame again. When that call happens, the
                  // layer surface will exist and the height will hopefully match what
                  // we asked for. That means we won't return here, and will actually
                  // draw into the surface down below.
                  // TODO: If the compositor doesn't send a configure with the size we
                  // requested, we'll enter an infinite loop. We need to keep track of
                  // the fact that a request was sent separately from what height we are.
                  wl_display_roundtrip(ctx.display);
                  return;
          }
  
          assert(ctx.configured);
  
          // Yay we can finally draw something!
          wl_surface_set_buffer_scale(ctx.surface, scale);
          wl_surface_damage_buffer(ctx.surface, 0, 0, INT32_MAX, INT32_MAX);
          wl_surface_attach(ctx.surface, ctx.current_buffer->buffer, 0, 0);
          ctx.current_buffer->busy = true;
  
          // Schedule a frame in case the state becomes dirty again
          schedule_frame_and_commit();
  
          ctx.dirty = false;
  }
  
  static void frame_handle_done(void *data, struct wl_callback *callback,
                  uint32_t time) {
          wl_callback_destroy(ctx.frame_callback);
          ctx.frame_callback = NULL;
  
          // Only draw again if we need to
          if (ctx.dirty) {
                  send_frame();
          }
  }
  
  static const struct wl_callback_listener frame_listener = {
          .done = frame_handle_done,
  };
  
  static void schedule_frame_and_commit() {
          if (ctx.frame_callback) {
                  return;
          }
          if (ctx.surface == NULL) {
                  // We don't yet have a surface, create it immediately
                  send_frame();
                  return;
          }
          ctx.frame_callback = wl_surface_frame(ctx.surface);
          wl_callback_add_listener(ctx.frame_callback, &frame_listener, NULL);
          wl_surface_commit(ctx.surface);
  }
  
  void set_dirty() {
          if (ctx.dirty) {
                  return;
          }
          ctx.dirty = true;
          schedule_frame_and_commit();
  }
  
  window wl_win_create(void) {
          struct window_wl *win = g_malloc0(sizeof(struct window_wl));
  
          win->esrc = g_water_wayland_source_new_for_display(NULL, ctx.display);
          return win;
  }
  
  void wl_win_destroy(window winptr) {
          struct window_wl *win = (struct window_wl*)winptr;
  
          g_water_wayland_source_free(win->esrc);
          // FIXME: Dealloc everything
          g_free(win);
  }
  
  void wl_win_show(window win) {
          // This is here for compatibilty with the X11 output. The window is
          // already shown in wl_display_surface.
  }
  
  void wl_win_hide(window win) {
          LOG_I("Wayland: Hiding window");
          ctx.cur_dim.h = 0;
          set_dirty();
          wl_display_roundtrip(ctx.display);
  }
  
  void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) {
          /* struct window_wl *win = (struct window_wl*)winptr; */
          int scale = wl_get_scale();
          LOG_D("Buffer size (scaled) %ix%i", dim->w * scale, dim->h * scale);
          ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers,
                          dim->w * scale, dim->h * scale);
  
          cairo_t *c = ctx.current_buffer->cairo;
          cairo_save(c);
          cairo_set_source_surface(c, srf, 0, 0);
          cairo_rectangle(c, 0, 0, dim->w * scale, dim->h * scale);
          cairo_fill(c);
          cairo_restore(c);
  
          ctx.cur_dim = *dim;
  
          set_dirty();
          wl_display_roundtrip(ctx.display);
  }
  
  cairo_t* wl_win_get_context(window winptr) {
          struct window_wl *win = (struct window_wl*)winptr;
          ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, 500, 500);
          win->c_surface = ctx.current_buffer->surface;
          win->c_ctx = ctx.current_buffer->cairo;
          return win->c_ctx;
  }
  
  const struct screen_info* wl_get_active_screen(void) {
          // TODO Screen size detection
          static struct screen_info scr = {
                  .w = 1920,
                  .h = 1080,
                  .x = 0,
                  .y = 0,
                  .id = 0,
                  .mmh = 500
          };
          scr.dpi = wl_get_scale() * 96;
          return &scr;
  }
  
  bool wl_is_idle(void) {
          LOG_I("Idle status queried: %i", ctx.is_idle);
          // When the user doesn't have a seat, or their compositor doesn't support the idle
          // protocol, we'll assume that they are not idle.
          if (settings.idle_threshold == 0 || ctx.has_idle_monitor == false) {
                  return false;
          } else {
                  return ctx.is_idle;
          }
  }
  
  bool wl_have_fullscreen_window(void) {
          bool have_fullscreen = false;
  
          struct dunst_output *current_output = get_configured_output();
  
          if (!current_output) {
                  // Cannot detect focused output, so return true if any of the
                  // outputs is fullscreen. This will work even when unfocused
                  // outputs have fullscreen toplevels, since a toplevel has to
                  // be fullscreen and activate to consider an output fullscreen.
                  struct dunst_output *output;
                  wl_list_for_each(output, &ctx.outputs, link) {
                          have_fullscreen |= output->fullscreen;
                  }
          } else {
                  have_fullscreen = current_output->fullscreen;
          }
  
          LOG_D("Fullscreen queried: %i", have_fullscreen);
          return have_fullscreen;
  }
  
  double wl_get_scale(void) {
          int scale = 0;
          struct dunst_output *output = get_configured_output();
          if (output) {
                  scale = output->scale;
          } else {
                  // return the largest scale
                  struct dunst_output *output;
                  wl_list_for_each(output, &ctx.outputs, link) {
                          scale = MAX(output->scale, scale);
                  }
          }
          if (scale <= 0)
                  scale = 1;
          return scale;
  }
  /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
  