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: 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.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/server.h b/src/server.h index ab357ff..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; @@ -101,7 +102,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 7282776..73a97ed 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -21,12 +21,12 @@ */ #include "toplevel.h" +#include "src/server.h" #include "src/workspace.h" #include #include #include #include -#include void focus_toplevel(struct turtile_toplevel *toplevel, struct wlr_surface *surface) { /* Note: this function only deals with keyboard focus. */ @@ -54,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); /* @@ -70,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( @@ -100,12 +109,23 @@ 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); 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); } @@ -117,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); @@ -148,6 +171,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); } diff --git a/src/toplevel.h b/src/toplevel.h index 57524ff..9fdd4d9 100644 --- a/src/toplevel.h +++ b/src/toplevel.h @@ -26,12 +26,15 @@ #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; + struct wlr_box geometry; struct wl_listener map; struct wl_listener unmap; struct wl_listener commit; @@ -53,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 @@ -73,6 +85,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. 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); }