diff --git a/.boring b/.boring index b7d979a..2fbc4ee 100644 --- a/.boring +++ b/.boring @@ -126,3 +126,4 @@ hikari\.1 version\.h ^hikari.*\.tar\.gz$ +^wlr-layer-shell-unstable-v1-protocol.h$ diff --git a/Makefile b/Makefile index 7d143f6..14a21b3 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ OBJS = \ input_buffer.o \ input_grab_mode.o \ keyboard.o \ + layer_shell.o \ layout.o \ layout_config.o \ layout_select_mode.o \ @@ -95,6 +96,10 @@ CFLAGS += -DHAVE_GAMMACONTROL=1 CFLAGS += -DHAVE_SCREENCOPY=1 .endif +.ifdef WITH_LAYERSHELL +CFLAGS += -DHAVE_LAYERSHELL=1 +.endif + CFLAGS += -Wall -I. -Iinclude WLROOTS_CFLAGS != pkg-config --cflags wlroots @@ -153,12 +158,15 @@ all: hikari hikari-unlocker version.h: echo "#define HIKARI_VERSION \"${VERSION}\"" >> version.h -hikari: version.h xdg-shell-protocol.h ${OBJS} +hikari: version.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h ${OBJS} ${CC} ${LDFLAGS} ${CFLAGS} ${INCLUDES} ${LIBS} ${OBJS} -o ${.TARGET} xdg-shell-protocol.h: wayland-scanner server-header ${WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml ${.TARGET} +wlr-layer-shell-unstable-v1-protocol.h: + wayland-scanner server-header protocol/wlr-layer-shell-unstable-v1.xml ${.TARGET} + hikari-unlocker: hikari_unlocker.c ${CC} -lpam hikari_unlocker.c -o hikari-unlocker @@ -170,6 +178,7 @@ clean: clean-doc @echo "cleaning headers" @test -e _darcs && rm version.h ||: @rm xdg-shell-protocol.h 2> /dev/null ||: + @rm wlr-layer-shell-unstable-v1-protocol.h 2> /dev/null ||: @echo "cleaning object files" @rm ${OBJS} 2> /dev/null ||: @echo "cleaning executables" @@ -190,6 +199,7 @@ hikari-${VERSION}.tar.gz: version.h share/man/man1/hikari.1 hikari_unlocker.c \ include/hikari/*.h \ src/*.c \ + protocol/*.xml \ Makefile \ LICENSE \ README.md \ diff --git a/README.md b/README.md index f18976b..dd99189 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,16 @@ and can be enabled via setting `WITH_GAMMACONTROL`. make WITH_GAMMACONTROL=YES ``` +#### Building with layer-shell support + +Some applications that are used to build desktop components require +`layer-shell`. Examples for this are `waybar`, `wofi` and `slurp`. To turn on +`layer-shell` support compile with the `WITH_LAYERSHELL` option. + +``` +make WITH_LAYERSHELL=YES +``` + #### Building the manpage Building the `hikari` manpage requires [`pandoc`](http://pandoc.org/). To build diff --git a/include/hikari/geometry.h b/include/hikari/geometry.h index d90dd80..d6a9153 100644 --- a/include/hikari/geometry.h +++ b/include/hikari/geometry.h @@ -21,10 +21,11 @@ void hikari_geometry_shrink(struct wlr_box *geometry, int gap); void -hikari_geometry_constrain_position(struct wlr_box *geometry, - int screen_width, - int screen_height, - int x, - int y); +hikari_geometry_constrain_absolute( + struct wlr_box *geometry, struct wlr_box *usable_area, int x, int y); + +void +hikari_geometry_constrain_relative( + struct wlr_box *geometry, struct wlr_box *usable_area, int x, int y); #endif diff --git a/include/hikari/layer_shell.h b/include/hikari/layer_shell.h new file mode 100644 index 0000000..980eb2e --- /dev/null +++ b/include/hikari/layer_shell.h @@ -0,0 +1,64 @@ +#if !defined(HIKARI_LAYER_SHELL_H) +#define HIKARI_LAYER_SHELL_H + +#include + +#include + +struct hikari_output; +struct hikari_layer; +struct hikari_layer_popup; + +enum hikari_layer_node_type { + HIKARI_LAYER_NODE_TYPE_LAYER, + HIKARI_LAYER_NODE_TYPE_POPUP +}; + +struct hikari_layer_node { + enum hikari_layer_node_type type; + + union { + struct hikari_layer *layer; + struct hikari_layer_popup *popup; + } node; +}; + +struct hikari_layer { + struct hikari_view_interface view_interface; + + struct wl_list layer_surfaces; + + struct wlr_layer_surface_v1 *surface; + + struct wl_listener commit; + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener new_popup; + + struct wlr_box geometry; + + struct hikari_output *output; + enum zwlr_layer_shell_v1_layer layer; +}; + +struct hikari_layer_popup { + struct hikari_layer_node parent; + + struct wlr_xdg_popup *popup; + + struct wl_listener commit; + struct wl_listener destroy; + struct wl_listener map; + struct wl_listener unmap; + struct wl_listener new_popup; +}; + +void +hikari_layer_init(struct hikari_layer *layer_surface, + struct wlr_layer_surface_v1 *wlr_layer_surface); + +void +hikari_layer_fini(struct hikari_layer *layer_surface); + +#endif diff --git a/include/hikari/output.h b/include/hikari/output.h index 13241ef..e254c78 100644 --- a/include/hikari/output.h +++ b/include/hikari/output.h @@ -23,6 +23,10 @@ struct hikari_output { struct wl_listener damage_destroy; /* struct wl_listener mode; */ +#ifdef HAVE_LAYERSHELL + struct wl_list layers[4]; +#endif + struct wl_list views; #ifdef HAVE_XWAYLAND struct wl_list unmanaged_xwayland_views; @@ -30,6 +34,7 @@ struct hikari_output { struct wl_list server_outputs; struct wlr_box geometry; + struct wlr_box usable_area; struct wlr_texture *background; }; diff --git a/include/hikari/server.h b/include/hikari/server.h index bde00cd..26b4885 100644 --- a/include/hikari/server.h +++ b/include/hikari/server.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,7 @@ struct hikari_server { struct wl_listener request_set_selection; struct wl_listener output_layout_change; struct wl_listener new_decoration; + struct wl_listener new_layer_shell_surface; #ifdef HAVE_XWAYLAND struct wl_listener new_xwayland_surface; @@ -64,6 +66,8 @@ struct hikari_server { struct wlr_server_decoration_manager *decoration_manager; struct wlr_xdg_shell *xdg_shell; + struct wlr_layer_shell_v1 *layer_shell; + struct wlr_output_layout *output_layout; struct wlr_seat *seat; diff --git a/protocol/wlr-layer-shell-unstable-v1.xml b/protocol/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..adc6a17 --- /dev/null +++ b/protocol/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,301 @@ + + + + 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. + + + + + 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. + + + + + 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. + + + + + + + + + + + + + + + + + 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. + + + + + + + + + + + + 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. + + + + + 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. + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + + + + 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. + + + + + + + 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. + + + + + + + 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. + + + + + + + This request destroys the layer surface. + + + + + + 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. + + + + + + + + + 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. + + + + + + + + + + + + + + + + + + + + + Change the layer that the surface is rendered on. + + Layer is double-buffered, see wl_surface.commit. + + + + + diff --git a/src/geometry.c b/src/geometry.c index b63ec1b..2e24f69 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -37,19 +37,57 @@ hikari_geometry_shrink(struct wlr_box *geometry, int gap) } void -hikari_geometry_constrain_position( - struct wlr_box *geometry, int screen_width, int screen_height, int x, int y) +hikari_geometry_constrain_absolute( + struct wlr_box *geometry, struct wlr_box *usable_area, int x, int y) { int border = hikari_configuration->border; - if (x + geometry->width + border > screen_width) { - geometry->x = screen_width - geometry->width - border; + int usable_max_x = usable_area->x + usable_area->width; + int usable_min_x = usable_area->x; + int usable_max_y = usable_area->y + usable_area->height; + int usable_min_y = usable_area->y; + + if (x + geometry->width + border > usable_max_x) { + geometry->x = usable_max_x - geometry->width - border; + } else if (x - border < usable_min_x) { + geometry->x = usable_min_x + border; } else { geometry->x = x; } - if (y + geometry->height + border > screen_height) { - geometry->y = screen_height - geometry->height - border; + if (y + geometry->height + border > usable_max_y) { + geometry->y = usable_max_y - geometry->height - border; + } else if (y - border < usable_min_y) { + geometry->y = usable_min_y + border; + } else { + geometry->y = y; + } +} + +void +hikari_geometry_constrain_relative( + struct wlr_box *geometry, struct wlr_box *usable_area, int x, int y) +{ + int border = hikari_configuration->border; + int gap = hikari_configuration->gap * 2 - border; + + int usable_max_x = usable_area->x + usable_area->width - gap; + int usable_min_x = usable_area->x - geometry->width + gap; + int usable_max_y = usable_area->y + usable_area->height - gap; + int usable_min_y = usable_area->y - geometry->height + gap; + + if (x > usable_max_x) { + geometry->x = usable_max_x; + } else if (x < usable_min_x) { + geometry->x = usable_min_x; + } else { + geometry->x = x; + } + + if (y > usable_max_y) { + geometry->y = usable_max_y; + } else if (y < usable_min_y) { + geometry->y = usable_min_y; } else { geometry->y = y; } diff --git a/src/layer_shell.c b/src/layer_shell.c new file mode 100644 index 0000000..ddc626f --- /dev/null +++ b/src/layer_shell.c @@ -0,0 +1,655 @@ +#ifdef HAVE_LAYERSHELL +#include + +#ifndef NDEBUG +#include +#endif + +#include +#include + +#include +#include +#include + +static void +map_handler(struct wl_listener *listener, void *data); + +static void +unmap_handler(struct wl_listener *listener, void *data); + +static void +commit_handler(struct wl_listener *listener, void *data); + +static void +destroy_handler(struct wl_listener *listener, void *data); + +static void +new_popup_handler(struct wl_listener *listener, void *data); + +static void +for_each_surface(struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data); + +static struct wlr_surface * +surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy); + +static void +focus(struct hikari_view_interface *view_interface); + +static void +calculate_geometry(struct hikari_layer *layer); + +static void +init_layer_popup(struct hikari_layer_popup *layer_popup, + struct hikari_layer *parent, + struct wlr_xdg_popup *popup); + +static void +init_popup_popup(struct hikari_layer_popup *layer_popup, + struct hikari_layer_popup *parent, + struct wlr_xdg_popup *popup); + +static void +fini_popup(struct hikari_layer_popup *layer_popup); + +static void +commit_popup_handler(struct wl_listener *listener, void *data); + +static void +destroy_popup_handler(struct wl_listener *listener, void *data); + +static void +map_popup_handler(struct wl_listener *listener, void *data); + +static void +unmap_popup_handler(struct wl_listener *listener, void *data); + +static void +destroy_popup_handler(struct wl_listener *listener, void *data); + +static void +new_popup_popup_handler(struct wl_listener *listener, void *data); + +static struct hikari_layer * +get_layer(struct hikari_layer_popup *popup); + +static void +apply_layer_state(struct wlr_box *usable_area, + uint32_t anchor, + int32_t exclusive, + int32_t margin_top, + int32_t margin_right, + int32_t margin_bottom, + int32_t margin_left) +{ + if (exclusive <= 0) { + return; + } + struct { + uint32_t anchors; + int *positive_axis; + int *negative_axis; + int margin; + } edges[] = { + { + .anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP, + .positive_axis = &usable_area->y, + .negative_axis = &usable_area->height, + .margin = margin_top, + }, + { + .anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->height, + .margin = margin_bottom, + }, + { + .anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = &usable_area->x, + .negative_axis = &usable_area->width, + .margin = margin_left, + }, + { + .anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM, + .positive_axis = NULL, + .negative_axis = &usable_area->width, + .margin = margin_right, + }, + }; + for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) { + if ((anchor & edges[i].anchors) == edges[i].anchors && + exclusive + edges[i].margin > 0) { + if (edges[i].positive_axis) { + *edges[i].positive_axis += exclusive + edges[i].margin; + } + if (edges[i].negative_axis) { + *edges[i].negative_axis -= exclusive + edges[i].margin; + } + } + } +} + +static void +calculate_exclusive(struct hikari_output *output) +{ + struct wlr_box usable_area = { 0 }; + + wlr_output_effective_resolution( + output->output, &usable_area.width, &usable_area.height); + struct hikari_layer *layer; + wl_list_for_each ( + layer, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], layer_surfaces) { + struct wlr_layer_surface_v1 *wlr_layer = layer->surface; + struct wlr_layer_surface_v1_state *state = &wlr_layer->current; + + apply_layer_state(&usable_area, + state->anchor, + state->exclusive_zone, + state->margin.top, + state->margin.right, + state->margin.bottom, + state->margin.left); + } + + output->usable_area = usable_area; +} + +void +hikari_layer_init( + struct hikari_layer *layer, struct wlr_layer_surface_v1 *wlr_layer_surface) +{ +#ifndef NDEBUG + printf("LAYER INIT %p\n", layer); +#endif + + struct hikari_output *output = hikari_server.workspace->output; + + layer->view_interface.surface_at = surface_at; + layer->view_interface.focus = focus; + layer->view_interface.for_each_surface = for_each_surface; + layer->output = output; + layer->layer = wlr_layer_surface->client_pending.layer; + layer->surface = wlr_layer_surface; + + wlr_layer_surface->output = output->output; + + layer->commit.notify = commit_handler; + wl_signal_add(&wlr_layer_surface->surface->events.commit, &layer->commit); + + layer->destroy.notify = destroy_handler; + wl_signal_add(&wlr_layer_surface->surface->events.destroy, &layer->destroy); + + layer->map.notify = map_handler; + wl_signal_add(&wlr_layer_surface->events.map, &layer->map); + + layer->unmap.notify = unmap_handler; + wl_signal_add(&wlr_layer_surface->events.unmap, &layer->unmap); + + layer->new_popup.notify = new_popup_handler; + wl_signal_add(&wlr_layer_surface->events.new_popup, &layer->new_popup); + + wl_list_insert(&output->layers[layer->layer], &layer->layer_surfaces); + + calculate_geometry(layer); +} + +void +hikari_layer_fini(struct hikari_layer *layer) +{ + wl_list_remove(&layer->layer_surfaces); + + wl_list_remove(&layer->commit.link); + wl_list_remove(&layer->destroy.link); + wl_list_remove(&layer->map.link); + wl_list_remove(&layer->unmap.link); + wl_list_remove(&layer->new_popup.link); +} + +static void +popup_unconstrain(struct hikari_layer_popup *layer_popup) +{ + struct hikari_layer *layer = get_layer(layer_popup); + struct hikari_output *output = layer->output; + + struct wlr_box box = { .x = -layer->geometry.x, + .y = -layer->geometry.y, + .width = output->geometry.width, + .height = output->geometry.height }; + + wlr_xdg_popup_unconstrain_from_box(layer_popup->popup, &box); +} + +static void +init_popup( + struct hikari_layer_popup *layer_popup, struct wlr_xdg_popup *wlr_popup) +{ + layer_popup->popup = wlr_popup; + + layer_popup->commit.notify = commit_popup_handler; + wl_signal_add(&wlr_popup->base->surface->events.commit, &layer_popup->commit); + + layer_popup->map.notify = map_popup_handler; + wl_signal_add(&wlr_popup->base->events.map, &layer_popup->map); + + layer_popup->unmap.notify = unmap_popup_handler; + wl_signal_add(&wlr_popup->base->events.unmap, &layer_popup->unmap); + + layer_popup->destroy.notify = destroy_popup_handler; + wl_signal_add(&wlr_popup->base->events.destroy, &layer_popup->destroy); + + layer_popup->new_popup.notify = new_popup_popup_handler; + wl_signal_add(&wlr_popup->base->events.new_popup, &layer_popup->new_popup); + + popup_unconstrain(layer_popup); +} + +static struct hikari_layer * +get_layer(struct hikari_layer_popup *layer_popup) +{ + struct hikari_layer_popup *current = layer_popup; + for (;;) { + switch (current->parent.type) { + case HIKARI_LAYER_NODE_TYPE_LAYER: + return current->parent.node.layer; + break; + + case HIKARI_LAYER_NODE_TYPE_POPUP: + current = current->parent.node.popup; + break; + } + } +} + +static void +damage(struct hikari_layer *layer, bool whole) +{ + struct wlr_surface *surface = layer->surface->surface; + + if (whole) { + struct wlr_box geometry = { .x = layer->geometry.x, + .y = layer->geometry.y, + .width = surface->current.width, + .height = surface->current.height }; + + hikari_output_add_damage(layer->output, &geometry); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface, &damage); + pixman_region32_translate(&damage, layer->geometry.x, layer->geometry.y); + wlr_output_damage_add(layer->output->damage, &damage); + pixman_region32_fini(&damage); + } +} + +static void +damage_popup(struct hikari_layer_popup *layer_popup, bool whole) +{ + struct wlr_xdg_popup *popup = layer_popup->popup; + struct wlr_surface *surface = popup->base->surface; + + int popup_sx = popup->geometry.x - popup->base->geometry.x; + int popup_sy = popup->geometry.y - popup->base->geometry.y; + int ox = popup_sx, oy = popup_sy; + + struct hikari_layer *layer; + struct hikari_layer_popup *current = layer_popup; + for (;;) { + switch (current->parent.type) { + case HIKARI_LAYER_NODE_TYPE_LAYER: + layer = current->parent.node.layer; + ox += layer->geometry.x; + oy += layer->geometry.y; + goto done; + + case HIKARI_LAYER_NODE_TYPE_POPUP: + current = current->parent.node.popup; + ox += current->popup->geometry.x; + oy += current->popup->geometry.y; + break; + } + } + +done: + + assert(layer != NULL); + + struct hikari_output *output = layer->output; + + if (whole) { + struct wlr_box geometry = { .x = ox, + .y = oy, + .width = surface->current.width, + .height = surface->current.height }; + + hikari_output_add_damage(output, &geometry); + } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_surface_get_effective_damage(surface, &damage); + pixman_region32_translate(&damage, ox, oy); + wlr_output_damage_add(layer->output->damage, &damage); + pixman_region32_fini(&damage); + } +} + +static void +commit_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer *layer = wl_container_of(listener, layer, commit); + struct wlr_box old_geometry = layer->geometry; + struct hikari_output *output = layer->output; + + calculate_exclusive(layer->output); + calculate_geometry(layer); + + enum zwlr_layer_shell_v1_layer current_layer = layer->surface->current.layer; + bool updated_geometry = + memcmp(&old_geometry, &layer->geometry, sizeof(struct wlr_box)) != 0; + bool changed_layer = layer->layer != current_layer; + + if (changed_layer) { + wl_list_remove(&layer->layer_surfaces); + wl_list_insert(&output->layers[current_layer], &layer->layer_surfaces); + layer->layer = current_layer; + } + + if (updated_geometry || changed_layer) { + hikari_output_add_damage(output, &old_geometry); + hikari_output_add_damage(output, &layer->geometry); + } else { + damage(layer, false); + } +} + +static void +destroy_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer *layer = wl_container_of(listener, layer, destroy); + +#ifndef NDEBUG + printf("LAYER DESTROY %p\n", layer); +#endif + + hikari_layer_fini(layer); + + calculate_exclusive(layer->output); + + hikari_free(layer); +} + +static void +map_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer *layer = wl_container_of(listener, layer, map); + +#ifndef NDEBUG + printf("LAYER MAP %p\n", layer); +#endif + + damage(layer, true); +} + +static void +unmap_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer *layer = wl_container_of(listener, layer, unmap); + +#ifndef NDEBUG + printf("LAYER UNMAP %p\n", layer); +#endif + + damage(layer, true); +} + +static void +calculate_geometry(struct hikari_layer *layer) +{ + struct hikari_output *output = layer->output; + struct wlr_layer_surface_v1_state *state = &layer->surface->current; + + struct wlr_box geometry = { .x = 0, + .y = 0, + .width = state->desired_width, + .height = state->desired_height }; + + struct wlr_box bounds = { .x = 0, + .y = 0, + .width = output->geometry.width, + .height = output->geometry.height }; + + const uint32_t both_horiz = + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if ((state->anchor & both_horiz) && geometry.width == 0) { + geometry.x = bounds.x; + geometry.width = bounds.width; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + geometry.x = bounds.x; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + geometry.x = bounds.x + (bounds.width - geometry.width); + } else { + geometry.x = bounds.x + ((bounds.width / 2) - (geometry.width / 2)); + } + + const uint32_t both_vert = + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if ((state->anchor & both_vert) && geometry.height == 0) { + geometry.y = bounds.y; + geometry.height = bounds.height; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + geometry.y = bounds.y; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + geometry.y = bounds.y + (bounds.height - geometry.height); + } else { + geometry.y = bounds.y + ((bounds.height / 2) - (geometry.height / 2)); + } + + if ((state->anchor & both_horiz) == both_horiz) { + geometry.x += state->margin.left; + geometry.width -= state->margin.left + state->margin.right; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + geometry.x += state->margin.left; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + geometry.x -= state->margin.right; + } + if ((state->anchor & both_vert) == both_vert) { + geometry.y += state->margin.top; + geometry.height -= state->margin.top + state->margin.bottom; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + geometry.y += state->margin.top; + } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + geometry.y -= state->margin.bottom; + } + + layer->geometry = geometry; + + wlr_layer_surface_v1_configure( + layer->surface, geometry.width, geometry.height); +} + +static void +destroy_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer_popup *layer_popup = + wl_container_of(listener, layer_popup, destroy); + +#ifndef NDEBUG + printf("DESTROY LAYER POPUP %p\n", layer_popup); +#endif + + fini_popup(layer_popup); + + hikari_free(layer_popup); +} + +static void +map_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer_popup *layer_popup = + wl_container_of(listener, layer_popup, map); + +#ifndef NDEBUG + printf("MAP LAYER POPUP %p\n", layer_popup); +#endif + + damage_popup(layer_popup, true); +} + +static void +unmap_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer_popup *layer_popup = + wl_container_of(listener, layer_popup, unmap); + +#ifndef NDEBUG + printf("UNMAP LAYER POPUP %p\n", layer_popup); +#endif + + damage_popup(layer_popup, true); +} + +static void +commit_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer_popup *layer_popup = + wl_container_of(listener, layer_popup, commit); + + damage_popup(layer_popup, false); +} + +static void +new_popup_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer_popup *layer_popup = + wl_container_of(listener, layer_popup, commit); + +#ifndef NDEBUG + printf("NEW LAYER POPUP POPUP %p\n", layer_popup); +#endif + + struct hikari_layer_popup *parent = layer_popup->parent.node.popup; + struct wlr_xdg_popup *wlr_popup = data; + + struct hikari_layer_popup *layer_popup_popup = + hikari_malloc(sizeof(struct hikari_layer_popup)); + + init_popup_popup(layer_popup_popup, parent, wlr_popup); +} + +static void +new_popup_handler(struct wl_listener *listener, void *data) +{ + struct hikari_layer *layer = wl_container_of(listener, layer, new_popup); + +#ifndef NDEBUG + printf("NEW LAYER POPUP\n"); +#endif + + struct hikari_layer_popup *layer_popup = + hikari_malloc(sizeof(struct hikari_layer_popup)); + + struct wlr_xdg_popup *wlr_popup = data; + + init_layer_popup(layer_popup, layer, wlr_popup); +} + +static void +focus(struct hikari_view_interface *view_interface) +{ + struct hikari_layer *layer = (struct hikari_layer *)view_interface; + struct wlr_layer_surface_v1_state *state = &layer->surface->current; + + if (state->keyboard_interactive) { + struct hikari_workspace *workspace = hikari_server.workspace; + struct hikari_view *focus_view = workspace->focus_view; + + if (focus_view != NULL) { + hikari_workspace_focus_view(workspace, NULL); + } + + struct wlr_seat *seat = hikari_server.seat; + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); + + wlr_seat_keyboard_notify_enter(seat, + layer->surface->surface, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers); + } +} + +static void +for_each_surface(struct hikari_view_interface *view_interface, + void (*func)(struct wlr_surface *, int, int, void *), + void *data) +{ + struct hikari_layer *layer = (struct hikari_layer *)view_interface; + + wlr_layer_surface_v1_for_each_surface(layer->surface, func, data); +} + +static struct wlr_surface * +surface_at(struct hikari_view_interface *view_interface, + double ox, + double oy, + double *sx, + double *sy) +{ + struct hikari_layer *layer = (struct hikari_layer *)view_interface; + + double x = ox - layer->geometry.x; + double y = oy - layer->geometry.y; + + struct wlr_surface *surface = + wlr_layer_surface_v1_surface_at(layer->surface, x, y, sx, sy); + + return surface; +} + +static void +init_layer_popup(struct hikari_layer_popup *layer_popup, + struct hikari_layer *parent, + struct wlr_xdg_popup *wlr_popup) +{ + layer_popup->parent.type = HIKARI_LAYER_NODE_TYPE_LAYER; + layer_popup->parent.node.layer = parent; + + init_popup(layer_popup, wlr_popup); +} + +static void +init_popup_popup(struct hikari_layer_popup *layer_popup, + struct hikari_layer_popup *parent, + struct wlr_xdg_popup *wlr_popup) +{ + layer_popup->parent.type = HIKARI_LAYER_NODE_TYPE_POPUP; + layer_popup->parent.node.popup = parent; + + init_popup(layer_popup, wlr_popup); +} + +static void +fini_popup(struct hikari_layer_popup *layer_popup) +{ + wl_list_remove(&layer_popup->commit.link); + wl_list_remove(&layer_popup->destroy.link); + wl_list_remove(&layer_popup->map.link); + wl_list_remove(&layer_popup->unmap.link); + wl_list_remove(&layer_popup->new_popup.link); +} + +#endif diff --git a/src/output.c b/src/output.c index 9f3537d..e7599e2 100644 --- a/src/output.c +++ b/src/output.c @@ -9,6 +9,7 @@ #include #include #include + #ifdef HAVE_XWAYLAND #include #endif @@ -24,6 +25,7 @@ #include #include #include + #ifdef HAVE_XWAYLAND #include #include @@ -215,6 +217,30 @@ render_background( &geometry); } +#ifdef HAVE_LAYERSHELL +static void +layer_for_each(struct wl_list *layers, + void (*func)(struct wlr_surface *, int, int, void *), + void *data) +{ + struct hikari_layer *layer; + wl_list_for_each (layer, layers, layer_surfaces) { + wlr_layer_surface_v1_for_each_surface(layer->surface, func, data); + } +} + +static void +render_layer(struct wl_list *layers, struct hikari_render_data *render_data) +{ + struct hikari_layer *layer; + wl_list_for_each (layer, layers, layer_surfaces) { + render_data->geometry = &layer->geometry; + wlr_layer_surface_v1_for_each_surface( + layer->surface, render_surface, render_data); + } +} +#endif + static void render_output(struct hikari_output *output, pixman_region32_t *damage, @@ -254,6 +280,12 @@ render_output(struct hikari_output *output, render_background(output, &render_data); +#ifdef HAVE_LAYERSHELL + render_layer( + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &render_data); + render_layer(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &render_data); +#endif + struct hikari_view *view = NULL; wl_list_for_each_reverse (view, &output->workspace->views, workspace_views) { render_data.geometry = hikari_view_border_geometry(view); @@ -268,6 +300,10 @@ render_output(struct hikari_output *output, (struct hikari_view_interface *)view, render_surface, &render_data); } +#ifdef HAVE_LAYERSHELL + render_layer(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &render_data); +#endif + #ifdef HAVE_XWAYLAND struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL; wl_list_for_each_reverse (xwayland_unmanaged_view, @@ -284,6 +320,11 @@ render_output(struct hikari_output *output, hikari_server.mode->render(output, &render_data); +#ifdef HAVE_LAYERSHELL + render_layer( + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &render_data); +#endif + render_end: wlr_renderer_scissor(renderer, NULL); wlr_output_render_software_cursors(wlr_output, NULL); @@ -356,6 +397,12 @@ buffer_damage_end: xwayland_unmanaged_view->surface->surface, send_frame_done, &now); } #endif + +#ifdef HAVE_LAYERSHELL + for (int i = 0; i < 4; i++) { + layer_for_each(&output->layers[i], send_frame_done, &now); + } +#endif } void @@ -431,6 +478,10 @@ output_geometry(struct hikari_output *output) output->geometry.y = output_box->y; output->geometry.width = output_box->width; output->geometry.height = output_box->height; + + output->usable_area = (struct wlr_box){ + .x = 0, .y = 0, .width = output_box->width, .height = output_box->height + }; } /* static void */ @@ -493,6 +544,12 @@ hikari_output_init(struct hikari_output *output, struct wlr_output *wlr_output) #endif wl_list_init(&output->views); +#ifdef HAVE_LAYERSHELL + for (int i = 0; i < 4; i++) { + wl_list_init(&output->layers[i]); + } +#endif + hikari_workspace_init(output->workspace, output); wlr_output->data = output; diff --git a/src/server.c b/src/server.c index 872c654..70a501b 100644 --- a/src/server.c +++ b/src/server.c @@ -9,23 +9,30 @@ #include #include #include -#ifdef HAVE_GAMMACONTROL -#include -#endif #include #include #include #include #include #include -#ifdef HAVE_SCREENCOPY -#include -#endif #include #include #include #include #include + +#ifdef HAVE_LAYERSHELL +#include +#endif + +#ifdef HAVE_GAMMACONTROL +#include +#endif + +#ifdef HAVE_SCREENCOPY +#include +#endif + #ifdef HAVE_XWAYLAND #include #endif @@ -45,6 +52,7 @@ #include #include #include + #ifdef HAVE_XWAYLAND #include #include @@ -157,6 +165,39 @@ surface_at(struct hikari_view_interface *view_interface, return false; } +#ifdef HAVE_LAYERSHELL +static bool +layer_at(struct wl_list *layers, + double ox, + double oy, + struct wlr_surface **surface, + double *sx, + double *sy, + struct hikari_view_interface **view_interface) +{ + double out_sx, out_sy; + + struct hikari_layer *layer; + wl_list_for_each (layer, layers, layer_surfaces) { + struct hikari_view_interface *out_view_interface = + (struct hikari_view_interface *)layer; + + struct wlr_surface *out_surface = hikari_view_interface_surface_at( + out_view_interface, ox, oy, &out_sx, &out_sy); + + if (out_surface != NULL) { + *sx = out_sx; + *sy = out_sy; + *surface = out_surface; + *view_interface = out_view_interface; + return true; + } + } + + return false; +} +#endif + static struct hikari_view_interface * view_interface_at( double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) @@ -172,6 +213,7 @@ view_interface_at( struct hikari_output *output = wlr_output->data; struct hikari_workspace *workspace; + struct hikari_view_interface *view_interface; if (hikari_server.workspace != output->workspace) { if (hikari_server.workspace->focus_view != NULL) { @@ -191,13 +233,24 @@ view_interface_at( double ox = lx - output->geometry.x; double oy = ly - output->geometry.y; +#ifdef HAVE_LAYERSHELL + if (layer_at(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], + ox, + oy, + surface, + sx, + sy, + &view_interface)) { + return view_interface; + } +#endif + #ifdef HAVE_XWAYLAND struct hikari_xwayland_unmanaged_view *xwayland_unmanaged_view = NULL; wl_list_for_each (xwayland_unmanaged_view, &output->unmanaged_xwayland_views, unmanaged_server_views) { - struct hikari_view_interface *view_interface = - (struct hikari_view_interface *)xwayland_unmanaged_view; + view_interface = (struct hikari_view_interface *)xwayland_unmanaged_view; if (surface_at(view_interface, ox, oy, surface, sx, sy)) { return view_interface; @@ -205,16 +258,39 @@ view_interface_at( } #endif +#ifdef HAVE_LAYERSHELL + if (layer_at(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], + ox, + oy, + surface, + sx, + sy, + &view_interface)) { + return view_interface; + } +#endif + struct hikari_view *view = NULL; wl_list_for_each (view, &workspace->views, workspace_views) { - struct hikari_view_interface *view_interface = - (struct hikari_view_interface *)view; + view_interface = (struct hikari_view_interface *)view; if (surface_at(view_interface, ox, oy, surface, sx, sy)) { return view_interface; } } +#ifdef HAVE_LAYERSHELL + if (layer_at(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], + ox, + oy, + surface, + sx, + sy, + &view_interface)) { + return view_interface; + } +#endif + return NULL; } @@ -557,6 +633,30 @@ setup_xdg_shell(struct hikari_server *server) &server->xdg_shell->events.new_surface, &server->new_xdg_surface); } +#ifdef HAVE_LAYERSHELL +static void +new_layer_shell_surface_handler(struct wl_listener *listener, void *data) +{ + printf("NEW LAYER SURFACE\n"); + + struct wlr_layer_surface_v1 *wlr_layer_surface = + (struct wlr_layer_surface_v1 *)data; + struct hikari_layer *layer = hikari_malloc(sizeof(struct hikari_layer)); + + hikari_layer_init(layer, wlr_layer_surface); +} + +static void +setup_layer_shell(struct hikari_server *server) +{ + server->layer_shell = wlr_layer_shell_v1_create(server->display); + + wl_signal_add(&server->layer_shell->events.new_surface, + &server->new_layer_shell_surface); + server->new_layer_shell_surface.notify = new_layer_shell_surface_handler; +} +#endif + struct hikari_server hikari_server; static void @@ -702,6 +802,9 @@ server_init(struct hikari_server *server, char *config_path) setup_decorations(server); setup_selection(server); setup_xdg_shell(server); +#ifdef HAVE_LAYERSHELL + setup_layer_shell(server); +#endif wl_list_init(&server->keyboards); wl_list_init(&server->groups); diff --git a/src/sheet.c b/src/sheet.c index 1e535ce..6877805 100644 --- a/src/sheet.c +++ b/src/sheet.c @@ -409,12 +409,10 @@ hikari_sheet_apply_split(struct hikari_sheet *sheet, struct hikari_split *split) hikari_layout_init(sheet->layout, split, sheet); } - struct wlr_box geometry = { .x = 0, .y = 0 }; + struct hikari_output *output = sheet->workspace->output; + struct wlr_box geometry = output->usable_area; struct hikari_view *first = hikari_sheet_first_tileable_view(sheet); - wlr_output_effective_resolution( - sheet->workspace->output->output, &geometry.width, &geometry.height); - sheet->layout->split = split; hikari_split_apply(split, &geometry, first); diff --git a/src/view.c b/src/view.c index 35fff58..3d3ad06 100644 --- a/src/view.c +++ b/src/view.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -102,37 +103,17 @@ move_view(struct hikari_view *view, struct wlr_box *geometry, int x, int y) wlr_output_effective_resolution( view->output->output, &screen_width, &screen_height); - int constrained_x = x; - int constrained_y = y; - int border = hikari_configuration->border; - - if (constrained_x != 0) { - if (constrained_x > screen_width - 10) { - constrained_x = screen_width - 10; - } else if (constrained_x + geometry->width + border * 2 < 10) { - constrained_x = -geometry->width - 2 * border + 10; - } - } - - if (constrained_y != 0) { - if (geometry->y > screen_height - 10) { - constrained_y = screen_height - 10; - } else if (constrained_y + geometry->height + border * 2 < 10) { - constrained_y = -geometry->height - 2 * border + 10; - } - } - - if (view->move != NULL) { - view->move(view, constrained_x, constrained_y); - } - if (!hikari_view_is_hidden(view)) { hikari_view_damage_whole(view); hikari_indicator_damage(&hikari_server.indicator, view); } - geometry->x = constrained_x; - geometry->y = constrained_y; + hikari_geometry_constrain_relative( + geometry, &view->output->usable_area, x, y); + + if (view->move != NULL) { + view->move(view, geometry->x, geometry->x); + } refresh_border_geometry(view); @@ -706,15 +687,10 @@ queue_full_maximize(struct hikari_view *view) assert(!hikari_view_is_hidden(view)); struct hikari_operation *op = &view->pending_operation; - - int width, height; - wlr_output_effective_resolution(view->output->output, &width, &height); + struct hikari_output *output = view->output; op->type = HIKARI_OPERATION_TYPE_FULL_MAXIMIZE; - op->geometry.x = 0; - op->geometry.y = 0; - op->geometry.width = width; - op->geometry.height = height; + op->geometry = output->usable_area; op->center = true; if (view->move_resize != NULL) { @@ -783,17 +759,15 @@ queue_horizontal_maximize(struct hikari_view *view) assert(!hikari_view_is_hidden(view)); struct hikari_operation *op = &view->pending_operation; - - int width, height; - wlr_output_effective_resolution(view->output->output, &width, &height); + struct hikari_output *output = view->output; struct wlr_box *geometry = view->current_unmaximized_geometry; op->type = HIKARI_OPERATION_TYPE_HORIZONTAL_MAXIMIZE; - op->geometry.x = 0; + op->geometry.x = output->usable_area.x; op->geometry.y = geometry->y; op->geometry.height = geometry->height; - op->geometry.width = width; + op->geometry.width = output->usable_area.width; op->center = true; if (view->move_resize != NULL) { @@ -815,16 +789,14 @@ queue_vertical_maximize(struct hikari_view *view) assert(!hikari_view_is_hidden(view)); struct hikari_operation *op = &view->pending_operation; - - int width, height; - wlr_output_effective_resolution(view->output->output, &width, &height); + struct hikari_output *output = view->output; struct wlr_box *geometry = view->current_unmaximized_geometry; op->type = HIKARI_OPERATION_TYPE_VERTICAL_MAXIMIZE; op->geometry.x = geometry->x; - op->geometry.y = 0; - op->geometry.height = height; + op->geometry.y = output->usable_area.y; + op->geometry.height = output->usable_area.height; op->geometry.width = geometry->width; op->center = true; diff --git a/src/workspace.c b/src/workspace.c index 4508744..64f093f 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -433,9 +433,10 @@ hikari_workspace_focus_view( hikari_indicator_damage(&hikari_server.indicator, focus_view); hikari_view_activate(focus_view, false); - wlr_seat_keyboard_end_grab(seat); } + wlr_seat_keyboard_end_grab(seat); + if (view != NULL) { assert(!hikari_view_is_hidden(view)); @@ -587,7 +588,8 @@ hikari_workspace_snap_view_up(struct hikari_workspace *workspace) } if (!found) { - y = border; + struct hikari_output *output = focus_view->output; + y = border + output->usable_area.y; } hikari_view_move_absolute(focus_view, view_geometry->x, y); @@ -601,7 +603,6 @@ hikari_workspace_snap_view_down(struct hikari_workspace *workspace) hikari_server_set_cycling(); struct wlr_box *view_geometry = hikari_view_geometry(focus_view); - struct wlr_output *output = workspace->output->output; int border = hikari_configuration->border; int gap = hikari_configuration->gap; @@ -631,10 +632,9 @@ hikari_workspace_snap_view_down(struct hikari_workspace *workspace) } if (!found) { - int ow, oh; - - wlr_output_transformed_resolution(output, &ow, &oh); - y = oh - view_geometry->height - border; + struct hikari_output *output = focus_view->output; + y = output->usable_area.y + output->usable_area.height - + view_geometry->height - border; } hikari_view_move_absolute(focus_view, view_geometry->x, y); @@ -677,7 +677,8 @@ hikari_workspace_snap_view_left(struct hikari_workspace *workspace) } if (!found) { - x = border; + struct hikari_output *output = focus_view->output; + x = border + output->usable_area.x; } hikari_view_move_absolute(focus_view, x, view_geometry->y); @@ -691,7 +692,6 @@ hikari_workspace_snap_view_right(struct hikari_workspace *workspace) hikari_server_set_cycling(); struct wlr_box *view_geometry = hikari_view_geometry(focus_view); - struct wlr_output *output = workspace->output->output; int border = hikari_configuration->border; int gap = hikari_configuration->gap; @@ -721,10 +721,9 @@ hikari_workspace_snap_view_right(struct hikari_workspace *workspace) } if (!found) { - int ow, oh; - - wlr_output_transformed_resolution(output, &ow, &oh); - x = ow - view_geometry->width - border; + struct hikari_output *output = focus_view->output; + x = output->usable_area.x + output->usable_area.width - + view_geometry->width - border; } hikari_view_move_absolute(focus_view, x, view_geometry->y); diff --git a/src/xdg_view.c b/src/xdg_view.c index 5d1eee0..29bd74f 100644 --- a/src/xdg_view.c +++ b/src/xdg_view.c @@ -109,10 +109,6 @@ first_map(struct hikari_xdg_view *xdg_view, bool *focus) struct wlr_box *geometry = &xdg_view->view.geometry; struct hikari_output *output = hikari_server.workspace->output; - int screen_width, screen_height; - wlr_output_effective_resolution( - output->output, &screen_width, &screen_height); - wlr_xdg_surface_get_geometry(xdg_view->surface, geometry); const char *app_id = xdg_view->surface->toplevel->app_id; @@ -131,8 +127,7 @@ first_map(struct hikari_xdg_view *xdg_view, bool *focus) hikari_view_manage(view, sheet, group); hikari_view_set_title(view, xdg_view->surface->toplevel->title); - hikari_geometry_constrain_position( - geometry, screen_width, screen_height, x, y); + hikari_geometry_constrain_absolute(geometry, &output->usable_area, x, y); hikari_view_refresh_geometry(view, geometry); diff --git a/src/xwayland_view.c b/src/xwayland_view.c index ada884f..1d5a0cd 100644 --- a/src/xwayland_view.c +++ b/src/xwayland_view.c @@ -161,12 +161,7 @@ first_map(struct hikari_xwayland_view *xwayland_view, bool *focus) hikari_view_set_title(view, xwayland_view->surface->title); hikari_view_manage(view, sheet, group); - int screen_width, screen_height; - wlr_output_effective_resolution( - output->output, &screen_width, &screen_height); - - hikari_geometry_constrain_position( - geometry, screen_width, screen_height, x, y); + hikari_geometry_constrain_absolute(geometry, &output->usable_area, x, y); wlr_xwayland_surface_configure(xwayland_view->surface, output->geometry.x + view->geometry.x, @@ -290,17 +285,15 @@ request_configure_handler(struct wl_listener *listener, void *data) struct hikari_sheet *sheet = xwayland_view->view.sheet; - int screen_width, screen_height; + struct wlr_box *usable_area; if (sheet != NULL) { - wlr_output_effective_resolution( - xwayland_view->view.output->output, &screen_width, &screen_height); + usable_area = &xwayland_view->view.output->usable_area; } else { - wlr_output_effective_resolution( - hikari_server.workspace->output->output, &screen_width, &screen_height); + usable_area = &hikari_server.workspace->output->usable_area; } - hikari_geometry_constrain_position( - &geometry, screen_width, screen_height, event->x, event->y); + hikari_geometry_constrain_absolute( + &geometry, usable_area, event->x, event->y); wlr_xwayland_surface_configure(xwayland_surface, geometry.x,