Implement borders

Implements rendering of borders. Title text is still to do.

Implements the following configuration directives:

* client.focused
* client.focused_inactive
* client.unfocused
* client.urgent
* border
* default_border
master
Ryan Dwyer 7 years ago committed by Drew DeVault
parent bf0603cd2d
commit 630ba30e3c

@ -204,11 +204,11 @@ struct bar_config {
};
struct border_colors {
uint32_t border;
uint32_t background;
uint32_t text;
uint32_t indicator;
uint32_t child_border;
float border[4];
float background[4];
float text[4];
float indicator[4];
float child_border[4];
};
enum edge_border_types {
@ -338,7 +338,7 @@ struct sway_config {
struct border_colors unfocused;
struct border_colors urgent;
struct border_colors placeholder;
uint32_t background;
float background[4];
} border_colors;
// floating view

@ -72,8 +72,8 @@ struct sway_container {
// For C_ROOT, this has no meaning
// For C_OUTPUT, this is the output position in layout coordinates
// For other types, this is the position in output-local coordinates
// Includes borders
double x, y;
// does not include borders or gaps.
double width, height;
double saved_width, saved_height;

@ -41,9 +41,16 @@ struct sway_view {
struct sway_container *swayc; // NULL for unmapped views
struct wlr_surface *surface; // NULL for unmapped views
// Geometry of the view itself (excludes borders)
double x, y;
int width, height;
bool is_fullscreen;
enum sway_container_border border;
int border_thickness;
union {
struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
struct wlr_xwayland_surface *wlr_xwayland_surface;
@ -160,6 +167,8 @@ const char *view_get_instance(struct sway_view *view);
void view_configure(struct sway_view *view, double ox, double oy, int width,
int height);
void view_autoconfigure(struct sway_view *view);
void view_set_activated(struct sway_view *view, bool activated);
void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen);
@ -184,8 +193,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface);
void view_unmap(struct sway_view *view);
void view_update_position(struct sway_view *view, double ox, double oy);
void view_update_size(struct sway_view *view, int width, int height);
void view_child_init(struct sway_view_child *child,

@ -97,8 +97,13 @@ static struct cmd_handler handlers[] = {
{ "bar", cmd_bar },
{ "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym },
{ "client.focused", cmd_client_focused },
{ "client.focused_inactive", cmd_client_focused_inactive },
{ "client.unfocused", cmd_client_unfocused },
{ "client.urgent", cmd_client_urgent },
{ "exec", cmd_exec },
{ "exec_always", cmd_exec_always },
{ "default_border", cmd_default_border },
{ "focus_follows_mouse", cmd_focus_follows_mouse },
{ "for_window", cmd_for_window },
{ "fullscreen", cmd_fullscreen },
@ -162,6 +167,7 @@ static struct cmd_handler config_handlers[] = {
/* Runtime-only commands. Keep alphabetized */
static struct cmd_handler command_handlers[] = {
{ "border", cmd_border },
{ "exit", cmd_exit },
{ "focus", cmd_focus },
{ "kill", cmd_kill },

@ -0,0 +1,41 @@
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/container.h"
#include "sway/tree/view.h"
struct cmd_results *cmd_border(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct sway_container *container =
config->handler_context.current_container;
if (container->type != C_VIEW) {
return cmd_results_new(CMD_INVALID, "border",
"Only views can have borders");
}
struct sway_view *view = container->sway_view;
if (strcmp(argv[0], "none") == 0) {
view->border = B_NONE;
} else if (strcmp(argv[0], "normal") == 0) {
view->border = B_NORMAL;
} else if (strcmp(argv[0], "pixel") == 0) {
view->border = B_PIXEL;
if (argc == 2) {
view->border_thickness = atoi(argv[1]);
}
} else if (strcmp(argv[0], "toggle") == 0) {
view->border = (view->border + 1) % 3;
} else {
return cmd_results_new(CMD_INVALID, "border",
"Expected 'border <none|normal|pixel|toggle>' "
"or 'border pixel <px>'");
}
view_autoconfigure(view);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

@ -0,0 +1,79 @@
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/container.h"
static bool parse_color(char *hexstring, float (*dest)[4]) {
if (hexstring[0] != '#') {
return false;
}
if (strlen(hexstring) != 7) {
return false;
}
++hexstring;
char *end;
uint32_t decimal = strtol(hexstring, &end, 16);
if (*end != '\0') {
return false;
}
(*dest)[0] = ((decimal >> 16) & 0xff) / 255.0;
(*dest)[1] = ((decimal >> 8) & 0xff) / 255.0;
(*dest)[2] = (decimal & 0xff) / 255.0;
(*dest)[3] = 1.0;
return true;
}
static struct cmd_results *handle_command(int argc, char **argv,
struct border_colors *class, char *cmd_name) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, cmd_name, EXPECTED_EQUAL_TO, 5))) {
return error;
}
if (!parse_color(argv[0], &class->border)) {
return cmd_results_new(CMD_INVALID, cmd_name,
"Unable to parse border color");
}
if (!parse_color(argv[1], &class->background)) {
return cmd_results_new(CMD_INVALID, cmd_name,
"Unable to parse background color");
}
if (!parse_color(argv[2], &class->text)) {
return cmd_results_new(CMD_INVALID, cmd_name,
"Unable to parse text color");
}
if (!parse_color(argv[3], &class->indicator)) {
return cmd_results_new(CMD_INVALID, cmd_name,
"Unable to parse indicator color");
}
if (!parse_color(argv[4], &class->child_border)) {
return cmd_results_new(CMD_INVALID, cmd_name,
"Unable to parse child border color");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
struct cmd_results *cmd_client_focused(int argc, char **argv) {
return handle_command(argc, argv, &config->border_colors.focused, "client.focused");
}
struct cmd_results *cmd_client_focused_inactive(int argc, char **argv) {
return handle_command(argc, argv, &config->border_colors.focused_inactive, "client.focused_inactive");
}
struct cmd_results *cmd_client_unfocused(int argc, char **argv) {
return handle_command(argc, argv, &config->border_colors.unfocused, "client.unfocused");
}
struct cmd_results *cmd_client_urgent(int argc, char **argv) {
return handle_command(argc, argv, &config->border_colors.urgent, "client.urgent");
}

@ -0,0 +1,27 @@
#include "log.h"
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/tree/container.h"
struct cmd_results *cmd_default_border(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "default_border", EXPECTED_AT_LEAST, 1))) {
return error;
}
if (strcmp(argv[0], "none") == 0) {
config->border = B_NONE;
} else if (strcmp(argv[0], "normal") == 0) {
config->border = B_NORMAL;
} else if (strcmp(argv[0], "pixel") == 0) {
config->border = B_PIXEL;
if (argc == 2) {
config->border_thickness = atoi(argv[1]);
}
} else {
return cmd_results_new(CMD_INVALID, "default_border",
"Expected 'default_border <none|normal|pixel>' or 'default_border pixel <px>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}

@ -130,6 +130,13 @@ static void destroy_removed_seats(struct sway_config *old_config,
}
}
static void set_color(float (*dest)[4], uint32_t color) {
(*dest)[0] = ((color >> 16) & 0xff) / 255.0;
(*dest)[1] = ((color >> 8) & 0xff) / 255.0;
(*dest)[2] = (color & 0xff) / 255.0;
(*dest)[3] = 1.0;
}
static void config_defaults(struct sway_config *config) {
if (!(config->symbols = create_list())) goto cleanup;
if (!(config->modes = create_list())) goto cleanup;
@ -162,7 +169,6 @@ static void config_defaults(struct sway_config *config) {
config->default_layout = L_NONE;
config->default_orientation = L_NONE;
if (!(config->font = strdup("monospace 10"))) goto cleanup;
// TODO: border
//config->font_height = get_font_text_height(config->font);
// floating view
@ -199,37 +205,38 @@ static void config_defaults(struct sway_config *config) {
config->hide_edge_borders = E_NONE;
// border colors
config->border_colors.focused.border = 0x4C7899FF;
config->border_colors.focused.background = 0x285577FF;
config->border_colors.focused.text = 0xFFFFFFFF;
config->border_colors.focused.indicator = 0x2E9EF4FF;
config->border_colors.focused.child_border = 0x285577FF;
config->border_colors.focused_inactive.border = 0x333333FF;
config->border_colors.focused_inactive.background = 0x5F676AFF;
config->border_colors.focused_inactive.text = 0xFFFFFFFF;
config->border_colors.focused_inactive.indicator = 0x484E50FF;
config->border_colors.focused_inactive.child_border = 0x5F676AFF;
config->border_colors.unfocused.border = 0x333333FF;
config->border_colors.unfocused.background = 0x222222FF;
config->border_colors.unfocused.text = 0x888888FF;
config->border_colors.unfocused.indicator = 0x292D2EFF;
config->border_colors.unfocused.child_border = 0x222222FF;
config->border_colors.urgent.border = 0x2F343AFF;
config->border_colors.urgent.background = 0x900000FF;
config->border_colors.urgent.text = 0xFFFFFFFF;
config->border_colors.urgent.indicator = 0x900000FF;
config->border_colors.urgent.child_border = 0x900000FF;
config->border_colors.placeholder.border = 0x000000FF;
config->border_colors.placeholder.background = 0x0C0C0CFF;
config->border_colors.placeholder.text = 0xFFFFFFFF;
config->border_colors.placeholder.indicator = 0x000000FF;
config->border_colors.placeholder.child_border = 0x0C0C0CFF;
config->border_colors.background = 0xFFFFFFFF;
set_color(&config->border_colors.focused.border, 0x4C7899);
set_color(&config->border_colors.focused.border, 0x4C7899);
set_color(&config->border_colors.focused.background, 0x285577);
set_color(&config->border_colors.focused.text, 0xFFFFFF);
set_color(&config->border_colors.focused.indicator, 0x2E9EF4);
set_color(&config->border_colors.focused.child_border, 0x285577);
set_color(&config->border_colors.focused_inactive.border, 0x333333);
set_color(&config->border_colors.focused_inactive.background, 0x5F676A);
set_color(&config->border_colors.focused_inactive.text, 0xFFFFFF);
set_color(&config->border_colors.focused_inactive.indicator, 0x484E50);
set_color(&config->border_colors.focused_inactive.child_border, 0x5F676A);
set_color(&config->border_colors.unfocused.border, 0x333333);
set_color(&config->border_colors.unfocused.background, 0x222222);
set_color(&config->border_colors.unfocused.text, 0x888888);
set_color(&config->border_colors.unfocused.indicator, 0x292D2E);
set_color(&config->border_colors.unfocused.child_border, 0x222222);
set_color(&config->border_colors.urgent.border, 0x2F343A);
set_color(&config->border_colors.urgent.background, 0x900000);
set_color(&config->border_colors.urgent.text, 0xFFFFFF);
set_color(&config->border_colors.urgent.indicator, 0x900000);
set_color(&config->border_colors.urgent.child_border, 0x900000);
set_color(&config->border_colors.placeholder.border, 0x000000);
set_color(&config->border_colors.placeholder.background, 0x0C0C0C);
set_color(&config->border_colors.placeholder.text, 0xFFFFFF);
set_color(&config->border_colors.placeholder.indicator, 0x000000);
set_color(&config->border_colors.placeholder.child_border, 0x0C0C0C);
set_color(&config->border_colors.background, 0xFFFFFF);
// Security
if (!(config->command_policies = create_list())) goto cleanup;

@ -116,8 +116,8 @@ static void surface_for_each_surface(struct wlr_surface *surface,
static void output_view_for_each_surface(struct sway_view *view,
struct root_geometry *geo, wlr_surface_iterator_func_t iterator,
void *user_data) {
geo->x = view->swayc->x;
geo->y = view->swayc->y;
geo->x = view->x;
geo->y = view->y;
geo->width = view->surface->current->width;
geo->height = view->surface->current->height;
geo->rotation = 0; // TODO
@ -217,23 +217,198 @@ static void render_unmanaged(struct sway_output *output,
render_surface_iterator, &data);
}
static void render_container_iterator(struct sway_container *con,
void *_data) {
struct sway_output *output = _data;
if (!sway_assert(con->type == C_VIEW, "expected a view")) {
return;
static void render_view(struct sway_view *view, struct sway_output *output) {
struct render_data data = { .output = output, .alpha = view->swayc->alpha };
output_view_for_each_surface(
view, &data.root_geo, render_surface_iterator, &data);
}
/**
* Render decorations for a view with "border normal".
*/
static void render_container_simple_border_normal(struct sway_output *output,
struct sway_container *con, struct border_colors *colors) {
struct wlr_renderer *renderer =
wlr_backend_get_renderer(output->wlr_output->backend);
struct wlr_box box;
float color[4];
color[3] = con->alpha;
// Child border - left edge
memcpy(&color, colors->child_border, sizeof(float) * 3);
box.x = con->x;
box.y = con->y + 1;
box.width = con->sway_view->border_thickness;
box.height = con->height - 1;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Child border - right edge
box.x = con->x + con->width - con->sway_view->border_thickness;
box.y = con->y + 1;
box.width = con->sway_view->border_thickness;
box.height = con->height - 1;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Child border - bottom edge
box.x = con->x;
box.y = con->y + con->height - con->sway_view->border_thickness;
box.width = con->width;
box.height = con->sway_view->border_thickness;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Single pixel bar above title
memcpy(&color, colors->border, sizeof(float) * 3);
box.x = con->x;
box.y = con->y;
box.width = con->width;
box.height = 1;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Single pixel bar below title
box.x = con->x + con->sway_view->border_thickness;
box.y = con->sway_view->y - 1;
box.width = con->width - con->sway_view->border_thickness * 2;
box.height = 1;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Title background
memcpy(&color, colors->background, sizeof(float) * 3);
box.x = con->x + con->sway_view->border_thickness;
box.y = con->y + 1;
box.width = con->width - con->sway_view->border_thickness * 2;
box.height = con->sway_view->y - con->y - 2;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Title text
// TODO
}
/**
* Render decorations for a view with "border pixel".
*/
static void render_container_simple_border_pixel(struct sway_output *output,
struct sway_container *con, struct border_colors *colors) {
struct wlr_renderer *renderer =
wlr_backend_get_renderer(output->wlr_output->backend);
struct wlr_box box;
float color[4];
color[3] = con->alpha;
// Child border - left edge
memcpy(&color, colors->child_border, sizeof(float) * 3);
box.x = con->x;
box.y = con->y;
box.width = con->sway_view->border_thickness;
box.height = con->height;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Child border - right edge
box.x = con->x + con->width - con->sway_view->border_thickness;
box.y = con->y;
box.width = con->sway_view->border_thickness;
box.height = con->height;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Child border - top edge
box.x = con->x;
box.y = con->y;
box.width = con->width;
box.height = con->sway_view->border_thickness;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
// Child border - bottom edge
box.x = con->x;
box.y = con->y + con->height - con->sway_view->border_thickness;
box.width = con->width;
box.height = con->sway_view->border_thickness;
wlr_render_rect(renderer, &box, color,
output->wlr_output->transform_matrix);
}
static void render_container(struct sway_output *output,
struct sway_container *con);
/**
* Render a container's children using a L_HORIZ or L_VERT layout.
*
* Wrap child views in borders and leave child containers borderless because
* they'll apply their own borders to their children.
*/
static void render_container_simple(struct sway_output *output,
struct sway_container *con) {
struct sway_seat *seat = input_manager_current_seat(input_manager);
struct sway_container *focus = seat_get_focus(seat);
for (int i = 0; i < con->children->length; ++i) {
struct sway_container *child = con->children->items[i];
if (child->type == C_VIEW) {
if (child->sway_view->border != B_NONE) {
struct border_colors *colors;
if (focus == child) {
colors = &config->border_colors.focused;
} else if (seat_get_focus_inactive(seat, con) == child) {
colors = &config->border_colors.focused_inactive;
} else {
colors = &config->border_colors.unfocused;
}
if (child->sway_view->border == B_NORMAL) {
render_container_simple_border_normal(output, child,
colors);
} else {
render_container_simple_border_pixel(output, child, colors);
}
}
render_view(child->sway_view, output);
} else {
render_container(output, child);
}
}
struct render_data data = { .output = output, .alpha = con->alpha };
output_view_for_each_surface(con->sway_view, &data.root_geo,
render_surface_iterator, &data);
}
/**
* Render a container's children using the L_TABBED layout.
*/
static void render_container_tabbed(struct sway_output *output,
struct sway_container *con) {
// TODO
}
/**
* Render a container's children using the L_STACKED layout.
*/
static void render_container_stacked(struct sway_output *output,
struct sway_container *con) {
// TODO
}
static void render_container(struct sway_output *output,
struct sway_container *con) {
if (con->type == C_VIEW) { // Happens if a view is fullscreened
render_container_iterator(con, output);
} else {
container_descendants(con, C_VIEW, render_container_iterator, output);
switch (con->layout) {
case L_NONE:
case L_HORIZ:
case L_VERT:
render_container_simple(output, con);
break;
case L_STACKED:
render_container_stacked(output, con);
break;
case L_TABBED:
render_container_tabbed(output, con);
break;
case L_FLOATING:
// TODO
break;
}
}
@ -282,7 +457,7 @@ static void render_output(struct sway_output *output, struct timespec *when,
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
wlr_renderer_clear(renderer, clear_color);
// TODO: handle views smaller than the output
render_container(output, workspace->sway_workspace->fullscreen->swayc);
render_view(workspace->sway_workspace->fullscreen, output);
if (workspace->sway_workspace->fullscreen->type == SWAY_VIEW_XWAYLAND) {
render_unmanaged(output,

@ -40,7 +40,6 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
if (wl_shell_view == NULL) {
return;
}
view_update_position(view, ox, oy);
wl_shell_view->pending_width = width;
wl_shell_view->pending_height = height;
wlr_wl_shell_surface_configure(view->wlr_wl_shell_surface, 0, width, height);

@ -102,7 +102,6 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
return;
}
view_update_position(view, ox, oy);
xdg_shell_v6_view->pending_width = width;
xdg_shell_v6_view->pending_height = height;
wlr_xdg_toplevel_v6_set_size(view->wlr_xdg_surface_v6, width, height);

@ -163,8 +163,6 @@ static void configure(struct sway_view *view, double ox, double oy, int width,
return;
}
view_update_position(view, ox, oy);
xwayland_view->pending_width = width;
xwayland_view->pending_height = height;
wlr_xwayland_surface_configure(xsurface, ox + loutput->x, oy + loutput->y,

@ -29,6 +29,9 @@ sway_sources = files(
'commands/assign.c',
'commands/bar.c',
'commands/bind.c',
'commands/border.c',
'commands/client.c',
'commands/default_border.c',
'commands/default_orientation.c',
'commands/exit.c',
'commands/exec.c',

@ -174,12 +174,7 @@ void arrange_children_of(struct sway_container *parent) {
}
if (workspace->sway_workspace->fullscreen) {
// Just arrange the fullscreen view and jump out
struct sway_container *view =
workspace->sway_workspace->fullscreen->swayc;
view_configure(view->sway_view, 0, 0,
workspace->parent->width, workspace->parent->height);
view->width = workspace->parent->width;
view->height = workspace->parent->height;
view_autoconfigure(workspace->sway_workspace->fullscreen);
return;
}
@ -204,8 +199,7 @@ void arrange_children_of(struct sway_container *parent) {
for (int i = 0; i < parent->children->length; ++i) {
struct sway_container *child = parent->children->items[i];
if (child->type == C_VIEW) {
view_configure(child->sway_view, child->x, child->y,
child->width, child->height);
view_autoconfigure(child->sway_view);
} else {
arrange_children_of(child);
}

@ -1,5 +1,6 @@
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_output_layout.h>
#include "log.h"
#include "sway/criteria.h"
@ -73,6 +74,51 @@ void view_configure(struct sway_view *view, double ox, double oy, int width,
}
}
/**
* Configure the view's position and size based on the swayc's position and
* size, taking borders into consideration.
*/
void view_autoconfigure(struct sway_view *view) {
if (!sway_assert(view->swayc,
"Called view_autoconfigure() on a view without a swayc")) {
return;
}
if (view->is_fullscreen) {
struct sway_container *output = container_parent(view->swayc, C_OUTPUT);
view_configure(view, 0, 0, output->width, output->height);
view->x = view->y = 0;
return;
}
double x, y, width, height;
switch (view->border) {
case B_NONE:
x = view->swayc->x;
y = view->swayc->y;
width = view->swayc->width;
height = view->swayc->height;
break;
case B_PIXEL:
x = view->swayc->x + view->border_thickness;
y = view->swayc->y + view->border_thickness;
width = view->swayc->width - view->border_thickness * 2;
height = view->swayc->height - view->border_thickness * 2;
break;
case B_NORMAL:
// TODO: Size the title bar by checking the font
x = view->swayc->x + view->border_thickness;
y = view->swayc->y + 20;
width = view->swayc->width - view->border_thickness * 2;
height = view->swayc->height - view->border_thickness - 20;
break;
}
view->x = x;
view->y = y;
view_configure(view, x, y, width, height);
}
void view_set_activated(struct sway_view *view, bool activated) {
if (view->impl->set_activated) {
view->impl->set_activated(view, activated);
@ -262,6 +308,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
view->surface = wlr_surface;
view->swayc = cont;
view->border = config->border;
view->border_thickness = config->border_thickness;
view_init_subsurfaces(view, wlr_surface);
wl_signal_add(&wlr_surface->events.new_subsurface,
@ -309,23 +357,13 @@ void view_unmap(struct sway_view *view) {
}
}
void view_update_position(struct sway_view *view, double ox, double oy) {
if (view->swayc->x == ox && view->swayc->y == oy) {
return;
}
view_damage(view, true);
view->swayc->x = ox;
view->swayc->y = oy;
view_damage(view, true);
}
void view_update_size(struct sway_view *view, int width, int height) {
if (view->width == width && view->height == height) {
return;
}
view_damage(view, true);
// Should we update the swayc width/height here too?
view->width = width;
view->height = height;
view_damage(view, true);

Loading…
Cancel
Save