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

Commit ac5054212d609bc045aa87a5f498cbb4421acca7
Parent: 0894e052ab127adb3c3a9a9aa6b415a22aace330
Author: mcol <mcol@posteo.net>
Date: 2021-01-25 23:39:01 +0000
Committer: mcol <mcol@posteo.net>
Committed: 2021-01-25 23:39:01 +0000

implement first steps in layer shell protocol

src/types.h Modified

@@ -54,6 +54,9 @@
     struct wl_listener decoration_listener;
     struct wlr_server_decoration_manager *server_decoration_manager;
 
+    struct wlr_layer_shell_v1 *layer_shell;
+    struct wl_listener layer_shell_surface_listener;
+
     struct wlr_cursor *cursor;
     struct wlr_xcursor_manager *cursor_manager;
     struct wl_listener cursor_motion_listener;
@@ -99,10 +102,20 @@
 
 struct output {
     struct wl_list link;
+    struct wl_list layer_views;
     struct wlr_output *wlr_output;
     struct wl_listener frame_listener;
 };
 
+struct layer_view {
+    struct wl_list link;
+    struct wlr_layer_surface_v1 *surface;
+    struct wl_listener map_listener;
+    struct wl_listener unmap_listener;
+    struct wl_listener destroy_listener;
+    struct output *output;
+};
+
 struct keyboard {
     struct wl_list link;
     struct wlr_input_device *device;

src/output.c Modified

@@ -1,4 +1,5 @@
 #include <time.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
 #include <wlr/types/wlr_matrix.h>
 #include <wlr/types/wlr_output_layout.h>
 
@@ -9,10 +10,12 @@
 struct render_data {
     struct wlr_output *output;
     struct wlr_renderer *renderer;
-    struct view *view;
+    struct wlr_surface *bordered;
     struct timespec *when;
     double zoom;
     bool is_focussed;
+    int x;
+    int y;
 };
 
 
@@ -20,7 +23,6 @@
     struct wlr_surface *surface, int sx, int sy, void *data
 ) {
     struct render_data *rdata = data;
-    struct view *view = rdata->view;
     struct wlr_output *output = rdata->output;
 
     struct wlr_texture *texture = wlr_surface_get_texture(surface);
@@ -37,13 +39,13 @@
 	    break;
 	}
     }
-    x = (x + view->x + sx) * output->scale * rdata->zoom;
-    y = (y + view->y + sy) * output->scale * rdata->zoom;
+    x = (x + rdata->x + sx) * output->scale * rdata->zoom;
+    y = (y + rdata->y + sy) * output->scale * rdata->zoom;
     int width = surface->current.width * output->scale * rdata->zoom;
     int height = surface->current.height * output->scale * rdata->zoom;
-    int border_width = wimp.current_desk->border_width;
 
-    if (view->surface->surface == surface) {
+    if (rdata->bordered == surface) {
+	int border_width = wimp.current_desk->border_width;
 	struct wlr_box borders = {
 	    .x = x - border_width,
 	    .y = y - border_width,
@@ -93,6 +95,17 @@
     wlr_renderer_begin(renderer, width, height);
     wlr_renderer_clear(renderer, desk->background);
 
+    struct render_data rdata = {
+	.output = output->wlr_output,
+	.renderer = renderer,
+	.bordered = NULL,
+	.when = &now,
+	.zoom = zoom,
+	.is_focussed = false,
+	.x = 0,
+	.y = 0,
+    };
+
     // paint wallpaper
     struct wallpaper *wallpaper = desk->wallpaper;
     if (wallpaper != NULL) {
@@ -108,21 +121,53 @@
 	}
     }
 
+    // paint background and bottom layers
+    struct layer_view *lview;
+    wl_list_for_each(lview, &output->layer_views, link) {
+	if (lview->surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) {
+	    wlr_surface_for_each_surface(
+		lview->surface->surface, render_surface, &rdata
+	    );
+	}
+    }
+    wl_list_for_each(lview, &output->layer_views, link) {
+	if (lview->surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) {
+	    wlr_surface_for_each_surface(
+		lview->surface->surface, render_surface, &rdata
+	    );
+	}
+    }
+
     // paint clients
     struct view *view;
     struct view *focussed = wl_container_of(wimp.current_desk->views.next, focussed, link);
     wl_list_for_each_reverse(view, &desk->views, link) {
-	struct render_data rdata = {
-	    .output = output->wlr_output,
-	    .view = view,
-	    .renderer = renderer,
-	    .when = &now,
-	    .zoom = zoom,
-	    .is_focussed = (view == focussed),
-	};
-	wlr_xdg_surface_for_each_surface(
-	    view->surface, render_surface, &rdata
-	);
+	rdata.x = view->x;
+	rdata.y = view->y;
+	rdata.is_focussed = (view == 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.x = 0;
+    rdata.y = 0;
+
+    // paint top and overlay layers
+    wl_list_for_each(lview, &output->layer_views, link) {
+	if (lview->surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+	    wlr_surface_for_each_surface(
+		lview->surface->surface, render_surface, &rdata
+	    );
+	}
+    }
+    wl_list_for_each(lview, &output->layer_views, link) {
+	if (lview->surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) {
+	    wlr_surface_for_each_surface(
+		lview->surface->surface, render_surface, &rdata
+	    );
+	}
     }
 
     // paint mark indicator
@@ -155,10 +200,14 @@
 
     struct output *output = calloc(1, sizeof(struct output));
     output->wlr_output = wlr_output;
+    wlr_output->data = output;
+
     output->frame_listener.notify = on_frame;
     wl_signal_add(&wlr_output->events.frame, &output->frame_listener);
     wl_list_insert(&wimp.outputs, &output->link);
     wlr_output_layout_add_auto(wimp.output_layout, wlr_output);
+
+    wl_list_init(&output->layer_views);
 }
 
 

src/main.c Modified

@@ -16,6 +16,7 @@
 #include "desk.h"
 #include "main.h"
 #include "keyboard.h"
+#include "layer_shell.h"
 #include "log.h"
 #include "output.h"
 #include "shell.h"
@@ -74,7 +75,12 @@
     };
 
     struct output *output, *toutput;
+    struct layer_view *lview, *tlview;
     wl_list_for_each_safe(output, toutput, &wimp.outputs, link) {
+	wl_list_for_each_safe(lview, tlview, &output->layer_views, link) {
+	    wl_list_remove(&lview->link);
+	    free(lview);
+	};
 	wl_list_remove(&output->link);
 	free(output);
     };
@@ -147,6 +153,7 @@
     set_up_cursor();
     set_up_keyboard();
     set_up_decorations();
+    set_up_layer_shell();
 
     // start
     const char *socket = wl_display_add_socket_auto(wimp.display);

src/layer_shell.h Added

@@ -0,0 +1,8 @@
+#ifndef DESKWM_LAYER_SHELL_H
+#define DESKWM_LAYER_SHELL_H
+
+#include "types.h"
+
+void set_up_layer_shell();
+
+#endif

src/layer_shell.c Added

@@ -0,0 +1,82 @@
+#include <wlr/types/wlr_layer_shell_v1.h>
+
+#include "layer_shell.h"
+
+
+static void layer(struct output *output) {
+    struct layer_view *lview;
+    struct wlr_layer_surface_v1 *surface;
+    struct wlr_layer_surface_v1_state *state;
+
+    if (wl_list_empty(&output->layer_views)) {
+	return;
+    }
+
+    wl_list_for_each(lview, &output->layer_views, link) {
+	surface = lview->surface;
+	state = &surface->current;
+	wlr_layer_surface_v1_configure(surface, state->desired_width, state->desired_height);
+    }
+}
+
+
+static void on_destroy(struct wl_listener *listener, void *data) {
+    struct layer_view *lview = wl_container_of(listener, lview, destroy_listener);
+    struct output *output = lview->output;
+
+    wl_list_remove(&lview->destroy_listener.link);
+    wl_list_remove(&lview->map_listener.link);
+    wl_list_remove(&lview->unmap_listener.link);
+    wl_list_remove(&lview->link);
+    free(lview);
+    layer(output);
+}
+
+
+static void on_unmap(struct wl_listener *listener, void *data) {
+    struct layer_view *lview = wl_container_of(listener, lview, unmap_listener);
+    lview->surface->mapped = false;
+    layer(lview->output);
+    wlr_surface_send_enter(lview->surface->surface, lview->surface->output);
+}
+
+
+static void on_map(struct wl_listener *listener, void *data) {
+    struct layer_view *lview = wl_container_of(listener, lview, map_listener);
+    lview->surface->mapped = true;
+    layer(lview->output);
+}
+
+
+static void on_new_surface(struct wl_listener *listener, void *data) {
+    struct wlr_layer_surface_v1 *surface = data;
+
+    struct layer_view *lview = calloc(1, sizeof(struct view));
+    lview->surface = surface;
+    surface->data = lview;
+
+    lview->map_listener.notify = on_map;
+    lview->unmap_listener.notify = on_unmap;
+    lview->destroy_listener.notify = on_destroy;
+    wl_signal_add(&surface->events.map, &lview->map_listener);
+    wl_signal_add(&surface->events.unmap, &lview->unmap_listener);
+    wl_signal_add(&surface->events.destroy, &lview->destroy_listener);
+    
+    if (!surface->output) {
+	surface->output = wlr_output_layout_output_at(
+	    wimp.output_layout, wimp.cursor->x, wimp.cursor->y
+	);
+    }
+    struct output *output = surface->output->data;
+    lview->output = output;
+    wl_list_insert(&output->layer_views, &lview->link);
+
+    layer(lview->output);
+}
+
+
+void set_up_layer_shell() {
+    wimp.layer_shell = wlr_layer_shell_v1_create(wimp.display);
+    wimp.layer_shell_surface_listener.notify = on_new_surface;
+    wl_signal_add(&wimp.layer_shell->events.new_surface, &wimp.layer_shell_surface_listener);
+}

protocols/wlr-layer-shell-unstable-v1.xml Added

@@ -0,0 +1,311 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+  <copyright>
+    Copyright © 2017 Drew DeVault
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <interface name="zwlr_layer_shell_v1" version="3">
+    <description summary="create surfaces that are layers of the desktop">
+      Clients can use this interface to assign the surface_layer role to
+      wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+      rendered with a defined z-depth respective to each other. They may also be
+      anchored to the edges and corners of a screen and specify input handling
+      semantics. This interface should be suitable for the implementation of
+      many desktop shell components, and a broad number of other applications
+      that interact with the desktop.
+    </description>
+
+    <request name="get_layer_surface">
+      <description summary="create a layer_surface from a surface">
+        Create a layer surface for an existing surface. This assigns the role of
+        layer_surface, or raises a protocol error if another role is already
+        assigned.
+
+        Creating a layer surface from a wl_surface which has a buffer attached
+        or committed is a client error, and any attempts by a client to attach
+        or manipulate a buffer prior to the first layer_surface.configure call
+        must also be treated as errors.
+
+        You may pass NULL for output to allow the compositor to decide which
+        output to use. Generally this will be the one that the user most
+        recently interacted with.
+
+        Clients can specify a namespace that defines the purpose of the layer
+        surface.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+      <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+      <arg name="namespace" type="string" summary="namespace for the layer surface"/>
+    </request>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="wl_surface has another role"/>
+      <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+      <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+    </enum>
+
+    <enum name="layer">
+      <description summary="available layers for surfaces">
+        These values indicate which layers a surface can be rendered in. They
+        are ordered by z depth, bottom-most first. Traditional shell surfaces
+        will typically be rendered between the bottom and top layers.
+        Fullscreen shell surfaces are typically rendered at the top layer.
+        Multiple surfaces can share a single layer, and ordering within a
+        single layer is undefined.
+      </description>
+
+      <entry name="background" value="0"/>
+      <entry name="bottom" value="1"/>
+      <entry name="top" value="2"/>
+      <entry name="overlay" value="3"/>
+    </enum>
+
+    <!-- Version 3 additions -->
+
+    <request name="destroy" type="destructor" since="3">
+      <description summary="destroy the layer_shell object">
+        This request indicates that the client will not use the layer_shell
+        object any more. Objects that have been created through this instance
+        are not affected.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="zwlr_layer_surface_v1" version="3">
+    <description summary="layer metadata interface">
+      An interface that may be implemented by a wl_surface, for surfaces that
+      are designed to be rendered as a layer of a stacked desktop-like
+      environment.
+
+      Layer surface state (layer, size, anchor, exclusive zone,
+      margin, interactivity) is double-buffered, and will be applied at the
+      time wl_surface.commit of the corresponding wl_surface is called.
+    </description>
+
+    <request name="set_size">
+      <description summary="sets the size of the surface">
+        Sets the size of the surface in surface-local coordinates. The
+        compositor will display the surface centered with respect to its
+        anchors.
+
+        If you pass 0 for either value, the compositor will assign it and
+        inform you of the assignment in the configure event. You must set your
+        anchor to opposite edges in the dimensions you omit; not doing so is a
+        protocol error. Both values are 0 by default.
+
+        Size is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="width" type="uint"/>
+      <arg name="height" type="uint"/>
+    </request>
+
+    <request name="set_anchor">
+      <description summary="configures the anchor point of the surface">
+        Requests that the compositor anchor the surface to the specified edges
+        and corners. If two orthogonal edges are specified (e.g. 'top' and
+        'left'), then the anchor point will be the intersection of the edges
+        (e.g. the top left corner of the output); otherwise the anchor point
+        will be centered on that edge, or in the center if none is specified.
+
+        Anchor is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="anchor" type="uint" enum="anchor"/>
+    </request>
+
+    <request name="set_exclusive_zone">
+      <description summary="configures the exclusive geometry of this surface">
+        Requests that the compositor avoids occluding an area with other
+        surfaces. The compositor's use of this information is
+        implementation-dependent - do not assume that this region will not
+        actually be occluded.
+
+        A positive value is only meaningful if the surface is anchored to one
+        edge or an edge and both perpendicular edges. If the surface is not
+        anchored, anchored to only two perpendicular edges (a corner), anchored
+        to only two parallel edges or anchored to all edges, a positive value
+        will be treated the same as zero.
+
+        A positive zone is the distance from the edge in surface-local
+        coordinates to consider exclusive.
+
+        Surfaces that do not wish to have an exclusive zone may instead specify
+        how they should interact with surfaces that do. If set to zero, the
+        surface indicates that it would like to be moved to avoid occluding
+        surfaces with a positive exclusive zone. If set to -1, the surface
+        indicates that it would not like to be moved to accommodate for other
+        surfaces, and the compositor should extend it all the way to the edges
+        it is anchored to.
+
+        For example, a panel might set its exclusive zone to 10, so that
+        maximized shell surfaces are not shown on top of it. A notification
+        might set its exclusive zone to 0, so that it is moved to avoid
+        occluding the panel, but shell surfaces are shown underneath it. A
+        wallpaper or lock screen might set their exclusive zone to -1, so that
+        they stretch below or over the panel.
+
+        The default value is 0.
+
+        Exclusive zone is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="zone" type="int"/>
+    </request>
+
+    <request name="set_margin">
+      <description summary="sets a margin from the anchor point">
+        Requests that the surface be placed some distance away from the anchor
+        point on the output, in surface-local coordinates. Setting this value
+        for edges you are not anchored to has no effect.
+
+        The exclusive zone includes the margin.
+
+        Margin is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="top" type="int"/>
+      <arg name="right" type="int"/>
+      <arg name="bottom" type="int"/>
+      <arg name="left" type="int"/>
+    </request>
+
+    <request name="set_keyboard_interactivity">
+      <description summary="requests keyboard events">
+        Set to 1 to request that the seat send keyboard events to this layer
+        surface. For layers below the shell surface layer, the seat will use
+        normal focus semantics. For layers above the shell surface layers, the
+        seat will always give exclusive keyboard focus to the top-most layer
+        which has keyboard interactivity set to true.
+
+        Layer surfaces receive pointer, touch, and tablet events normally. If
+        you do not want to receive them, set the input region on your surface
+        to an empty region.
+
+        Events is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="keyboard_interactivity" type="uint"/>
+    </request>
+
+    <request name="get_popup">
+      <description summary="assign this layer_surface as an xdg_popup parent">
+        This assigns an xdg_popup's parent to this layer_surface.  This popup
+        should have been created via xdg_surface::get_popup with the parent set
+        to NULL, and this request must be invoked before committing the popup's
+        initial state.
+
+        See the documentation of xdg_popup for more details about what an
+        xdg_popup is and how it is used.
+      </description>
+      <arg name="popup" type="object" interface="xdg_popup"/>
+    </request>
+
+    <request name="ack_configure">
+      <description summary="ack a configure event">
+        When a configure event is received, if a client commits the
+        surface in response to the configure event, then the client
+        must make an ack_configure request sometime before the commit
+        request, passing along the serial of the configure event.
+
+        If the client receives multiple configure events before it
+        can respond to one, it only has to ack the last configure event.
+
+        A client is not required to commit immediately after sending
+        an ack_configure request - it may even ack_configure several times
+        before its next surface commit.
+
+        A client may send multiple ack_configure requests before committing, but
+        only the last request sent before a commit indicates which configure
+        event the client really is responding to.
+      </description>
+      <arg name="serial" type="uint" summary="the serial from the configure event"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the layer_surface">
+        This request destroys the layer surface.
+      </description>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+        The configure event asks the client to resize its surface.
+
+        Clients should arrange their surface for the new states, and then send
+        an ack_configure request with the serial sent in this configure event at
+        some point before committing the new surface.
+
+        The client is free to dismiss all but the last configure event it
+        received.
+
+        The width and height arguments specify the size of the window in
+        surface-local coordinates.
+
+        The size is a hint, in the sense that the client is free to ignore it if
+        it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+        resize in steps of NxM pixels). If the client picks a smaller size and
+        is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+        surface will be centered on this axis.
+
+        If the width or height arguments are zero, it means the client should
+        decide its own window dimension.
+      </description>
+      <arg name="serial" type="uint"/>
+      <arg name="width" type="uint"/>
+      <arg name="height" type="uint"/>
+    </event>
+
+    <event name="closed">
+      <description summary="surface should be closed">
+        The closed event is sent by the compositor when the surface will no
+        longer be shown. The output may have been destroyed or the user may
+        have asked for it to be removed. Further changes to the surface will be
+        ignored. The client should destroy the resource after receiving this
+        event, and create a new surface if they so choose.
+      </description>
+    </event>
+
+    <enum name="error">
+      <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+      <entry name="invalid_size" value="1" summary="size is invalid"/>
+      <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+    </enum>
+
+    <enum name="anchor" bitfield="true">
+      <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+      <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+      <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+      <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+    </enum>
+
+    <!-- Version 2 additions -->
+
+    <request name="set_layer" since="2">
+      <description summary="change the layer of the surface">
+        Change the layer that the surface is rendered on.
+
+        Layer is double-buffered, see wl_surface.commit.
+      </description>
+      <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+    </request>
+  </interface>
+</protocol>

Makefile Modified

@@ -10,7 +10,7 @@
 PREFIX    ?= /usr/local
 BINPREFIX ?= $(PREFIX)/bin
 
-${OUT}: xdg-shell-protocol.h xdg-shell-protocol.c ${OBJECTS}
+${OUT}: xdg-shell-protocol wlr-layer-shell-unstable-v1-protocol ${OBJECTS}
 	@$(CC) -o ${OUT} $(OBJECTS) $(LDFLAGS)
 
 %.o: %.c %.h
@@ -19,11 +19,13 @@
 WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols)
 WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner)
 
-xdg-shell-protocol.h:
-	@$(WAYLAND_SCANNER) server-header $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+xdg-shell-protocol:
+	@$(WAYLAND_SCANNER) server-header $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@.h
+	@$(WAYLAND_SCANNER) private-code $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@.c
 
-xdg-shell-protocol.c: xdg-shell-protocol.h
-	@$(WAYLAND_SCANNER) private-code $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+wlr-layer-shell-unstable-v1-protocol:
+	@$(WAYLAND_SCANNER) server-header protocols/wlr-layer-shell-unstable-v1.xml $@.h
+	@$(WAYLAND_SCANNER) private-code protocols/wlr-layer-shell-unstable-v1.xml $@.c
 
 clean:
 	rm -f ${OUT} xdg-shell-protocol.h xdg-shell-protocol.c ${OBJECTS}