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

Commit c15fc8a82f5e3d8ad28f1d46156473ff80cd2475
Parent: d4ae291eac1e78e2cd6dfa79a52ee0995957757e
Author: mcol <mcol@posteo.net>
Date: 2021-02-04 21:55:20 +0000
Committer: mcol <mcol@posteo.net>
Committed: 2021-02-04 21:55:20 +0000

Add scratchpad windows

wimp.conf Modified

@@ -85,6 +85,9 @@
 # The line after 'exec' is executed by /bin/sh
 bind		Return		exec gnome-terminal
 
+# And replace with 'exec' with 'scratchpad' and its window will be sticky
+bind	Ctrl	e		scratchpad footclient mutt
+
 # These two actions can be used to mark and go to set positions. Each one, once
 # pressed, waits for the next key press and uses that as the 'mark', e.g.
 # hitting 'mod + m' then 'a' will save the current position, then hitting 'mod

src/types.h Modified

@@ -106,8 +106,10 @@
     struct wlr_box grab_geobox;
     uint32_t resize_edges;
     double zoom_min, zoom_max;
-
     bool vt_switching;
+
+    struct wl_list scratchpads;
+    pid_t scratchpad_waiting;
 };
 
 extern struct wimp wimp;
@@ -122,6 +124,7 @@
     struct wl_listener request_resize_listener;
     struct wl_listener request_fullscreen_listener;
     double x, y;
+    bool is_scratchpad;
 };
 
 struct output {
@@ -208,4 +211,13 @@
     LEFT = 1 << 3,
 };
 
+struct scratchpad {
+    struct wl_list link;
+    char *command;
+    int id;
+    pid_t pid;
+    struct view *view;
+    bool is_mapped;
+};
+
 #endif

src/shell.c Modified

@@ -5,6 +5,19 @@
 #include "types.h"
 
 
+static struct scratchpad *scratchpad_from_view(struct view *view) {
+    struct scratchpad *scratchpad;
+
+    wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	if (view == scratchpad->view) {
+	    return scratchpad;
+	}
+    }
+
+    return NULL;
+}
+
+
 void unmap_view(struct view *view) {
     wl_signal_emit(&view->surface->events.unmap, view);
 }
@@ -31,10 +44,19 @@
 	wlr_xdg_toplevel_set_activated(previous, false);
     }
     struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
-    /* Move the view to the front */
-    wl_list_remove(&view->link);
-    wl_list_insert(&wimp.current_desk->views, &view->link);
-    /* Activate the new surface */
+
+    if (view->is_scratchpad) {
+	struct scratchpad *scratchpad;
+	wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	    if (scratchpad->view == view) {
+		scratchpad->is_mapped = true;
+	    }
+	}
+    } else {
+	wl_list_remove(&view->link);
+	wl_list_insert(&wimp.current_desk->views, &view->link);
+    }
+
     wlr_xdg_toplevel_set_activated(view->surface, true);
     wlr_seat_keyboard_notify_enter(
 	seat, view->surface->surface, keyboard->keycodes,
@@ -122,17 +144,33 @@
 
 static void on_map(struct wl_listener *listener, void *data) {
     struct view *view = wl_container_of(listener, view, map_listener);
-    if (wimp.can_steal_focus)
+
+    if (view->is_scratchpad) {
+	struct scratchpad *scratchpad = scratchpad_from_view(view);
+	scratchpad->is_mapped = true;
+    }
+
+    if (wimp.can_steal_focus) {
 	focus_view(view, view->surface->surface);
+    }
 }
 
 static void on_unmap(struct wl_listener *listener, void *data) {
     struct view *view = wl_container_of(listener, view, unmap_listener);
-    if (wimp.can_steal_focus) {
+
+    if (view->is_scratchpad) {
+	struct scratchpad *scratchpad = scratchpad_from_view(view);
+	scratchpad->is_mapped = false;
+    } else {
 	wl_list_remove(&view->link);
 	wl_list_insert(wimp.current_desk->views.prev, &view->link);
-	struct view *next_view = wl_container_of(wimp.current_desk->views.next, view, link);
-	focus_view(next_view, next_view->surface->surface);
+    }
+
+    if (wimp.can_steal_focus) {
+	if (!wl_list_empty(&wimp.current_desk->views)) {
+	    struct view *next_view = wl_container_of(wimp.current_desk->views.next, view, link);
+	    focus_view(next_view, next_view->surface->surface);
+	}
     }
 }
 
@@ -142,7 +180,15 @@
     if (wimp.current_desk->fullscreened == view->surface) {
 	wimp.current_desk->fullscreened = NULL;
     }
-    wl_list_remove(&view->link);
+
+    if (view->is_scratchpad) {
+	struct scratchpad *scratchpad = scratchpad_from_view(view);
+	scratchpad->is_mapped = false;
+	scratchpad->view = NULL;
+    } else {
+	wl_list_remove(&view->link);
+    }
+
     free(view);
 }
 
@@ -215,6 +261,23 @@
     view->request_fullscreen_listener.notify = on_request_fullscreen;
     wl_signal_add(&toplevel->events.request_fullscreen, &view->request_fullscreen_listener);
 
+    if (wimp.scratchpad_waiting) {
+	pid_t pid;
+	wl_client_get_credentials(surface->client->client, &pid, NULL, NULL);
+	struct scratchpad *scratchpad;
+	wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	    if (scratchpad->pid == pid) {
+		scratchpad->view = view;
+		view->is_scratchpad = true;
+		view->x = 50;
+		view->y = 50;
+		map_view(view);
+		return;
+	    }
+	}
+    }
+
+    view->is_scratchpad = false;
     view->x = wimp.current_desk->border_width;
     view->y = wimp.current_desk->border_width;
     wl_list_insert(&wimp.current_desk->views, &view->link);

src/output.c Modified

@@ -140,7 +140,7 @@
 
     // paint clients
     struct view *view;
-    struct view *focussed = wl_container_of(wimp.current_desk->views.next, focussed, link);
+    struct wlr_surface *focussed = wimp.seat->keyboard_state.focused_surface;
     int border_width = wimp.current_desk->border_width;
     wl_list_for_each_reverse(view, &desk->views, link) {
 	rdata.x = view->x - ox / zoom;
@@ -154,14 +154,27 @@
 	}
 	rdata.x = view->x + ox;
 	rdata.y = view->y + oy;
-	rdata.is_focussed = (view == focussed);
+	rdata.is_focussed = (view->surface->surface == focussed);
 	rdata.bordered = view->surface->surface;
 	wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata);
     }
 
+    rdata.zoom = 1;
+
+    struct scratchpad *scratchpad;
+    wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	if (scratchpad->is_mapped) {
+	    view = scratchpad->view;
+	    rdata.x = view->x;
+	    rdata.y = view->y;
+	    rdata.is_focussed = (view->surface->surface == focussed);
+	    rdata.bordered = view->surface->surface;
+	    wlr_xdg_surface_for_each_surface(view->surface, render_surface, &rdata);
+	}
+    }
+
     rdata.bordered = NULL;
     rdata.is_focussed = false;
-    rdata.zoom = 1;
 
     // paint top and overlay layers
     wl_list_for_each(lview, &output->layer_views[ZWLR_LAYER_SHELL_V1_LAYER_TOP], link) {

src/main.c Modified

@@ -37,6 +37,7 @@
     .mark_indicator.box.height = 25,
     .mark_indicator.box.x = 0,
     .mark_indicator.box.y = 0,
+    .scratchpad_waiting = false,
 };
 
 
@@ -116,6 +117,14 @@
 	free(output);
     };
 
+    struct scratchpad *scratchpad, *tscratchpad;
+    wl_list_for_each_safe(scratchpad, tscratchpad, &wimp.scratchpads, link) {
+	wl_list_remove(&scratchpad->link);
+	free(scratchpad->view);
+	free(scratchpad->command);
+	free(scratchpad);
+    };
+
     struct keyboard *keyboard, *tkeyboard;
     wl_list_for_each_safe(keyboard, tkeyboard, &wimp.keyboards, link) {
 	wl_list_remove(&keyboard->link);
@@ -182,6 +191,7 @@
     wl_list_init(&wimp.key_bindings);
     wl_list_init(&wimp.mouse_bindings);
     wl_list_init(&wimp.marks);
+    wl_list_init(&wimp.scratchpads);
 
     // configure
     locate_config();

src/cursor.c Modified

@@ -75,6 +75,24 @@
 	}
     }
 
+    // scratchpads
+    struct scratchpad *scratchpad;
+    wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	if (scratchpad->is_mapped) {
+	    view = scratchpad->view;
+	    tsurface = wlr_xdg_surface_surface_at(
+		view->surface, x - view->x, y - view->y, &tsx, &tsy
+	    );
+	    if (tsurface) {
+		*sx = tsx;
+		*sy = tsy;
+		*surface = tsurface;
+		*is_layer = false;
+		return view;
+	    }
+	}
+    }
+
     // clients
     double zx = x / wimp.current_desk->zoom;
     double zy = y / wimp.current_desk->zoom;

src/config.c Modified

@@ -138,6 +138,26 @@
 }
 
 
+static int scratchpad_id = 0;
+
+
+static void scratchpad_handler(struct binding *kb, char *data) {
+    if (!data) {
+	return;
+    }
+    struct scratchpad *scratchpad = calloc(1, sizeof(struct scratchpad));
+    wl_list_insert(wimp.scratchpads.prev, &scratchpad->link);
+    scratchpad->command = strdup(data);
+    scratchpad->id = scratchpad_id;
+    scratchpad->view = NULL;
+    scratchpad->is_mapped = false;
+
+    kb->data = calloc(1, sizeof(int));
+    *(int *)(kb->data) = scratchpad_id;
+    scratchpad_id++;
+}
+
+
 static struct {
     const char *name;
     const action action;
@@ -160,6 +180,7 @@
     { "maximize", &maximize, NULL },
     { "reload_config", &reload_config, NULL },
     { "send_to_desk", &send_to_desk, &str_handler },
+    { "scratchpad", &scratchpad, &scratchpad_handler },
 };
 
 

src/action.h Modified

@@ -26,5 +26,6 @@
 void maximize(void *data);
 void reload_config(void *data);
 void send_to_desk(void *data);
+void scratchpad(void *data);
 
 #endif

src/action.c Modified

@@ -23,7 +23,7 @@
 	    exit(EXIT_FAILURE);
 	}
     } else if (pid < 0) {
-	wlr_log(WLR_ERROR, "Failed to execute: %s", data);
+	wlr_log(WLR_ERROR, "Failed to fork for exec command.");
     } else {
 	wlr_log(WLR_DEBUG, "Executing: %s", data);
     }
@@ -400,3 +400,37 @@
     unfullscreen();
     view_to_desk(view, index);
 }
+
+
+void scratchpad(void *data) {
+    int id = *(int *)data;
+    struct scratchpad *scratchpad;
+
+    wl_list_for_each(scratchpad, &wimp.scratchpads, link) {
+	if (scratchpad->id == id) {
+	    break;
+	}
+    }
+
+    if (scratchpad->view) {
+	if (scratchpad->is_mapped) {
+	    unmap_view(scratchpad->view);
+	} else {
+	    map_view(scratchpad->view);
+	}
+	return;
+    }
+
+    wimp.scratchpad_waiting = true;
+
+    scratchpad->pid = fork();
+    if (scratchpad->pid == 0) {
+	execl("/bin/sh", "/bin/sh", "-c", scratchpad->command, (void *)NULL);
+	exit(EXIT_FAILURE);
+    } else if (scratchpad->pid < 0) {
+	wlr_log(WLR_ERROR, "Failed to fork for scratchpad.");
+	wimp.scratchpad_waiting = false;
+    } else {
+	wlr_log(WLR_DEBUG, "Launching scratchpad: %s", scratchpad->command);
+    }
+}