diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index c780a52b6..137fcd229 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -1,6 +1,7 @@ #ifndef _SWAY_INPUT_SEAT_H #define _SWAY_INPUT_SEAT_H +#include #include #include "sway/input/input-manager.h" @@ -28,6 +29,9 @@ struct sway_seat { bool has_focus; struct wl_list focus_stack; // list of containers in focus order + // If the focused layer is set, views cannot receive keyboard focus + struct wlr_layer_surface *focused_layer; + struct wl_listener focus_destroy; struct wl_listener new_container; @@ -57,6 +61,9 @@ void seat_set_focus(struct sway_seat *seat, struct sway_container *container); void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp); +void seat_set_focus_layer(struct sway_seat *seat, + struct wlr_layer_surface *layer); + struct sway_container *seat_get_focus(struct sway_seat *seat); /** diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 74292519f..503b961c1 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -58,6 +58,7 @@ struct swaybar_output { bool focused; uint32_t width, height; + int32_t scale; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; }; diff --git a/sway/config/output.c b/sway/config/output.c index c4b74ce2c..1c298d37a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -123,11 +123,14 @@ void terminate_swaybg(pid_t pid) { void apply_output_config(struct output_config *oc, struct sway_container *output) { assert(output->type == C_OUTPUT); + struct wlr_output_layout *output_layout = + root_container.sway_root->output_layout; struct wlr_output *wlr_output = output->sway_output->wlr_output; + if (oc && oc->enabled == 0) { + container_destroy(output); wlr_output_layout_remove(root_container.sway_root->output_layout, wlr_output); - container_destroy(output); return; } @@ -148,11 +151,9 @@ void apply_output_config(struct output_config *oc, struct sway_container *output // Find position for it if (oc && (oc->x != -1 || oc->y != -1)) { wlr_log(L_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); - wlr_output_layout_add(root_container.sway_root->output_layout, - wlr_output, oc->x, oc->y); + wlr_output_layout_add(output_layout, wlr_output, oc->x, oc->y); } else { - wlr_output_layout_add_auto(root_container.sway_root->output_layout, - wlr_output); + wlr_output_layout_add_auto(output_layout, wlr_output); } if (!oc || !oc->background) { diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index c18f51c71..663ec7baf 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -7,6 +7,8 @@ #include #include #include +#include "sway/input/input-manager.h" +#include "sway/input/seat.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" @@ -187,6 +189,31 @@ void arrange_layers(struct sway_output *output) { &usable_area, false); arrange_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &usable_area, false); + + // Find topmost keyboard interactive layer, if such a layer exists + uint32_t layers_above_shell[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); + struct sway_layer_surface *layer, *topmost = NULL; + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(layer, + &output->layers[layers_above_shell[i]], link) { + if (layer->layer_surface->current.keyboard_interactive) { + topmost = layer; + break; + } + } + if (topmost != NULL) { + break; + } + } + + struct sway_seat *seat; + wl_list_for_each(seat, &input_manager->seats, link) { + seat_set_focus_layer(seat, topmost ? topmost->layer_surface : NULL); + } } static void handle_output_destroy(struct wl_listener *listener, void *data) { @@ -251,6 +278,8 @@ static void handle_map(struct wl_listener *listener, void *data) { sway_layer, map); struct sway_output *output = sway_layer->layer_surface->output->data; wlr_output_damage_add_box(output->damage, &sway_layer->geo); + wlr_surface_send_enter(sway_layer->layer_surface->surface, + sway_layer->layer_surface->output); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 96f23291d..8a4fb4a2d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -57,10 +57,7 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, */ static bool surface_intersect_output(struct wlr_surface *surface, struct wlr_output_layout *output_layout, struct wlr_output *wlr_output, - double lx, double ly, float rotation, struct wlr_box *box) { - double ox = lx, oy = ly; - wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); - + double ox, double oy, float rotation, struct wlr_box *box) { if (box != NULL) { box->x = ox * wlr_output->scale; box->y = oy * wlr_output->scale; @@ -69,7 +66,7 @@ static bool surface_intersect_output(struct wlr_surface *surface, } struct wlr_box layout_box = { - .x = lx, .y = ly, + .x = wlr_output->lx + ox, .y = wlr_output->ly + oy, .width = surface->current->width, .height = surface->current->height, }; wlr_box_rotated_bounds(&layout_box, rotation, &layout_box); @@ -78,7 +75,7 @@ static bool surface_intersect_output(struct wlr_surface *surface, static void render_surface(struct wlr_surface *surface, struct wlr_output *wlr_output, struct timespec *when, - double lx, double ly, float rotation) { + double ox, double oy, float rotation) { struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); @@ -90,7 +87,7 @@ static void render_surface(struct wlr_surface *surface, struct wlr_box box; bool intersects = surface_intersect_output(surface, layout, wlr_output, - lx, ly, rotation, &box); + ox, oy, rotation, &box); if (intersects) { float matrix[9]; enum wl_output_transform transform = @@ -113,7 +110,7 @@ static void render_surface(struct wlr_surface *surface, surface->current->width, surface->current->height, rotation); render_surface(subsurface->surface, wlr_output, when, - lx + sx, ly + sy, rotation); + ox + sx, oy + sy, rotation); } } @@ -211,9 +208,7 @@ static void render_view(struct sway_container *view, void *data) { } } -static void render_layer(struct sway_output *output, - const struct wlr_box *output_layout_box, - struct timespec *when, +static void render_layer(struct sway_output *output, struct timespec *when, struct wl_list *layer) { struct sway_layer_surface *sway_layer; wl_list_for_each(sway_layer, layer, link) { @@ -245,14 +240,15 @@ static void render_output(struct sway_output *output, struct timespec *when, float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; wlr_renderer_clear(renderer, clear_color); - struct wlr_output_layout *layout = root_container.sway_root->output_layout; + struct wlr_output_layout *output_layout = + root_container.sway_root->output_layout; const struct wlr_box *output_box = - wlr_output_layout_get_box(layout, wlr_output); + wlr_output_layout_get_box(output_layout, wlr_output); - render_layer(output, output_box, when, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer(output, output_box, when, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); + render_layer(output, when, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); + render_layer(output, when, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); struct sway_seat *seat = input_manager_current_seat(input_manager); struct sway_container *focus = @@ -262,7 +258,7 @@ static void render_output(struct sway_output *output, struct timespec *when, focus = output->swayc->children->items[0]; } struct sway_container *workspace = focus->type == C_WORKSPACE ? - focus : container_parent(focus, C_WORKSPACE); + focus : container_parent(focus, C_WORKSPACE); struct render_data rdata = { .output = output, @@ -296,10 +292,10 @@ static void render_output(struct sway_output *output, struct timespec *when, } // TODO: Consider revising this when fullscreen windows are supported - render_layer(output, output_box, when, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - render_layer(output, output_box, when, - &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); + render_layer(output, when, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); + render_layer(output, when, + &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); renderer_end: wlr_renderer_end(renderer); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 97b4473c4..9229e92d1 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -180,13 +180,18 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) { double sx, sy; struct sway_container *cont = container_at_cursor(cursor, &surface, &sx, &sy); + if (surface && wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface *layer = + wlr_layer_surface_from_wlr_surface(surface); + if (layer->current.keyboard_interactive) { + seat_set_focus_layer(cursor->seat, layer); + return; + } + } // Avoid moving keyboard focus from a surface that accepts it to one // that does not unless the change would move us to a new workspace. // // This prevents, for example, losing focus when clicking on swaybar. - // - // TODO: Replace this condition with something like - // !surface_accepts_keyboard_input if (surface && cont && cont->type != C_VIEW) { struct sway_container *new_ws = cont; if (new_ws && new_ws->type != C_WORKSPACE) { diff --git a/sway/input/seat.c b/sway/input/seat.c index c326f176b..4a99e9eb4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -352,8 +352,11 @@ void seat_configure_xcursor(struct sway_seat *seat) { void seat_set_focus_warp(struct sway_seat *seat, struct sway_container *container, bool warp) { - struct sway_container *last_focus = seat_get_focus(seat); + if (seat->focused_layer) { + return; + } + struct sway_container *last_focus = seat_get_focus(seat); if (container && last_focus == container) { return; } @@ -419,6 +422,37 @@ void seat_set_focus(struct sway_seat *seat, seat_set_focus_warp(seat, container, true); } +void seat_set_focus_layer(struct sway_seat *seat, + struct wlr_layer_surface *layer) { + if (!layer) { + seat->focused_layer = NULL; + return; + } + if (seat->focused_layer == layer) { + return; + } + if (seat->has_focus) { + struct sway_container *focus = seat_get_focus(seat); + if (focus->type == C_VIEW) { + wlr_seat_keyboard_clear_focus(seat->wlr_seat); + view_set_activated(focus->sway_view, false); + } + } + if (layer->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + seat->focused_layer = layer; + } + struct wlr_keyboard *keyboard = + wlr_seat_get_keyboard(seat->wlr_seat); + if (keyboard) { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + layer->surface, keyboard->keycodes, + keyboard->num_keycodes, &keyboard->modifiers); + } else { + wlr_seat_keyboard_notify_enter(seat->wlr_seat, + layer->surface, NULL, 0, NULL); + } +} + struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, struct sway_container *container) { return seat_get_focus_by_type(seat, container, C_TYPES); diff --git a/sway/tree/layout.c b/sway/tree/layout.c index a46359bdf..5abdbc321 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c @@ -18,10 +18,14 @@ struct sway_container root_container; -static void output_layout_change_notify(struct wl_listener *listener, +static void output_layout_handle_change(struct wl_listener *listener, void *data) { - struct wlr_box *layout_box = wlr_output_layout_get_box( - root_container.sway_root->output_layout, NULL); + struct wlr_output_layout *output_layout = + root_container.sway_root->output_layout; + const struct wlr_box *layout_box = + wlr_output_layout_get_box(output_layout, NULL); + root_container.x = layout_box->x; + root_container.y = layout_box->y; root_container.width = layout_box->width; root_container.height = layout_box->height; @@ -33,8 +37,8 @@ static void output_layout_change_notify(struct wl_listener *listener, } struct sway_output *output = output_container->sway_output; - struct wlr_box *output_box = wlr_output_layout_get_box( - root_container.sway_root->output_layout, output->wlr_output); + const struct wlr_box *output_box = + wlr_output_layout_get_box(output_layout, output->wlr_output); if (!output_box) { continue; } @@ -74,7 +78,7 @@ void layout_init(void) { wl_signal_init(&root_container.sway_root->events.new_container); root_container.sway_root->output_layout_change.notify = - output_layout_change_notify; + output_layout_handle_change; wl_signal_add(&root_container.sway_root->output_layout->events.change, &root_container.sway_root->output_layout_change); } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 6ba3d9732..316f01e4e 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -139,6 +139,24 @@ char *workspace_next_name(const char *output_name) { continue; } + // If the command is workspace number , isolate the name + if (strncmp(_target, "number ", strlen("number ")) == 0) { + size_t length = strlen(_target) - strlen("number ") + 1; + char *temp = malloc(length); + strncpy(temp, _target + strlen("number "), length - 1); + temp[length - 1] = '\0'; + free(_target); + _target = temp; + wlr_log(L_DEBUG, "Isolated name from workspace number: '%s'", _target); + + // Make sure the workspace number doesn't already exist + if (workspace_by_number(_target)) { + free(_target); + free(dup); + continue; + } + } + // Make sure that the workspace doesn't already exist if (workspace_by_name(_target)) { free(_target); diff --git a/swaybar/bar.c b/swaybar/bar.c index fb4170954..ea0141ccd 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -69,11 +69,19 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, break; } } + int max_scale = 1; + struct swaybar_output *_output; + wl_list_for_each(_output, &bar->outputs, link) { + if (_output->scale > max_scale) { + max_scale = _output->scale; + } + } + wl_surface_set_buffer_scale(pointer->cursor_surface, max_scale); wl_surface_attach(pointer->cursor_surface, wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); wl_pointer_set_cursor(wl_pointer, serial, pointer->cursor_surface, - pointer->cursor_image->hotspot_x, - pointer->cursor_image->hotspot_y); + pointer->cursor_image->hotspot_x / max_scale, + pointer->cursor_image->hotspot_y / max_scale); wl_surface_commit(pointer->cursor_surface); } @@ -103,10 +111,12 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } struct swaybar_hotspot *hotspot; wl_list_for_each(hotspot, &output->hotspots, link) { - if (pointer->x >= hotspot->x - && pointer->y >= hotspot->y - && pointer->x < hotspot->x + hotspot->width - && pointer->y < hotspot->y + hotspot->height) { + double x = pointer->x * output->scale; + double y = pointer->y * output->scale; + if (x >= hotspot->x + && y >= hotspot->y + && x < hotspot->x + hotspot->width + && y < hotspot->y + hotspot->height) { hotspot->callback(output, pointer->x, pointer->y, button, hotspot->data); } @@ -197,12 +207,43 @@ const struct wl_seat_listener seat_listener = { .name = seat_handle_name, }; +static void output_geometry(void *data, struct wl_output *output, int32_t x, + int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, + const char *make, const char *model, int32_t transform) { + // Who cares +} + +static void output_mode(void *data, struct wl_output *output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) { + // Who cares +} + +static void output_done(void *data, struct wl_output *output) { + // Who cares +} + +static void output_scale(void *data, struct wl_output *wl_output, + int32_t factor) { + struct swaybar_output *output = data; + output->scale = factor; + if (output->surface) { + render_frame(output->bar, output); + } +} + +struct wl_output_listener output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaybar *bar = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { bar->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 1); + &wl_compositor_interface, 3); } else if (strcmp(interface, wl_seat_interface.name) == 0) { bar->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); @@ -216,7 +257,9 @@ static void handle_global(void *data, struct wl_registry *registry, calloc(1, sizeof(struct swaybar_output)); output->bar = bar; output->output = wl_registry_bind(registry, name, - &wl_output_interface, 1); + &wl_output_interface, 3); + wl_output_add_listener(output->output, &output_listener, output); + output->scale = 1; output->index = index++; wl_list_init(&output->workspaces); wl_list_init(&output->hotspots); @@ -256,24 +299,36 @@ void bar_setup(struct swaybar *bar, bar->status = status_line_init(bar->config->status_command); } - assert(bar->display = wl_display_connect(NULL)); + bar->display = wl_display_connect(NULL); + assert(bar->display); struct wl_registry *registry = wl_display_get_registry(bar->display); wl_registry_add_listener(registry, ®istry_listener, bar); wl_display_roundtrip(bar->display); assert(bar->compositor && bar->layer_shell && bar->shm); + wl_display_roundtrip(bar->display); + struct swaybar_pointer *pointer = &bar->pointer; - assert(pointer->cursor_theme = wl_cursor_theme_load(NULL, 16, bar->shm)); + int max_scale = 1; + struct swaybar_output *output; + wl_list_for_each(output, &bar->outputs, link) { + if (output->scale > max_scale) { + max_scale = output->scale; + } + } + + pointer->cursor_theme = wl_cursor_theme_load( + NULL, 24 * max_scale, bar->shm); + assert(pointer->cursor_theme); struct wl_cursor *cursor; - assert(cursor = wl_cursor_theme_get_cursor( - pointer->cursor_theme, "left_ptr")); + cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + assert(cursor); pointer->cursor_image = cursor->images[0]; - assert(pointer->cursor_surface = - wl_compositor_create_surface(bar->compositor)); + pointer->cursor_surface = wl_compositor_create_surface(bar->compositor); + assert(pointer->cursor_surface); // TODO: we might not necessarily be meant to do all of the outputs - struct swaybar_output *output; wl_list_for_each(output, &bar->outputs, link) { struct config_output *coutput; wl_list_for_each(coutput, &bar->config->outputs, link) { @@ -281,8 +336,8 @@ void bar_setup(struct swaybar *bar, continue; } output->name = strdup(coutput->name); - assert(output->surface = wl_compositor_create_surface( - bar->compositor)); + output->surface = wl_compositor_create_surface(bar->compositor); + assert(output->surface); output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( bar->layer_shell, output->surface, output->output, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 46459e248..ed134a016 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -203,6 +203,9 @@ void i3bar_block_send_click(struct status_line *status, json_object_object_add(event_json, "button", json_object_new_int(button)); json_object_object_add(event_json, "x", json_object_new_int(x)); json_object_object_add(event_json, "y", json_object_new_int(y)); - dprintf(status->write_fd, "%s\n", json_object_to_json_string(event_json)); + if (dprintf(status->write_fd, "%s\n", + json_object_to_json_string(event_json)) < 0) { + status_error(status, "[failed to write click event]"); + } json_object_put(event_json); } diff --git a/swaybar/render.c b/swaybar/render.c index 6f3b07889..be58301d0 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -14,55 +14,72 @@ #include "swaybar/status_line.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static const int ws_horizontal_padding = 5; -static const double ws_vertical_padding = 1.5; -static const double border_width = 1; +static const int WS_HORIZONTAL_PADDING = 5; +static const double WS_VERTICAL_PADDING = 1.5; +static const double BORDER_WIDTH = 1; static uint32_t render_status_line_error(cairo_t *cairo, - struct swaybar_config *config, const char *error, - double *x, uint32_t width, uint32_t height) { + struct swaybar_output *output, struct swaybar_config *config, + const char *error, double *x, uint32_t surface_height) { if (!error) { return 0; } + + uint32_t height = surface_height * output->scale; + cairo_set_source_u32(cairo, 0xFF0000FF); - static const int margin = 3; + + int margin = 3 * output->scale; + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int text_width, text_height; get_text_size(cairo, config->font, - &text_width, &text_height, 1, false, "%s", error); + &text_width, &text_height, output->scale, false, "%s", error); + uint32_t ideal_height = text_height + ws_vertical_padding * 2; if (height < ideal_height) { - return ideal_height; + return ideal_height / output->scale; } *x -= text_width + margin; + double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, false, "%s", error); + pango_printf(cairo, config->font, output->scale, false, "%s", error); *x -= margin; - return ideal_height; + return ideal_height / output->scale; } static uint32_t render_status_line_text(cairo_t *cairo, - struct swaybar_config *config, const char *text, - bool focused, double *x, uint32_t width, uint32_t height) { + struct swaybar_output *output, struct swaybar_config *config, + const char *text, bool focused, double *x, uint32_t surface_height) { if (!text) { return 0; } + + uint32_t height = surface_height * output->scale; + cairo_set_source_u32(cairo, focused ? config->colors.focused_statusline : config->colors.statusline); - static const int margin = 3; + int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, config->pango_markup, "%s", text); + output->scale, config->pango_markup, "%s", text); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int margin = 3 * output->scale; + uint32_t ideal_height = text_height + ws_vertical_padding * 2; if (height < ideal_height) { - return ideal_height; + return ideal_height / output->scale; } + *x -= text_width + margin; double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, config->pango_markup, "%s", text); + pango_printf(cairo, config->font, output->scale, + config->pango_markup, "%s", text); *x -= margin; - return ideal_height; + return ideal_height / output->scale; } static void render_sharp_line(cairo_t *cairo, uint32_t color, @@ -99,23 +116,29 @@ static void block_hotspot_callback(struct swaybar_output *output, static uint32_t render_status_block(cairo_t *cairo, struct swaybar_config *config, struct swaybar_output *output, struct i3bar_block *block, double *x, - uint32_t height, bool focused, bool edge) { - static const int margin = 3; + uint32_t surface_height, bool focused, bool edge) { if (!block->full_text || !*block->full_text) { return 0; } + uint32_t height = surface_height * output->scale; + int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, block->markup, "%s", block->full_text); + output->scale, block->markup, "%s", block->full_text); + + int margin = 3 * output->scale; + int ws_vertical_padding = WS_VERTICAL_PADDING * 2; + int width = text_width; if (width < block->min_width) { width = block->min_width; } + double block_width = width; uint32_t ideal_height = text_height + ws_vertical_padding * 2; if (height < ideal_height) { - return ideal_height; + return ideal_height / output->scale; } *x -= width; @@ -133,10 +156,10 @@ static uint32_t render_status_block(cairo_t *cairo, if (config->sep_symbol) { int _height; get_text_size(cairo, config->font, &sep_width, &_height, - 1, false, "%s", config->sep_symbol); + output->scale, false, "%s", config->sep_symbol); uint32_t _ideal_height = _height + ws_vertical_padding * 2; - if (height < _ideal_height) { - return _height; + if ((uint32_t)_height < _ideal_height / output->scale) { + return _height / output->scale; } if (sep_width > block->separator_block_width) { block->separator_block_width = sep_width + margin * 2; @@ -160,22 +183,26 @@ static uint32_t render_status_block(cairo_t *cairo, double pos = *x; if (block->background) { cairo_set_source_u32(cairo, block->background); - cairo_rectangle(cairo, pos - 0.5, 1, block_width, height); + cairo_rectangle(cairo, pos - 0.5 * output->scale, + output->scale, block_width, height); cairo_fill(cairo); } if (block->border && block->border_top > 0) { render_sharp_line(cairo, block->border, - pos - 0.5, 1, block_width, block->border_top); + pos - 0.5 * output->scale, output->scale, + block_width, block->border_top); } if (block->border && block->border_bottom > 0) { render_sharp_line(cairo, block->border, - pos - 0.5, height - 1 - block->border_bottom, + pos - 0.5 * output->scale, + height - output->scale - block->border_bottom, block_width, block->border_bottom); } if (block->border != 0 && block->border_left > 0) { render_sharp_line(cairo, block->border, - pos - 0.5, 1, block->border_left, height); + pos - 0.5 * output->scale, output->scale, + block->border_left, height); pos += block->border_left + margin; } @@ -190,13 +217,15 @@ static uint32_t render_status_block(cairo_t *cairo, cairo_move_to(cairo, offset, height / 2.0 - text_height / 2.0); uint32_t color = block->color ? *block->color : config->colors.statusline; cairo_set_source_u32(cairo, color); - pango_printf(cairo, config->font, 1, block->markup, "%s", block->full_text); + pango_printf(cairo, config->font, output->scale, + block->markup, "%s", block->full_text); pos += width; if (block->border && block->border_right > 0) { pos += margin; render_sharp_line(cairo, block->border, - pos - 0.5, 1, block->border_right, height); + pos - 0.5 * output->scale, output->scale, + block->border_right, height); pos += block->border_right; } @@ -209,7 +238,7 @@ static uint32_t render_status_block(cairo_t *cairo, if (config->sep_symbol) { offset = pos + (block->separator_block_width - sep_width) / 2; cairo_move_to(cairo, offset, margin); - pango_printf(cairo, config->font, 1, false, + pango_printf(cairo, config->font, output->scale, false, "%s", config->sep_symbol); } else { cairo_set_line_width(cairo, 1); @@ -220,19 +249,19 @@ static uint32_t render_status_block(cairo_t *cairo, cairo_stroke(cairo); } } - return ideal_height; + return ideal_height / output->scale; } static uint32_t render_status_line_i3bar(cairo_t *cairo, struct swaybar_config *config, struct swaybar_output *output, struct status_line *status, bool focused, - double *x, uint32_t width, uint32_t height) { + double *x, uint32_t surface_height) { uint32_t max_height = 0; bool edge = true; struct i3bar_block *block; wl_list_for_each(block, &status->blocks, link) { uint32_t h = render_status_block(cairo, config, output, - block, x, height, focused, edge); + block, x, surface_height, focused, edge); max_height = h > max_height ? h : max_height; edge = false; } @@ -242,17 +271,17 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, static uint32_t render_status_line(cairo_t *cairo, struct swaybar_config *config, struct swaybar_output *output, struct status_line *status, bool focused, - double *x, uint32_t width, uint32_t height) { + double *x, uint32_t surface_height) { switch (status->protocol) { case PROTOCOL_ERROR: - return render_status_line_error(cairo, - config, status->text, x, width, height); + return render_status_line_error(cairo, output, config, + status->text, x, surface_height); case PROTOCOL_TEXT: - return render_status_line_text(cairo, - config, status->text, focused, x, width, height); + return render_status_line_text(cairo, output, config, + status->text, focused, x, surface_height); case PROTOCOL_I3BAR: - return render_status_line_i3bar(cairo, config, output, status, - focused, x, width, height); + return render_status_line_i3bar(cairo, config, output, + status, focused, x, surface_height); case PROTOCOL_UNDEF: return 0; } @@ -260,15 +289,23 @@ static uint32_t render_status_line(cairo_t *cairo, } static uint32_t render_binding_mode_indicator(cairo_t *cairo, - struct swaybar_config *config, const char *mode, double x, - uint32_t height) { + struct swaybar_output *output, struct swaybar_config *config, + const char *mode, double x, uint32_t surface_height) { + + uint32_t height = surface_height * output->scale; + int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, true, "%s", mode); + output->scale, true, "%s", mode); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + border_width * 2; if (height < ideal_height) { - return ideal_height; + return ideal_height / output->scale; } uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; @@ -289,8 +326,8 @@ static uint32_t render_binding_mode_indicator(cairo_t *cairo, double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, true, "%s", mode); - return ideal_height; + pango_printf(cairo, config->font, output->scale, true, "%s", mode); + return ideal_height / output->scale; } static const char *strip_workspace_number(const char *ws_name) { @@ -313,7 +350,7 @@ static void workspace_hotspot_callback(struct swaybar_output *output, static uint32_t render_workspace_button(cairo_t *cairo, struct swaybar_output *output, struct swaybar_config *config, - struct swaybar_workspace *ws, double *x, uint32_t height) { + struct swaybar_workspace *ws, double *x, uint32_t surface_height) { const char *name = ws->name; if (config->strip_workspace_numbers) { name = strip_workspace_number(ws->name); @@ -330,14 +367,22 @@ static uint32_t render_workspace_button(cairo_t *cairo, box_colors = config->colors.inactive_workspace; } + uint32_t height = surface_height * output->scale; + int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, - 1, true, "%s", name); + output->scale, true, "%s", name); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + uint32_t ideal_height = ws_vertical_padding * 2 + text_height + border_width * 2; if (height < ideal_height) { - return ideal_height; + return ideal_height / output->scale; } + uint32_t width = ws_horizontal_padding * 2 + text_width + border_width * 2; cairo_set_source_u32(cairo, box_colors.background); @@ -357,7 +402,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, double text_y = height / 2.0 - text_height / 2.0; cairo_set_source_u32(cairo, box_colors.text); cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); - pango_printf(cairo, config->font, 1, true, "%s", name); + pango_printf(cairo, config->font, output->scale, true, "%s", name); struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); hotspot->x = *x; @@ -370,7 +415,7 @@ static uint32_t render_workspace_button(cairo_t *cairo, wl_list_insert(&output->hotspots, &hotspot->link); *x += width; - return height; + return height / output->scale; } static uint32_t render_to_cairo(cairo_t *cairo, @@ -394,7 +439,13 @@ static uint32_t render_to_cairo(cairo_t *cairo, * height is too tall, the render function should adapt its drawing to * utilize the available space. */ - double x = 0; + double x = output->width * output->scale; + if (bar->status) { + uint32_t h = render_status_line(cairo, config, output, + bar->status, output->focused, &x, output->height); + max_height = h > max_height ? h : max_height; + } + x = 0; if (config->workspace_buttons) { struct swaybar_workspace *ws; wl_list_for_each_reverse(ws, &output->workspaces, link) { @@ -404,14 +455,8 @@ static uint32_t render_to_cairo(cairo_t *cairo, } } if (config->binding_mode_indicator && config->mode) { - uint32_t h = render_binding_mode_indicator( - cairo, config, config->mode, x, output->height); - max_height = h > max_height ? h : max_height; - } - x = output->width; - if (bar->status) { - uint32_t h = render_status_line(cairo, config, output, bar->status, - output->focused, &x, output->width, output->height); + uint32_t h = render_binding_mode_indicator(cairo, + output, config, config->mode, x, output->height); max_height = h > max_height ? h : max_height; } @@ -451,7 +496,9 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { } else { // Replay recording into shm and send it off output->current_buffer = get_next_buffer(bar->shm, - output->buffers, output->width, output->height); + output->buffers, + output->width * output->scale, + output->height * output->scale); cairo_t *shm = output->current_buffer->cairo; cairo_save(shm); @@ -462,9 +509,11 @@ void render_frame(struct swaybar *bar, struct swaybar_output *output) { cairo_set_source_surface(shm, recorder, 0.0, 0.0); cairo_paint(shm); + wl_surface_set_buffer_scale(output->surface, output->scale); wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); - wl_surface_damage(output->surface, 0, 0, output->width, output->height); + wl_surface_damage(output->surface, 0, 0, + output->width, output->height); wl_surface_commit(output->surface); wl_display_roundtrip(bar->display); } diff --git a/swaybar/status_line.c b/swaybar/status_line.c index cc7e217f0..8afe47073 100644 --- a/swaybar/status_line.c +++ b/swaybar/status_line.c @@ -59,7 +59,11 @@ bool status_handle_readable(struct status_line *status) { wlr_log(L_DEBUG, "Enabled click events."); status->i3bar_state.click_events = true; const char *events_array = "[\n"; - write(status->write_fd, events_array, strlen(events_array)); + ssize_t len = strlen(events_array); + if (write(status->write_fd, events_array, len) != len) { + status_error(status, + "[failed to write to status command]"); + } } json_object_put(proto); } diff --git a/swaybg/main.c b/swaybg/main.c index 203082f64..c282a7074 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -43,10 +43,12 @@ struct swaybg_state { struct wl_output *output; struct wl_surface *surface; + struct wl_region *input_region; struct zwlr_layer_surface_v1 *layer_surface; bool run_display; uint32_t width, height; + int32_t scale; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; }; @@ -74,52 +76,53 @@ static void render_image(struct swaybg_state *state) { cairo_surface_t *image = state->context.image; double width = cairo_image_surface_get_width(image); double height = cairo_image_surface_get_height(image); - int wwidth = state->width; - int wheight = state->height; + int buffer_width = state->width * state->scale; + int buffer_height = state->height * state->scale; switch (state->args->mode) { case BACKGROUND_MODE_STRETCH: - cairo_scale(cairo, (double)wwidth / width, (double)wheight / height); + cairo_scale(cairo, (double)buffer_width / width, + (double)buffer_height / height); cairo_set_source_surface(cairo, image, 0, 0); break; case BACKGROUND_MODE_FILL: { - double window_ratio = (double)wwidth / wheight; + double window_ratio = (double)buffer_width / buffer_height; double bg_ratio = width / height; if (window_ratio > bg_ratio) { - double scale = (double)wwidth / width; + double scale = (double)buffer_width / width; cairo_scale(cairo, scale, scale); cairo_set_source_surface(cairo, image, - 0, (double)wheight / 2 / scale - height / 2); + 0, (double)buffer_height / 2 / scale - height / 2); } else { - double scale = (double)wheight / height; + double scale = (double)buffer_height / height; cairo_scale(cairo, scale, scale); cairo_set_source_surface(cairo, image, - (double)wwidth / 2 / scale - width / 2, 0); + (double)buffer_width / 2 / scale - width / 2, 0); } break; } case BACKGROUND_MODE_FIT: { - double window_ratio = (double)wwidth / wheight; + double window_ratio = (double)buffer_width / buffer_height; double bg_ratio = width / height; if (window_ratio > bg_ratio) { - double scale = (double)wheight / height; + double scale = (double)buffer_height / height; cairo_scale(cairo, scale, scale); cairo_set_source_surface(cairo, image, - (double)wwidth / 2 / scale - width / 2, 0); + (double)buffer_width / 2 / scale - width / 2, 0); } else { - double scale = (double)wwidth / width; + double scale = (double)buffer_width / width; cairo_scale(cairo, scale, scale); cairo_set_source_surface(cairo, image, - 0, (double)wheight / 2 / scale - height / 2); + 0, (double)buffer_height / 2 / scale - height / 2); } break; } case BACKGROUND_MODE_CENTER: cairo_set_source_surface(cairo, image, - (double)wwidth / 2 - width / 2, - (double)wheight / 2 - height / 2); + (double)buffer_width / 2 - width / 2, + (double)buffer_height / 2 - height / 2); break; case BACKGROUND_MODE_TILE: { cairo_pattern_t *pattern = cairo_pattern_create_for_surface(image); @@ -135,8 +138,8 @@ static void render_image(struct swaybg_state *state) { } static void render_frame(struct swaybg_state *state) { - state->current_buffer = get_next_buffer(state->shm, - state->buffers, state->width, state->height); + state->current_buffer = get_next_buffer(state->shm, state->buffers, + state->width * state->scale, state->height * state->scale); cairo_t *cairo = state->current_buffer->cairo; switch (state->args->mode) { @@ -149,6 +152,7 @@ static void render_frame(struct swaybg_state *state) { break; } + wl_surface_set_buffer_scale(state->surface, state->scale); wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); wl_surface_damage(state->surface, 0, 0, state->width, state->height); wl_surface_commit(state->surface); @@ -204,6 +208,7 @@ static void layer_surface_closed(void *data, struct swaybg_state *state = data; zwlr_layer_surface_v1_destroy(state->layer_surface); wl_surface_destroy(state->surface); + wl_region_destroy(state->input_region); state->run_display = false; } @@ -212,12 +217,42 @@ struct zwlr_layer_surface_v1_listener layer_surface_listener = { .closed = layer_surface_closed, }; +static void output_geometry(void *data, struct wl_output *output, int32_t x, + int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, + const char *make, const char *model, int32_t transform) { + // Who cares +} + +static void output_mode(void *data, struct wl_output *output, uint32_t flags, + int32_t width, int32_t height, int32_t refresh) { + // Who cares +} + +static void output_done(void *data, struct wl_output *output) { + // Who cares +} + +static void output_scale(void *data, struct wl_output *output, int32_t factor) { + struct swaybg_state *state = data; + state->scale = factor; + if (state->run_display) { + render_frame(state); + } +} + +struct wl_output_listener output_listener = { + .geometry = output_geometry, + .mode = output_mode, + .done = output_done, + .scale = output_scale, +}; + static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct swaybg_state *state = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { state->compositor = wl_registry_bind(registry, name, - &wl_compositor_interface, 1); + &wl_compositor_interface, 3); } else if (strcmp(interface, wl_shm_interface.name) == 0) { state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); @@ -225,7 +260,8 @@ static void handle_global(void *data, struct wl_registry *registry, static int output_idx = 0; if (output_idx == state->args->output_idx) { state->output = wl_registry_bind(registry, name, - &wl_output_interface, 1); + &wl_output_interface, 3); + wl_output_add_listener(state->output, &output_listener, state); } output_idx++; } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { @@ -287,8 +323,14 @@ int main(int argc, const char **argv) { wl_display_roundtrip(state.display); assert(state.compositor && state.layer_shell && state.output && state.shm); + // Second roundtrip to get output properties + wl_display_roundtrip(state.display); + assert(state.surface = wl_compositor_create_surface(state.compositor)); + assert(state.input_region = wl_compositor_create_region(state.compositor)); + wl_surface_set_input_region(state.surface, state.input_region); + state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( state.layer_shell, state.surface, state.output, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper");