From 7a67ecfa6321c261d455057bcec34d6391630350 Mon Sep 17 00:00:00 2001 From: migueldeoleiros Date: Wed, 30 Oct 2024 12:22:02 +0100 Subject: [PATCH 1/4] feat: implement basic master stack tiling Not my best code, a lot of asumptions are made for it to work. --- src/server.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/toplevel.c | 14 ++++++++++++++ src/toplevel.h | 3 +++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/server.c b/src/server.c index e55e4eb..b857be9 100644 --- a/src/server.c +++ b/src/server.c @@ -24,8 +24,11 @@ #include "cursor.h" #include "keyboard.h" +#include "src/output.h" #include "toplevel.h" #include "popup.h" +#include "wlr/util/box.h" +#include "wlr/types/wlr_output_layout.h" #include #include #include @@ -113,7 +116,51 @@ void server_new_xdg_popup(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_popup->events.destroy, &popup->destroy); } -void server_redraw_windows(struct turtile_server *server){ +void tile(struct turtile_server *server) { + int nmaster = 1; //number of master windows + double mfact = 0.5; //master size + unsigned int i, n = 0, mw, my, ty; + struct turtile_toplevel *toplevel; + + struct wlr_box m; + struct turtile_output *output; + wl_list_for_each(output, &server->outputs, link) + wlr_output_layout_get_box(server->output_layout, output->wlr_output, &m); + + wl_list_for_each(toplevel, &server->toplevels, link) { + if (toplevel->workspace == server->active_workspace) + n++; + } + if (n == 0) + return; + + if (n > nmaster) + mw = nmaster ? m.width * mfact : 0; + else + mw = m.width; + i = my = ty = 0; + wl_list_for_each(toplevel, &server->toplevels, link) { + if (toplevel->workspace != server->active_workspace) + continue; + if (i < 1) { + toplevel_resize(toplevel, (struct wlr_box){ + .x = m.x, .y = m.y + my, + .width = mw, + .height = (m.height - my) / + (((n) < (nmaster) ? (n) : (nmaster)) - i)}); + my += toplevel->geometry.height; + } else { + toplevel_resize(toplevel, (struct wlr_box){ + .x = m.x + mw, .y = m.y + ty, + .width = m.width - mw, + .height = (m.height - ty) / (n - i)}); + ty += toplevel->geometry.height; + } + i++; + } +} + +void server_redraw_windows(struct turtile_server *server) { struct turtile_toplevel *toplevel; wl_list_for_each(toplevel, &server->toplevels, link) { if (toplevel->workspace == server->active_workspace) { @@ -121,6 +168,7 @@ void server_redraw_windows(struct turtile_server *server){ } else { wlr_scene_node_set_enabled(&toplevel->scene_tree->node, false); } - } + } + tile(server); } diff --git a/src/toplevel.c b/src/toplevel.c index 7282776..fe97ccd 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -21,6 +21,7 @@ */ #include "toplevel.h" +#include "src/server.h" #include "src/workspace.h" #include #include @@ -148,6 +149,7 @@ void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&toplevel->request_maximize.link); wl_list_remove(&toplevel->request_fullscreen.link); + server_redraw_windows(toplevel->server); free(toplevel); } @@ -189,6 +191,18 @@ void begin_interactive(struct turtile_toplevel *toplevel, } } +void toplevel_resize( + struct turtile_toplevel *toplevel, struct wlr_box geometry) { + /* wlr_xdg_toplevel_set_bounds(toplevel->xdg_toplevel, geometry.width, */ + /* geometry.height); */ + toplevel->geometry = geometry; + + wlr_scene_node_set_position(&toplevel->scene_tree->node, + toplevel->geometry.x, toplevel->geometry.y); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->geometry.width, + toplevel->geometry.height); +} + void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive diff --git a/src/toplevel.h b/src/toplevel.h index 57524ff..2a8b2bd 100644 --- a/src/toplevel.h +++ b/src/toplevel.h @@ -32,6 +32,7 @@ struct turtile_toplevel { struct turtile_server *server; struct wlr_xdg_toplevel *xdg_toplevel; struct wlr_scene_tree *scene_tree; + struct wlr_box geometry; struct wl_listener map; struct wl_listener unmap; struct wl_listener commit; @@ -83,6 +84,8 @@ struct turtile_toplevel *desktop_toplevel_at( */ void xdg_toplevel_map(struct wl_listener *listener, void *data); +void toplevel_resize( + struct turtile_toplevel *toplevel, struct wlr_box geometry); /** * Called when the surface is unmapped, and should no longer be shown. * From e71afec497edc469fb4aed46db1b3ee5025eedaa Mon Sep 17 00:00:00 2001 From: migueldeoleiros Date: Wed, 30 Oct 2024 12:32:40 +0100 Subject: [PATCH 2/4] docs: update tiling related documentation in code --- src/server.h | 2 +- src/toplevel.c | 22 ++++++++++------------ src/toplevel.h | 10 ++++++++-- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/server.h b/src/server.h index ab357ff..d00674b 100644 --- a/src/server.h +++ b/src/server.h @@ -101,7 +101,7 @@ void server_new_xdg_popup(struct wl_listener *listener, void *data); /** * Redraws the windows on the server by enabling or disabling their scene nodes - * based on whether they are on the active workspace. + * based on whether they are on the active workspace, and run tiling script. * * @param server The server instance whose windows will be redrawn. */ diff --git a/src/toplevel.c b/src/toplevel.c index fe97ccd..84d8aa7 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -101,6 +101,16 @@ struct turtile_toplevel *desktop_toplevel_at( return tree->node.data; } +void toplevel_resize( + struct turtile_toplevel *toplevel, struct wlr_box geometry) { + toplevel->geometry = geometry; + + wlr_scene_node_set_position(&toplevel->scene_tree->node, + toplevel->geometry.x, toplevel->geometry.y); + wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->geometry.width, + toplevel->geometry.height); +} + void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct turtile_toplevel *toplevel = wl_container_of(listener, toplevel, map); @@ -191,18 +201,6 @@ void begin_interactive(struct turtile_toplevel *toplevel, } } -void toplevel_resize( - struct turtile_toplevel *toplevel, struct wlr_box geometry) { - /* wlr_xdg_toplevel_set_bounds(toplevel->xdg_toplevel, geometry.width, */ - /* geometry.height); */ - toplevel->geometry = geometry; - - wlr_scene_node_set_position(&toplevel->scene_tree->node, - toplevel->geometry.x, toplevel->geometry.y); - wlr_xdg_toplevel_set_size(toplevel->xdg_toplevel, toplevel->geometry.width, - toplevel->geometry.height); -} - void xdg_toplevel_request_move( struct wl_listener *listener, void *data) { /* This event is raised when a client would like to begin an interactive diff --git a/src/toplevel.h b/src/toplevel.h index 2a8b2bd..4ce4fa7 100644 --- a/src/toplevel.h +++ b/src/toplevel.h @@ -74,6 +74,14 @@ void focus_toplevel(struct turtile_toplevel *toplevel, struct turtile_toplevel *desktop_toplevel_at( struct turtile_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy); +/** + * Resizes the given toplevel to the specified geometry. + * + * @param toplevel The turtile toplevel to resize. + * @param geometry The new geometry for the toplevel. + */ +void toplevel_resize( + struct turtile_toplevel *toplevel, struct wlr_box geometry); /** * Called when the surface is mapped, or ready to display on-screen. @@ -84,8 +92,6 @@ struct turtile_toplevel *desktop_toplevel_at( */ void xdg_toplevel_map(struct wl_listener *listener, void *data); -void toplevel_resize( - struct turtile_toplevel *toplevel, struct wlr_box geometry); /** * Called when the surface is unmapped, and should no longer be shown. * From b34eadee45bb31fc00f9ae75af68a89003f8a0d7 Mon Sep 17 00:00:00 2001 From: migueldeoleiros Date: Wed, 30 Oct 2024 17:53:37 +0100 Subject: [PATCH 3/4] fix: Clients no longer move on focus switch - Implemented a separate list of clients to handle focus to avoid the tiling being afected. - Add focus switch on workspace switch --- src/main.c | 1 + src/server.h | 1 + src/toplevel.c | 22 +++++++++++++++++----- src/toplevel.h | 11 +++++++++++ src/workspace.c | 7 ++++++- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index 91554ed..8d05308 100644 --- a/src/main.c +++ b/src/main.c @@ -150,6 +150,7 @@ int main(int argc, char *argv[]) { * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ wl_list_init(&server.toplevels); + wl_list_init(&server.focus_toplevels); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 3); server.new_xdg_toplevel.notify = server_new_xdg_toplevel; wl_signal_add(&server.xdg_shell->events.new_toplevel, &server.new_xdg_toplevel); diff --git a/src/server.h b/src/server.h index d00674b..b347754 100644 --- a/src/server.h +++ b/src/server.h @@ -44,6 +44,7 @@ struct turtile_server { struct wl_listener new_xdg_toplevel; struct wl_listener new_xdg_popup; struct wl_list toplevels; + struct wl_list focus_toplevels; struct wl_list workspaces; struct turtile_workspace *active_workspace; diff --git a/src/toplevel.c b/src/toplevel.c index 84d8aa7..73a97ed 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -27,7 +27,6 @@ #include #include #include -#include void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ @@ -55,11 +54,11 @@ void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surfa } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); // switch to the right workspace - switch_workspace(toplevel->workspace); + /* switch_workspace(toplevel->workspace); */ + server->active_workspace = toplevel->workspace; /* Move the toplevel to the front */ - wlr_scene_node_raise_to_top(&toplevel->scene_tree->node); - wl_list_remove(&toplevel->link); - wl_list_insert(&server->toplevels, &toplevel->link); + wl_list_remove(&toplevel->flink); + wl_list_insert(&server->focus_toplevels, &toplevel->flink); /* Activate the new surface */ wlr_xdg_toplevel_set_activated(toplevel->xdg_toplevel, true); /* @@ -71,6 +70,15 @@ void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surfa wlr_seat_keyboard_notify_enter(seat, toplevel->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } + server_redraw_windows(server); +} + +struct turtile_toplevel *get_first_toplevel(struct turtile_server *server) { + struct turtile_toplevel *toplevel; + wl_list_for_each(toplevel, &server->focus_toplevels, flink) + if (toplevel->workspace == server->active_workspace) + return toplevel; + return NULL; } struct turtile_toplevel *desktop_toplevel_at( @@ -117,6 +125,7 @@ void xdg_toplevel_map(struct wl_listener *listener, void *data) { toplevel->workspace = toplevel->server->active_workspace; wl_list_insert(&toplevel->server->toplevels, &toplevel->link); + wl_list_insert(&toplevel->server->focus_toplevels, &toplevel->flink); focus_toplevel(toplevel, toplevel->xdg_toplevel->base->surface); } @@ -128,6 +137,9 @@ void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Reset the cursor mode if the grabbed toplevel was unmapped. */ if (toplevel == toplevel->server->grabbed_toplevel) { reset_cursor_mode(toplevel->server); + + struct turtile_toplevel *newfocus = get_first_toplevel(toplevel->server); + focus_toplevel(newfocus, newfocus->xdg_toplevel->base->surface); } wl_list_remove(&toplevel->link); diff --git a/src/toplevel.h b/src/toplevel.h index 4ce4fa7..9fdd4d9 100644 --- a/src/toplevel.h +++ b/src/toplevel.h @@ -26,9 +26,11 @@ #include "cursor.h" #include "src/output.h" #include +#include struct turtile_toplevel { struct wl_list link; + struct wl_list flink; struct turtile_server *server; struct wlr_xdg_toplevel *xdg_toplevel; struct wlr_scene_tree *scene_tree; @@ -54,6 +56,15 @@ struct turtile_toplevel { */ void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surface); +/** + * Retrieves the first toplevel on the active workspace of the given server. + * If no such toplevel is found, NULL is returned. + + * @param server The turtile server to search for the toplevel on. + * @return A pointer to the first toplevel on the active workspace, + * or NULL if no such toplevel is found. + */ +struct turtile_toplevel *get_first_toplevel(struct turtile_server *server); /** * Given a server, layout coordinates, and optional surface and position diff --git a/src/workspace.c b/src/workspace.c index 84448b4..0682f15 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -23,6 +23,7 @@ #include "workspace.h" #include "src/config.h" #include "src/server.h" +#include "src/toplevel.h" #include "wlr/util/log.h" #include #include @@ -45,8 +46,12 @@ void switch_workspace(struct turtile_workspace *workspace){ return; } struct turtile_server *server = workspace->server; - server->active_workspace = workspace; + + struct turtile_toplevel *newfocus = get_first_toplevel(server); + if(newfocus != NULL) + focus_toplevel(newfocus, newfocus->xdg_toplevel->base->surface); + server_redraw_windows(server); } From 0568fd0c163d1266a24e0d3cf8268cba299d2cf9 Mon Sep 17 00:00:00 2001 From: migueldeoleiros Date: Wed, 30 Oct 2024 19:25:37 +0100 Subject: [PATCH 4/4] ci: add xvfb to tests --- .github/workflows/main.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a99d430..cc34411 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,10 @@ jobs: run: | sudo apt update sudo apt install -y meson ninja-build gcc cmake pkg-config \ - libxkbcommon-dev libconfig-dev libjson-c-dev python3 weston + libxkbcommon-dev libconfig-dev libjson-c-dev + + - name: Install test dependencies + run: sudo apt-get install -y xvfb python3 weston - name: Install wayland-server run: | @@ -72,7 +75,7 @@ jobs: - name: Run functional tests run: | mkdir -p $XDG_RUNTIME_DIR - TURTILE_BACKEND=headless ./build/turtile -c tests/test.cfg & + TURTILE_BACKEND=headless xvfb-run --auto-servernum --server-args='-screen 0 1024x768x24' ./build/turtile -c tests/test.cfg & sleep 5 python tests/test.py env: