gimp but for windows: a wayland compositor
git clone https://github.com/m-col/wimp
Files | Refs | Readme

Commit fbd8d7e039fd0f72d605950aa317742b1cf89bad
Parent: 98659c1a8cf1063c4f142e73c945aef2f0980524
Author: mcol <mcol@posteo.net>
Date: 2020-11-22 17:30:18 +0000
Committer: mcol <mcol@posteo.net>
Committed: 2020-11-22 17:30:18 +0000

reorganise functions, move actions into single file, check includes

src/types.h Modified

@@ -36,6 +36,7 @@
     struct wlr_xdg_shell *shell;
     struct wl_listener new_xdg_surface_listener;
     struct wl_list desks;
+    int desk_count;
     struct desk *current_desk;
 
     struct wlr_cursor *cursor;
@@ -84,9 +85,38 @@
     struct wl_listener key_listener;
 };
 
+struct wallpaper {
+    struct wlr_texture *texture;
+    int width, height;
+};
+
+struct desk {
+    struct wl_list link;
+    struct server *server;
+    struct wl_list views;
+    float background[4];
+    struct wallpaper *wallpaper;
+    double panned_x, panned_y;
+    int index;
+    double zoom;
+};
+
+struct binding {
+    struct wl_list link;
+    enum wlr_keyboard_modifier mods;
+    uint32_t key;
+    action action;
+    void *data;
+};
+
 struct motion {
     double dx;
     double dy;
 };
 
+enum mouse_keys {
+    MOTION,
+    SCROLL,
+};
+
 #endif

src/shell.h Modified

@@ -3,16 +3,9 @@
 
 #include "types.h"
 
-void close_current_window(struct server *server, void *data);
 void unmap_view(struct view *view);
 void map_view(struct view *view);
 void focus_view(struct view *view, struct wlr_surface *surface);
-void on_map(struct wl_listener *listener, void *data);
-void on_surface_destroy(struct wl_listener *listener, void *data);
-void process_move_resize(struct view *view, enum cursor_mode mode, uint32_t edges);
-void on_request_move(struct wl_listener *listener, void *data);
-void on_request_resize(struct wl_listener *listener, void *data);
-void on_new_xdg_surface(struct wl_listener *listener, void *data);
 void set_up_shell(struct server *server);
 
 #endif

src/shell.c Modified

@@ -1,22 +1,8 @@
 #include <wlr/util/edges.h>
 
-#include "config.h"
-#include "desk.h"
-#include "output.h"
 #include "types.h"
 
 
-void close_current_window(struct server *server, void *data) {
-    struct wlr_surface *surface = server->seat->keyboard_state.focused_surface;
-    if (surface) {
-	struct wlr_xdg_surface *xdg_surface = surface->role_data;
-	if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && xdg_surface->toplevel) {
-	    xdg_toplevel_send_close(xdg_surface->toplevel->resource);
-	}
-    }
-}
-
-
 void unmap_view(struct view *view) {
     wl_signal_emit(&view->surface->events.unmap, view);
 }

src/output.h Modified

@@ -1,30 +1,8 @@
 #ifndef DESKWM_OUTPUT_H
 #define DESKWM_OUTPUT_H
 
-#include <wlr/types/wlr_output_layout.h>
-
 #include "types.h"
 
-struct output {
-    struct wl_list link;
-    struct server *server;
-    struct wlr_output *wlr_output;
-    struct wl_listener frame_listener;
-};
-
-struct render_data {
-    struct wlr_output *output;
-    struct wlr_renderer *renderer;
-    struct view *view;
-    struct timespec *when;
-    double zoom;
-};
-
-void render_surface(
-    struct wlr_surface *surface, int sx, int sy, void *data
-);
-void on_frame(struct wl_listener *listener, void *data);
-void on_new_output(struct wl_listener *listener, void *data);
 void set_up_outputs(struct server *server);
 
 #endif

src/output.c Modified

@@ -2,11 +2,27 @@
 #include <wlr/types/wlr_matrix.h>
 #include <wlr/types/wlr_output_layout.h>
 
-#include "desk.h"
 #include "output.h"
 #include "types.h"
 
 
+struct output {
+    struct wl_list link;
+    struct server *server;
+    struct wlr_output *wlr_output;
+    struct wl_listener frame_listener;
+};
+
+
+struct render_data {
+    struct wlr_output *output;
+    struct wlr_renderer *renderer;
+    struct view *view;
+    struct timespec *when;
+    double zoom;
+};
+
+
 void render_surface(
     struct wlr_surface *surface, int sx, int sy, void *data
 ) {

src/input.h Modified

@@ -2,41 +2,10 @@
 #define DESKWM_INPUT_H
 
 #include "types.h"
-#include "shell.h"
 
 enum wlr_keyboard_modifier modifier_by_name(char *mod);
 void add_binding(struct server *server, char *data, int line);
-void process_cursor_move(struct server *server, uint32_t time, double zoom);
-void process_cursor_resize(struct server *server, uint32_t time, double zoom);
-bool view_at(
-    struct view *view, double lx, double ly, struct wlr_surface **surface,
-    double *sx, double *sy
-);
-struct view *desktop_view_at(
-    struct server *server, double lx, double ly, struct wlr_surface **surface,
-    double *sx, double *sy
-);
-void process_cursor_motion(struct server *server, uint32_t time, double dx, double dy);
-void on_cursor_motion(struct wl_listener *listener, void *data);
-void on_cursor_motion_absolute(struct wl_listener *listener, void *data);
-void on_cursor_button(struct wl_listener *listener, void *data);
-void on_cursor_axis(struct wl_listener *listener, void *data);
-void on_cursor_frame(struct wl_listener *listener, void *data);
-void on_modifier(struct wl_listener *listener, void *data);
-void on_key(struct wl_listener *listener, void *data);
-void on_new_keyboard(struct server *server, struct wlr_input_device *device);
-void on_new_input(struct wl_listener *listener, void *data);
-void on_request_cursor(struct wl_listener *listener, void *data);
-void on_request_set_selection(struct wl_listener *listener, void *data);
 void set_up_cursor(struct server *server);
 void set_up_keyboard(struct server *server);
 
-struct key_binding {
-    struct wl_list link;
-    enum wlr_keyboard_modifier mods;
-    uint32_t key;
-    action action;
-    void *data;
-};
-
 #endif

src/input.c Modified

@@ -5,7 +5,6 @@
 #include <wlr/types/wlr_data_device.h>
 #include <wlr/types/wlr_xcursor_manager.h>
 
-#include "desk.h"
 #include "input.h"
 #include "shell.h"
 #include "types.h"
@@ -13,150 +12,6 @@
 #define is_int(s) (strspn(s, "-0123456789") == strlen(s))
 
 
-void shutdown(struct server *server, void *data) {
-    wl_display_terminate(server->display);
-}
-
-
-void exec_command(struct server *server, void *data) {
-    if (fork() == 0) {
-	execl("/bin/sh", "/bin/sh", "-c", data, (void *)NULL);
-    }
-}
-
-
-enum wlr_keyboard_modifier modifier_by_name(char *mod) {
-    if (strcasecmp(mod, "shift") == 0)
-	return WLR_MODIFIER_SHIFT;
-    if (strcasecmp(mod, "caps") == 0)
-	return WLR_MODIFIER_CAPS;
-    if (strcasecmp(mod, "ctrl") == 0)
-	return WLR_MODIFIER_CTRL;
-    if (strcasecmp(mod, "alt") == 0)
-	return WLR_MODIFIER_ALT;
-    if (strcasecmp(mod, "mod2") == 0)
-	return WLR_MODIFIER_MOD2;
-    if (strcasecmp(mod, "mod3") == 0)
-	return WLR_MODIFIER_MOD3;
-    if (strcasecmp(mod, "logo") == 0)
-	return WLR_MODIFIER_LOGO;
-    if (strcasecmp(mod, "mod5") == 0)
-	return WLR_MODIFIER_MOD5;
-    return 0;
-}
-
-
-void assign_action(char *name, char *data, struct key_binding *kb) {
-    kb->data = NULL;
-    kb->action = NULL;
-
-    if (strcasecmp(name, "shutdown") == 0)
-	kb->action = &shutdown;
-    else if (strcasecmp(name, "close_current_window") == 0)
-	kb->action = &close_current_window;
-    else if (strcasecmp(name, "next_desk") == 0)
-	kb->action = &next_desk;
-    else if (strcasecmp(name, "prev_desk") == 0)
-	kb->action = &prev_desk;
-    else if (strcasecmp(name, "pan_desk") == 0)
-	kb->action = &pan_desk;
-    else if (strcasecmp(name, "reset_pan") == 0)
-	kb->action = &reset_pan;
-    else if (strcasecmp(name, "save_pan") == 0)
-	kb->action = &save_pan;
-    else if (strcasecmp(name, "zoom_in") == 0) {
-	kb->action = &zoom_desk;
-	kb->data = calloc(1, sizeof(int));
-	*(int *)(kb->data) = 1;
-    }
-    else if (strcasecmp(name, "zoom_out") == 0) {
-	kb->action = &zoom_desk;
-	kb->data = calloc(1, sizeof(int));
-	*(int *)(kb->data) = -1;
-    }
-    else if (strcasecmp(name, "zoom_out") == 0) {
-	kb->action = &zoom_desk;
-	kb->data = calloc(1, sizeof(int));
-	*(int *)(kb->data) = -1;
-    }
-    else if (strcasecmp(name, "exec") == 0) {
-	kb->action = &exec_command;
-	kb->data = calloc(strlen(data), sizeof(char));
-	strncpy(kb->data, data, strlen(data));
-    }
-}
-
-
-enum mouse_keys {
-    MOTION,
-    SCROLL,
-};
-
-
-enum mouse_keys mouse_key_from_name(char *name) {
-    if (strcasecmp(name, "motion") == 0)
-	return MOTION;
-    else if (strcasecmp(name, "scroll") == 0)
-	return SCROLL;
-    return 0;
-}
-
-
-void add_binding(struct server *server, char *data, int line) {
-    struct key_binding *kb = calloc(1, sizeof(struct key_binding));
-    enum wlr_keyboard_modifier mod;
-    char *s = strtok(data, " \t\n\r");
-    bool is_mouse_binding = false;
-
-    // additional modifiers
-    kb->mods = 0;
-    for (int i = 0; i < 8; i++) {
-	mod = modifier_by_name(s);
-	if (mod == 0)
-	    break;
-	kb->mods |= mod;
-	s = strtok(NULL, " \t\n\r");
-    }
-
-    // key
-    kb->key = xkb_keysym_from_name(s, XKB_KEYSYM_CASE_INSENSITIVE);
-    if (kb->key == XKB_KEY_NoSymbol) {
-	kb->key = mouse_key_from_name(s);
-	if (kb->key == 0) {
-	    wlr_log(WLR_ERROR, "Config line %i: No such key '%s'.", line, s);
-	    free(kb);
-	    return;
-	}
-	is_mouse_binding = true;
-    }
-
-    // action
-    s = strtok(NULL, " \t\n\r");
-    assign_action(s, strtok(NULL, ""), kb);
-    if (kb->action == NULL) {
-	wlr_log(WLR_ERROR, "Config line %i: No such action '%s'.", line, s);
-	free(kb);
-	return;
-    }
-
-    struct key_binding *kb_existing;
-    struct wl_list *bindings;
-    if (is_mouse_binding) {
-	bindings = &server->mouse_bindings;
-    } else {
-	bindings = &server->key_bindings;
-    }
-    wl_list_for_each(kb_existing, bindings, link) {
-	if (kb_existing->key == kb->key && kb_existing->mods == kb->mods) {
-	    wlr_log(WLR_ERROR, "Config line %i: binding exists for key combo.", line);
-	    free(kb);
-	    return;
-	}
-    }
-    wl_list_insert(bindings, &kb->link);
-}
-
-
 void process_cursor_move(struct server *server, uint32_t time, double zoom) {
     server->grabbed_view->x = (server->cursor->x - server->grab_x) / zoom;
     server->grabbed_view->y = (server->cursor->y - server->grab_y) / zoom;
@@ -356,7 +211,7 @@
     );
 
     uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
-    struct key_binding *kb;
+    struct binding *kb;
     bool handled = false;
     if ((modifiers & server->mod)) {
 	modifiers &= ~server->mod;
@@ -396,7 +251,7 @@
 
     bool handled = false;
     uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard);
-    struct key_binding *kb;
+    struct binding *kb;
     if ((modifiers & server->mod) && event->state == WLR_KEY_PRESSED) {
 	modifiers &= ~server->mod;
 	for (int i = 0; i < nsyms; i++) {

src/desk.h Modified

@@ -4,28 +4,6 @@
 #include "types.h"
 
 void add_desk(struct server *server);
-void remove_desk(struct desk *desk);
-void next_desk(struct server *server, void *data);
-void prev_desk(struct server *server, void *data);
-void pan_desk(struct server *server, void *data);
-void reset_pan(struct server *server, void *data);
-void save_pan(struct server *server, void *data);
-void zoom_desk(struct server *server, void *data);
-
-struct wallpaper {
-    struct wlr_texture *texture;
-    int width, height;
-};
-
-struct desk {
-    struct wl_list link;
-    struct server *server;
-    struct wl_list views;
-    float background[4];
-    struct wallpaper *wallpaper;
-    double panned_x, panned_y;
-    int index;
-    double zoom;
-};
+void set_desk(struct server *server, struct desk *desk);
 
 #endif

src/desk.c Modified

@@ -4,9 +4,6 @@
 #include "types.h"
 
 
-static int num_desks = 0;
-
-
 void add_desk(struct server *server) {
     struct desk *desk = calloc(1, sizeof(struct desk));
     wl_list_insert(server->desks.prev, &desk->link);
@@ -14,15 +11,9 @@
     wl_list_init(&desk->views);
     assign_colour("#5D479D", desk->background);
     desk->wallpaper = NULL;
-    desk->index = num_desks;
+    desk->index = server->desk_count;
     desk->zoom = 1;
-    num_desks++;
-}
-
-
-void remove_desk(struct desk *desk) {
-    wl_list_remove(&desk->link);
-    free(desk);
+    server->desk_count++;
 }
 
 
@@ -36,81 +27,3 @@
 	map_view(view);
     }
 }
-
-
-void next_desk(struct server *server, void *data) {
-    struct desk *desk;
-    if (server->current_desk->index + 1 == num_desks) {
-	desk = wl_container_of(server->desks.next, desk, link);
-    } else {
-	desk = wl_container_of(server->current_desk->link.next, desk, link);
-    }
-    set_desk(server, desk);
-}
-
-
-void prev_desk(struct server *server, void *data) {
-    struct desk *desk;
-    if (server->current_desk->index == 0) {
-	desk = wl_container_of(server->desks.prev, desk, link);
-    } else {
-	desk = wl_container_of(server->current_desk->link.prev, desk, link);
-    }
-    set_desk(server, desk);
-}
-
-
-void pan_desk(struct server *server, void *data) {
-    struct desk *desk = server->current_desk;
-    struct motion motion = *(struct motion*)data;
-    double dx = motion.dx;
-    double dy = motion.dy;
-    struct view *view;
-    wl_list_for_each(view, &desk->views, link) {
-	view->x += dx;
-	view->y += dy;
-    }
-    desk->panned_x += dx;
-    desk->panned_y += dy;
-}
-
-
-void reset_pan(struct server *server, void *data) {
-    struct desk *desk = server->current_desk;
-    struct view *view;
-    wl_list_for_each(view, &desk->views, link) {
-	view->x -= desk->panned_x;
-	view->y -= desk->panned_y;
-    }
-    desk->panned_x = desk->panned_y = 0;
-    desk->zoom = 1;
-}
-
-
-void save_pan(struct server *server, void *data) {
-    server->current_desk->panned_x = server->current_desk->panned_y = 0;
-}
-
-
-void zoom_desk(struct server *server, void *data) {
-    /* dir > 0 ? zoom in : zoom out */
-    struct desk *desk = server->current_desk;
-    int dir = *(int*)data;
-    double f = dir > 0 ? 1.015 : 1/1.015;
-    if (
-	(f > 1 && desk->zoom >= server->zoom_max) ||
-	(f < 1 && desk->zoom <= server->zoom_min)
-    ) {
-	return;
-    }
-    desk->zoom *= f;
-    double fx = server->cursor->x * (f - 1) / desk->zoom;
-    double fy = server->cursor->y * (f - 1) / desk->zoom;
-    struct view *view;
-    wl_list_for_each(view, &desk->views, link) {
-	view->x -= fx;
-	view->y -= fy;
-    }
-    desk->panned_x -= fx;
-    desk->panned_y -= fy;
-}

src/config.c Modified

@@ -6,6 +6,7 @@
 #include <wlr/render/wlr_texture.h>
 #include <unistd.h>
 
+#include "action.h"
 #include "config.h"
 #include "desk.h"
 #include "input.h"
@@ -14,6 +15,131 @@
 #define is_decimal(s) (strspn(s, "0123456789.") == strlen(s))
 
 
+enum wlr_keyboard_modifier modifier_by_name(char *mod) {
+    if (strcasecmp(mod, "shift") == 0)
+	return WLR_MODIFIER_SHIFT;
+    if (strcasecmp(mod, "caps") == 0)
+	return WLR_MODIFIER_CAPS;
+    if (strcasecmp(mod, "ctrl") == 0)
+	return WLR_MODIFIER_CTRL;
+    if (strcasecmp(mod, "alt") == 0)
+	return WLR_MODIFIER_ALT;
+    if (strcasecmp(mod, "mod2") == 0)
+	return WLR_MODIFIER_MOD2;
+    if (strcasecmp(mod, "mod3") == 0)
+	return WLR_MODIFIER_MOD3;
+    if (strcasecmp(mod, "logo") == 0)
+	return WLR_MODIFIER_LOGO;
+    if (strcasecmp(mod, "mod5") == 0)
+	return WLR_MODIFIER_MOD5;
+    return 0;
+}
+
+
+void assign_action(char *name, char *data, struct binding *kb) {
+    kb->data = NULL;
+    kb->action = NULL;
+
+    if (strcasecmp(name, "shutdown") == 0)
+	kb->action = &shutdown;
+    else if (strcasecmp(name, "close_current_window") == 0)
+	kb->action = &close_current_window;
+    else if (strcasecmp(name, "next_desk") == 0)
+	kb->action = &next_desk;
+    else if (strcasecmp(name, "prev_desk") == 0)
+	kb->action = &prev_desk;
+    else if (strcasecmp(name, "pan_desk") == 0)
+	kb->action = &pan_desk;
+    else if (strcasecmp(name, "reset_pan") == 0)
+	kb->action = &reset_pan;
+    else if (strcasecmp(name, "save_pan") == 0)
+	kb->action = &save_pan;
+    else if (strcasecmp(name, "zoom_in") == 0) {
+	kb->action = &zoom_desk;
+	kb->data = calloc(1, sizeof(int));
+	*(int *)(kb->data) = 1;
+    }
+    else if (strcasecmp(name, "zoom_out") == 0) {
+	kb->action = &zoom_desk;
+	kb->data = calloc(1, sizeof(int));
+	*(int *)(kb->data) = -1;
+    }
+    else if (strcasecmp(name, "zoom_out") == 0) {
+	kb->action = &zoom_desk;
+	kb->data = calloc(1, sizeof(int));
+	*(int *)(kb->data) = -1;
+    }
+    else if (strcasecmp(name, "exec") == 0) {
+	kb->action = &exec_command;
+	kb->data = calloc(strlen(data), sizeof(char));
+	strncpy(kb->data, data, strlen(data));
+    }
+}
+
+
+enum mouse_keys mouse_key_from_name(char *name) {
+    if (strcasecmp(name, "motion") == 0)
+	return MOTION;
+    else if (strcasecmp(name, "scroll") == 0)
+	return SCROLL;
+    return 0;
+}
+
+
+void add_binding(struct server *server, char *data, int line) {
+    struct binding *kb = calloc(1, sizeof(struct binding));
+    enum wlr_keyboard_modifier mod;
+    char *s = strtok(data, " \t\n\r");
+    bool is_mouse_binding = false;
+
+    // additional modifiers
+    kb->mods = 0;
+    for (int i = 0; i < 8; i++) {
+	mod = modifier_by_name(s);
+	if (mod == 0)
+	    break;
+	kb->mods |= mod;
+	s = strtok(NULL, " \t\n\r");
+    }
+
+    // key
+    kb->key = xkb_keysym_from_name(s, XKB_KEYSYM_CASE_INSENSITIVE);
+    if (kb->key == XKB_KEY_NoSymbol) {
+	kb->key = mouse_key_from_name(s);
+	if (kb->key == 0) {
+	    wlr_log(WLR_ERROR, "Config line %i: No such key '%s'.", line, s);
+	    free(kb);
+	    return;
+	}
+	is_mouse_binding = true;
+    }
+
+    // action
+    s = strtok(NULL, " \t\n\r");
+    assign_action(s, strtok(NULL, ""), kb);
+    if (kb->action == NULL) {
+	wlr_log(WLR_ERROR, "Config line %i: No such action '%s'.", line, s);
+	free(kb);
+	return;
+    }
+
+    struct binding *kb_existing;
+    struct wl_list *bindings;
+    if (is_mouse_binding) {
+	bindings = &server->mouse_bindings;
+    } else {
+	bindings = &server->key_bindings;
+    }
+    wl_list_for_each(kb_existing, bindings, link) {
+	if (kb_existing->key == kb->key && kb_existing->mods == kb->mods) {
+	    wlr_log(WLR_ERROR, "Config line %i: binding exists for key combo.", line);
+	    free(kb);
+	    return;
+	}
+    }
+    wl_list_insert(bindings, &kb->link);
+}
+
 void assign_colour(char *hex, float dest[4]) {
     if (strlen(hex) != 7 || hex[0] != '#') {
 	wlr_log(WLR_INFO, "Invalid colour: %s. Should be in the form #rrggbb.", hex);

src/action.h Added

@@ -0,0 +1,16 @@
+#ifndef DESKWM_ACTION_H
+#define DESKWM_ACTION_H
+
+#include "types.h"
+
+void shutdown(struct server *server, void *data);
+void exec_command(struct server *server, void *data);
+void close_current_window(struct server *server, void *data);
+void next_desk(struct server *server, void *data);
+void prev_desk(struct server *server, void *data);
+void pan_desk(struct server *server, void *data);
+void reset_pan(struct server *server, void *data);
+void save_pan(struct server *server, void *data);
+void zoom_desk(struct server *server, void *data);
+
+#endif

src/action.c Added

@@ -0,0 +1,103 @@
+#include "desk.h"
+#include "types.h"
+
+
+void shutdown(struct server *server, void *data) {
+    wl_display_terminate(server->display);
+}
+
+
+void exec_command(struct server *server, void *data) {
+    if (fork() == 0) {
+	execl("/bin/sh", "/bin/sh", "-c", data, (void *)NULL);
+    }
+}
+
+
+void close_current_window(struct server *server, void *data) {
+    struct wlr_surface *surface = server->seat->keyboard_state.focused_surface;
+    if (surface) {
+	struct wlr_xdg_surface *xdg_surface = surface->role_data;
+	if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && xdg_surface->toplevel) {
+	    xdg_toplevel_send_close(xdg_surface->toplevel->resource);
+	}
+    }
+}
+
+
+void next_desk(struct server *server, void *data) {
+    struct desk *desk;
+    if (server->current_desk->index + 1 == server->desk_count) {
+	desk = wl_container_of(server->desks.next, desk, link);
+    } else {
+	desk = wl_container_of(server->current_desk->link.next, desk, link);
+    }
+    set_desk(server, desk);
+}
+
+
+void prev_desk(struct server *server, void *data) {
+    struct desk *desk;
+    if (server->current_desk->index == 0) {
+	desk = wl_container_of(server->desks.prev, desk, link);
+    } else {
+	desk = wl_container_of(server->current_desk->link.prev, desk, link);
+    }
+    set_desk(server, desk);
+}
+
+
+void pan_desk(struct server *server, void *data) {
+    struct desk *desk = server->current_desk;
+    struct motion motion = *(struct motion*)data;
+    double dx = motion.dx;
+    double dy = motion.dy;
+    struct view *view;
+    wl_list_for_each(view, &desk->views, link) {
+	view->x += dx;
+	view->y += dy;
+    }
+    desk->panned_x += dx;
+    desk->panned_y += dy;
+}
+
+
+void reset_pan(struct server *server, void *data) {
+    struct desk *desk = server->current_desk;
+    struct view *view;
+    wl_list_for_each(view, &desk->views, link) {
+	view->x -= desk->panned_x;
+	view->y -= desk->panned_y;
+    }
+    desk->panned_x = desk->panned_y = 0;
+    desk->zoom = 1;
+}
+
+
+void save_pan(struct server *server, void *data) {
+    server->current_desk->panned_x = server->current_desk->panned_y = 0;
+}
+
+
+void zoom_desk(struct server *server, void *data) {
+    /* dir > 0 ? zoom in : zoom out */
+    struct desk *desk = server->current_desk;
+    int dir = *(int*)data;
+    double f = dir > 0 ? 1.015 : 1/1.015;
+    if (
+	(f > 1 && desk->zoom >= server->zoom_max) ||
+	(f < 1 && desk->zoom <= server->zoom_min)
+    ) {
+	return;
+    }
+    desk->zoom *= f;
+    double fx = server->cursor->x * (f - 1) / desk->zoom;
+    double fy = server->cursor->y * (f - 1) / desk->zoom;
+    struct view *view;
+    wl_list_for_each(view, &desk->views, link) {
+	view->x -= fx;
+	view->y -= fy;
+    }
+    desk->panned_x -= fx;
+    desk->panned_y -= fy;
+}