Merge pull request #551 from mikkeloscar/window-borders

Implement Window borders
This commit is contained in:
Drew DeVault 2016-03-30 07:41:13 -04:00
commit 4bf10d7771
16 changed files with 655 additions and 47 deletions

11
include/border.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef _SWAY_BORDER_H
#define _SWAY_BORDER_H
#include <wlc/wlc.h>
#include "container.h"
void render_view_borders(wlc_handle view);
void update_view_border(swayc_t *view);
void map_update_view_border(swayc_t *view, void *data);
int get_font_text_height(const char *font);
#endif

View file

@ -1,12 +1,12 @@
#ifndef _SWAY_CLIENT_PANGO_H #ifndef _SWAY_CLIENT_PANGO_H
#define _SWAY_CLIENT_PANGO_H #define _SWAY_CLIENT_PANGO_H
#include "client/window.h" #include <cairo/cairo.h>
#include "client/buffer.h" #include <pango/pangocairo.h>
#include <stdarg.h> #include <stdarg.h>
PangoLayout *get_pango_layout(struct window *window, struct buffer *buffer, const char *text); PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text);
void get_text_size(struct window *window, int *width, int *height, const char *fmt, ...); void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, const char *fmt, ...);
void pango_printf(struct window *window, const char *fmt, ...); void pango_printf(cairo_t *cairo, const char *font, const char *fmt, ...);
#endif #endif

View file

@ -148,6 +148,21 @@ struct bar_config {
} colors; } colors;
}; };
struct border_colors {
uint32_t border;
uint32_t background;
uint32_t text;
uint32_t indicator;
uint32_t child_border;
};
enum edge_border_types {
E_NONE, /**< Don't hide edge borders */
E_VERTICAL, /**< hide vertical edge borders */
E_HORIZONTAL, /**< hide horizontal edge borders */
E_BOTH /**< hide vertical and horizontal edge borders */
};
/** /**
* The configuration struct. The result of loading a config file. * The configuration struct. The result of loading a config file.
*/ */
@ -169,6 +184,7 @@ struct sway_config {
enum swayc_layouts default_orientation; enum swayc_layouts default_orientation;
enum swayc_layouts default_layout; enum swayc_layouts default_layout;
char *font; char *font;
int font_height;
// Flags // Flags
bool focus_follows_mouse; bool focus_follows_mouse;
@ -187,6 +203,20 @@ struct sway_config {
list_t *config_chain; list_t *config_chain;
const char *current_config; const char *current_config;
enum swayc_border_types border;
int border_thickness;
enum edge_border_types hide_edge_borders;
// border colors
struct {
struct border_colors focused;
struct border_colors focused_inactive;
struct border_colors unfocused;
struct border_colors urgent;
struct border_colors placeholder;
uint32_t background;
} border_colors;
}; };
/** /**

View file

@ -36,6 +36,12 @@ enum swayc_layouts {
L_LAYOUTS, L_LAYOUTS,
}; };
enum swayc_border_types {
B_NONE, /**< No border */
B_PIXEL, /**< 1px border */
B_NORMAL /**< Normal border with title bar */
};
/** /**
* Stores information about a container. * Stores information about a container.
* *
@ -109,6 +115,16 @@ struct sway_container {
* If this container's children include a fullscreen view, this is that view. * If this container's children include a fullscreen view, this is that view.
*/ */
struct sway_container *fullscreen; struct sway_container *fullscreen;
/**
* If this container is a view, this may be set to the window's decoration
* buffer (or NULL).
*/
unsigned char *border;
enum swayc_border_types border_type;
struct wlc_geometry border_geometry;
struct wlc_geometry title_bar_geometry;
struct wlc_geometry actual_geometry;
int border_thickness;
}; };
enum visibility_mask { enum visibility_mask {

View file

@ -5,6 +5,8 @@ include_directories(
${JSONC_INCLUDE_DIRS} ${JSONC_INCLUDE_DIRS}
${XKBCOMMON_INCLUDE_DIRS} ${XKBCOMMON_INCLUDE_DIRS}
${LIBINPUT_INCLUDE_DIRS} ${LIBINPUT_INCLUDE_DIRS}
${CAIRO_INCLUDE_DIRS}
${PANGO_INCLUDE_DIRS}
) )
add_executable(sway add_executable(sway
@ -24,6 +26,7 @@ add_executable(sway
output.c output.c
resize.c resize.c
workspace.c workspace.c
border.c
) )
add_definitions( add_definitions(
@ -33,12 +36,15 @@ add_definitions(
target_link_libraries(sway target_link_libraries(sway
sway-common sway-common
sway-protocols sway-protocols
sway-wayland
${WLC_LIBRARIES} ${WLC_LIBRARIES}
${XKBCOMMON_LIBRARIES} ${XKBCOMMON_LIBRARIES}
${PCRE_LIBRARIES} ${PCRE_LIBRARIES}
${JSONC_LIBRARIES} ${JSONC_LIBRARIES}
${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES}
${LIBINPUT_LIBRARIES} ${LIBINPUT_LIBRARIES}
${PANGO_LIBRARIES}
${JSONC_LIBRARIES}
m m
) )

266
sway/border.c Normal file
View file

@ -0,0 +1,266 @@
#include "border.h"
#include <wlc/wlc-render.h>
#include <cairo/cairo.h>
#include <pango/pangocairo.h>
#include <stdlib.h>
#include <stdio.h>
#include "container.h"
#include "config.h"
#include "client/pango.h"
void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
int endian = 1;
if (*(char *)&endian == 1) { // little endian
cairo_set_source_rgba(cairo,
(color >> (1*8) & 0xFF) / 255.0,
(color >> (2*8) & 0xFF) / 255.0,
(color >> (3*8) & 0xFF) / 255.0,
(color >> (0*8) & 0xFF) / 255.0);
} else { // big endian
cairo_set_source_rgba(cairo,
(color >> (0*8) & 0xFF) / 255.0,
(color >> (3*8) & 0xFF) / 255.0,
(color >> (2*8) & 0xFF) / 255.0,
(color >> (1*8) & 0xFF) / 255.0);
}
}
static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, cairo_surface_t **surface) {
cairo_t *cr;
view->border_geometry = geo;
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, geo.size.w);
view->border = calloc(stride * geo.size.h, sizeof(unsigned char));
if (!view->border) {
sway_log(L_DEBUG, "Unable to allocate buffer");
return NULL;
}
*surface = cairo_image_surface_create_for_data(view->border,
CAIRO_FORMAT_ARGB32, geo.size.w, geo.size.h, stride);
if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
free(view->border);
view->border = NULL;
sway_log(L_DEBUG, "Unable to allocate surface");
return NULL;
}
cr = cairo_create(*surface);
if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
cairo_surface_destroy(*surface);
free(view->border);
view->border = NULL;
sway_log(L_DEBUG, "Unable to create cairo context");
return NULL;
}
return cr;
}
// TODO: move to client/cairo.h when local set_source_u32 is fixed.
/**
* Renders a sharp line of any width and height.
*
* The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
* if the line has a width/height of one pixel, respectively.
*/
static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
cairo_set_source_u32(cairo, color);
if (width > 1 && height > 1) {
cairo_rectangle(cairo, x, y, width, height);
cairo_fill(cairo);
} else {
if (width == 1) {
x += 0.5;
height += y;
width = x;
}
if (height == 1) {
y += 0.5;
width += x;
height = y;
}
cairo_move_to(cairo, x, y);
cairo_set_line_width(cairo, 1.0);
cairo_line_to(cairo, width, height);
cairo_stroke(cairo);
}
}
int get_font_text_height(const char *font) {
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
cairo_t *cr = cairo_create(surface);
int width, height;
get_text_size(cr, font, &width, &height, "Gg");
return height;
}
static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors) {
struct wlc_geometry *b = &view->border_geometry;
struct wlc_geometry *v = &view->actual_geometry;
// left border
int left_border = v->origin.x - b->origin.x;
if (left_border > 0) {
render_sharp_line(cr,
colors->child_border,
0, 0,
left_border,
b->size.h);
}
// right border
int right_border = b->size.w - v->size.w - left_border;
if (right_border > 0) {
render_sharp_line(cr,
colors->child_border,
b->size.w - right_border,
0,
right_border,
b->size.h);
}
// top border
int top_border = v->origin.y - b->origin.y;
if (top_border > 0) {
render_sharp_line(cr,
colors->child_border,
0, 0,
b->size.w,
top_border);
}
// bottom border
int bottom_border = b->size.h - (top_border + v->size.h);
if (bottom_border > 0) {
render_sharp_line(cr,
colors->child_border,
0,
b->size.h - bottom_border,
b->size.w,
bottom_border);
}
}
static void render_with_title_bar(swayc_t *view, cairo_t *cr, struct border_colors *colors) {
struct wlc_geometry *tb = &view->title_bar_geometry;
struct wlc_geometry *b = &view->border_geometry;
// borders
render_borders(view, cr, colors);
// title bar background
cairo_set_source_u32(cr, colors->child_border);
cairo_rectangle(cr, 0, 0, tb->size.w, tb->size.h);
cairo_fill(cr);
// header top line
render_sharp_line(cr, colors->border, 0, 0, tb->size.w, 1);
// text
if (view->name) {
int width, height;
get_text_size(cr, config->font, &width, &height, "%s", view->name);
cairo_move_to(cr, view->border_thickness, 2);
cairo_set_source_u32(cr, colors->text);
pango_printf(cr, config->font, "%s", view->name);
}
// header bottom line
render_sharp_line(cr, colors->border,
view->actual_geometry.origin.x - b->origin.x,
tb->size.h - 1,
view->actual_geometry.size.w, 1);
}
void map_update_view_border(swayc_t *view, void *data) {
if (view->type == C_VIEW) {
update_view_border(view);
}
}
void update_view_border(swayc_t *view) {
cairo_t *cr = NULL;
cairo_surface_t *surface = NULL;
if (view->border) {
free(view->border);
view->border = NULL;
}
swayc_t *focused = get_focused_view(&root_container);
swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
swayc_t *focused_inactive = NULL;
if (container) {
focused_inactive = swayc_focus_by_type(container, C_VIEW);
} else {
container = swayc_parent_by_type(view, C_WORKSPACE);
if (container) {
focused_inactive = swayc_focus_by_type(container, C_VIEW);
}
}
switch (view->border_type) {
case B_NONE:
break;
case B_PIXEL:
cr = create_border_buffer(view, view->border_geometry, &surface);
if (!cr) {
break;
}
if (focused == view) {
render_borders(view, cr, &config->border_colors.focused);
} else if (focused_inactive == view) {
render_borders(view, cr, &config->border_colors.focused_inactive);
} else {
render_borders(view, cr, &config->border_colors.unfocused);
}
break;
case B_NORMAL:
cr = create_border_buffer(view, view->border_geometry, &surface);
if (!cr) {
break;
}
if (focused == view) {
render_with_title_bar(view, cr, &config->border_colors.focused);
} else if (focused_inactive == view) {
render_with_title_bar(view, cr, &config->border_colors.focused_inactive);
} else {
render_with_title_bar(view, cr, &config->border_colors.unfocused);
}
break;
}
if (surface) {
cairo_surface_flush(surface);
cairo_surface_destroy(surface);
}
if (cr) {
cairo_destroy(cr);
}
}
void render_view_borders(wlc_handle view) {
swayc_t *c = swayc_by_handle(view);
if (!c || c->border_type == B_NONE) {
return;
}
if (c->border_type == B_NORMAL) {
// update window title
const char *new_name = wlc_view_get_title(view);
if (new_name && strcmp(c->name, new_name) != 0) {
free(c->name);
c->name = strdup(new_name);
update_view_border(c);
}
}
if (c->border) {
wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border);
}
}

View file

@ -31,6 +31,7 @@
#include "ipc-server.h" #include "ipc-server.h"
#include "list.h" #include "list.h"
#include "input.h" #include "input.h"
#include "border.h"
typedef struct cmd_results *sway_cmd(int argc, char **argv); typedef struct cmd_results *sway_cmd(int argc, char **argv);
@ -43,6 +44,7 @@ static sway_cmd cmd_assign;
static sway_cmd cmd_bar; static sway_cmd cmd_bar;
static sway_cmd cmd_bindcode; static sway_cmd cmd_bindcode;
static sway_cmd cmd_bindsym; static sway_cmd cmd_bindsym;
static sway_cmd cmd_border;
static sway_cmd cmd_debuglog; static sway_cmd cmd_debuglog;
static sway_cmd cmd_exec; static sway_cmd cmd_exec;
static sway_cmd cmd_exec_always; static sway_cmd cmd_exec_always;
@ -55,6 +57,7 @@ static sway_cmd cmd_font;
static sway_cmd cmd_for_window; static sway_cmd cmd_for_window;
static sway_cmd cmd_fullscreen; static sway_cmd cmd_fullscreen;
static sway_cmd cmd_gaps; static sway_cmd cmd_gaps;
static sway_cmd cmd_hide_edge_borders;
static sway_cmd cmd_include; static sway_cmd cmd_include;
static sway_cmd cmd_input; static sway_cmd cmd_input;
static sway_cmd cmd_kill; static sway_cmd cmd_kill;
@ -345,6 +348,71 @@ static struct cmd_results *cmd_bindcode(int argc, char **argv) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static 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;
}
if (argc > 2) {
return cmd_results_new(CMD_FAILURE, "border",
"Expected 'border <normal|pixel|none|toggle> [<n>]");
}
enum swayc_border_types border = config->border;
int thickness = config->border_thickness;
swayc_t *view = NULL;
if (config->active) {
view = get_focused_view(&root_container);
border = view->border_type;
thickness = view->border_thickness;
}
if (strcasecmp(argv[0], "none") == 0) {
border = B_NONE;
} else if (strcasecmp(argv[0], "normal") == 0) {
border = B_NORMAL;
} else if (strcasecmp(argv[0], "pixel") == 0) {
border = B_PIXEL;
} else if (strcasecmp(argv[0], "toggle") == 0) {
switch (border) {
case B_NONE:
border = B_PIXEL;
break;
case B_NORMAL:
border = B_NONE;
break;
case B_PIXEL:
border = B_NORMAL;
break;
}
} else {
return cmd_results_new(CMD_FAILURE, "border",
"Expected 'border <normal|pixel|none|toggle>");
}
if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) {
thickness = (int)strtol(argv[1], NULL, 10);
if (errno == ERANGE || thickness < 0) {
errno = 0;
return cmd_results_new(CMD_INVALID, "border", "Number is out out of range.");
}
}
if (config->active && view) {
view->border_type = border;
view->border_thickness = thickness;
update_geometry(view);
} else {
config->border = border;
config->border_thickness = thickness;
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
static struct cmd_results *cmd_exec_always(int argc, char **argv) { static struct cmd_results *cmd_exec_always(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL); if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL);
@ -847,9 +915,9 @@ static struct cmd_results *cmd_move(int argc, char **argv) {
} else if (strcasecmp(argv[0], "position") == 0 && strcasecmp(argv[1], "mouse") == 0) { } else if (strcasecmp(argv[0], "position") == 0 && strcasecmp(argv[1], "mouse") == 0) {
if (view->is_floating) { if (view->is_floating) {
swayc_t *output = swayc_parent_by_type(view, C_OUTPUT); swayc_t *output = swayc_parent_by_type(view, C_OUTPUT);
const struct wlc_geometry *geometry = wlc_view_get_geometry(view->handle); struct wlc_geometry g;
wlc_view_get_visible_geometry(view->handle, &g);
const struct wlc_size *size = wlc_output_get_resolution(output->handle); const struct wlc_size *size = wlc_output_get_resolution(output->handle);
struct wlc_geometry g = *geometry;
struct wlc_point origin; struct wlc_point origin;
wlc_pointer_get_position(&origin); wlc_pointer_get_position(&origin);
@ -1500,6 +1568,29 @@ static struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
return cmd_results_new(CMD_SUCCESS, NULL, NULL); return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
static struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) {
return error;
}
if (strcasecmp(argv[0], "none") == 0) {
config->hide_edge_borders = E_NONE;
} else if (strcasecmp(argv[0], "vertical") == 0) {
config->hide_edge_borders = E_VERTICAL;
} else if (strcasecmp(argv[0], "horizontal") == 0) {
config->hide_edge_borders = E_HORIZONTAL;
} else if (strcasecmp(argv[0], "both") == 0) {
config->hide_edge_borders = E_BOTH;
} else {
return cmd_results_new(CMD_INVALID, "hide_edge_borders",
"Expected 'hide_edge_borders <none|vertical|horizontal|both>'");
}
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
}
static struct cmd_results *cmd_kill(int argc, char **argv) { static struct cmd_results *cmd_kill(int argc, char **argv) {
if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file."); if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running."); if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
@ -1866,16 +1957,18 @@ static struct cmd_results *cmd_font(int argc, char **argv) {
} }
char *font = join_args(argv, argc); char *font = join_args(argv, argc);
if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
free(config->font); free(config->font);
config->font = font; if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
sway_log(L_DEBUG, "Settings font %s", config->font); config->font = strdup(font + 6);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} else {
free(font); free(font);
return cmd_results_new(CMD_FAILURE, "font", "non-pango font detected"); } else {
config->font = font;
} }
config->font_height = get_font_text_height(config->font);
sway_log(L_DEBUG, "Settings font %s", config->font);
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
} }
@ -2044,6 +2137,7 @@ static struct cmd_handler handlers[] = {
{ "bar", cmd_bar }, { "bar", cmd_bar },
{ "bindcode", cmd_bindcode }, { "bindcode", cmd_bindcode },
{ "bindsym", cmd_bindsym }, { "bindsym", cmd_bindsym },
{ "border", cmd_border },
{ "debuglog", cmd_debuglog }, { "debuglog", cmd_debuglog },
{ "default_orientation", cmd_orientation }, { "default_orientation", cmd_orientation },
{ "exec", cmd_exec }, { "exec", cmd_exec },
@ -2057,6 +2151,7 @@ static struct cmd_handler handlers[] = {
{ "for_window", cmd_for_window }, { "for_window", cmd_for_window },
{ "fullscreen", cmd_fullscreen }, { "fullscreen", cmd_fullscreen },
{ "gaps", cmd_gaps }, { "gaps", cmd_gaps },
{ "hide_edge_borders", cmd_hide_edge_borders },
{ "include", cmd_include }, { "include", cmd_include },
{ "input", cmd_input }, { "input", cmd_input },
{ "kill", cmd_kill }, { "kill", cmd_kill },

View file

@ -22,6 +22,7 @@
#include "input_state.h" #include "input_state.h"
#include "criteria.h" #include "criteria.h"
#include "input.h" #include "input.h"
#include "border.h"
struct sway_config *config = NULL; struct sway_config *config = NULL;
@ -160,7 +161,8 @@ static void config_defaults(struct sway_config *config) {
config->resizing_key = M_RIGHT_CLICK; config->resizing_key = M_RIGHT_CLICK;
config->default_layout = L_NONE; config->default_layout = L_NONE;
config->default_orientation = L_NONE; config->default_orientation = L_NONE;
config->font = strdup("pango:monospace 10"); config->font = strdup("monospace 10");
config->font_height = get_font_text_height(config->font);
// Flags // Flags
config->focus_follows_mouse = true; config->focus_follows_mouse = true;
@ -181,6 +183,44 @@ static void config_defaults(struct sway_config *config) {
config->config_chain = create_list(); config->config_chain = create_list();
config->current_config = NULL; config->current_config = NULL;
// borders
config->border = B_NORMAL;
config->border_thickness = 2;
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;
} }
static int compare_modifiers(const void *left, const void *right) { static int compare_modifiers(const void *left, const void *right) {

View file

@ -22,8 +22,11 @@ static swayc_t *new_swayc(enum swayc_types type) {
c->gaps = -1; c->gaps = -1;
c->layout = L_NONE; c->layout = L_NONE;
c->type = type; c->type = type;
c->border_type = config->border;
c->border_thickness = config->border_thickness;
if (type != C_VIEW) { if (type != C_VIEW) {
c->children = create_list(); c->children = create_list();
c->border_type = B_NONE;
} }
return c; return c;
} }
@ -266,11 +269,12 @@ swayc_t *new_view(swayc_t *sibling, wlc_handle handle) {
view->is_focused = true; view->is_focused = true;
view->sticky = false; view->sticky = false;
// Setup geometry // Setup geometry
const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); struct wlc_geometry geometry;
wlc_view_get_visible_geometry(handle, &geometry);
view->width = 0; view->width = 0;
view->height = 0; view->height = 0;
view->desired_width = geometry->size.w; view->desired_width = geometry.size.w;
view->desired_height = geometry->size.h; view->desired_height = geometry.size.h;
view->is_floating = false; view->is_floating = false;
@ -303,13 +307,14 @@ swayc_t *new_floating_view(wlc_handle handle) {
view->sticky = false; view->sticky = false;
// Set the geometry of the floating view // Set the geometry of the floating view
const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); struct wlc_geometry geometry;
wlc_view_get_visible_geometry(handle, &geometry);
// give it requested geometry, but place in center // give it requested geometry, but place in center
view->x = (swayc_active_workspace()->width - geometry->size.w) / 2; view->x = (swayc_active_workspace()->width - geometry.size.w) / 2;
view->y = (swayc_active_workspace()->height- geometry->size.h) / 2; view->y = (swayc_active_workspace()->height- geometry.size.h) / 2;
view->width = geometry->size.w; view->width = geometry.size.w;
view->height = geometry->size.h; view->height = geometry.size.h;
view->desired_width = view->width; view->desired_width = view->width;
view->desired_height = view->height; view->desired_height = view->height;

View file

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "input_state.h" #include "input_state.h"
#include "ipc-server.h" #include "ipc-server.h"
#include "border.h"
bool locked_container_focus = false; bool locked_container_focus = false;
bool locked_view_focus = false; bool locked_view_focus = false;
@ -28,6 +29,8 @@ static void update_focus(swayc_t *c) {
// Case where output changes // Case where output changes
case C_OUTPUT: case C_OUTPUT:
// update borders for views in prev
container_map(prev, map_update_view_border, NULL);
wlc_output_focus(c->handle); wlc_output_focus(c->handle);
break; break;
@ -130,6 +133,7 @@ bool set_focused_container(swayc_t *c) {
// unactivate previous focus // unactivate previous focus
if (focused->type == C_VIEW) { if (focused->type == C_VIEW) {
wlc_view_set_state(focused->handle, WLC_BIT_ACTIVATED, false); wlc_view_set_state(focused->handle, WLC_BIT_ACTIVATED, false);
update_view_border(focused);
} }
// activate current focus // activate current focus
if (p->type == C_VIEW) { if (p->type == C_VIEW) {
@ -137,6 +141,7 @@ bool set_focused_container(swayc_t *c) {
// set focus if view_focus is unlocked // set focus if view_focus is unlocked
if (!locked_view_focus) { if (!locked_view_focus) {
wlc_view_focus(p->handle); wlc_view_focus(p->handle);
update_view_border(p);
} }
} }
} else if (p->type == C_WORKSPACE) { } else if (p->type == C_WORKSPACE) {

View file

@ -9,6 +9,7 @@
#include <ctype.h> #include <ctype.h>
#include "handlers.h" #include "handlers.h"
#include "border.h"
#include "log.h" #include "log.h"
#include "layout.h" #include "layout.h"
#include "config.h" #include "config.h"
@ -150,6 +151,10 @@ static void handle_output_post_render(wlc_handle output) {
ipc_get_pixels(output); ipc_get_pixels(output);
} }
static void handle_view_pre_render(wlc_handle view) {
render_view_borders(view);
}
static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) { static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) {
sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h); sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h);
swayc_t *c = swayc_by_handle(output); swayc_t *c = swayc_by_handle(output);
@ -716,6 +721,7 @@ void register_wlc_handlers() {
wlc_set_view_created_cb(handle_view_created); wlc_set_view_created_cb(handle_view_created);
wlc_set_view_destroyed_cb(handle_view_destroyed); wlc_set_view_destroyed_cb(handle_view_destroyed);
wlc_set_view_focus_cb(handle_view_focus); wlc_set_view_focus_cb(handle_view_focus);
wlc_set_view_render_pre_cb(handle_view_pre_render);
wlc_set_view_request_geometry_cb(handle_view_geometry_request); wlc_set_view_request_geometry_cb(handle_view_geometry_request);
wlc_set_view_request_state_cb(handle_view_state_request); wlc_set_view_request_state_cb(handle_view_state_request);
wlc_set_keyboard_key_cb(handle_key); wlc_set_keyboard_key_cb(handle_key);

View file

@ -12,6 +12,7 @@
#include "focus.h" #include "focus.h"
#include "output.h" #include "output.h"
#include "ipc-server.h" #include "ipc-server.h"
#include "border.h"
swayc_t root_container; swayc_t root_container;
list_t *scratchpad; list_t *scratchpad;
@ -373,6 +374,46 @@ void move_workspace_to(swayc_t* workspace, swayc_t* destination) {
update_visibility(src_op); update_visibility(src_op);
} }
static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) {
struct wlc_geometry g = *geometry;
c->actual_geometry = g;
switch (c->border_type) {
case B_NONE:
break;
case B_PIXEL:
g.origin.x -= c->border_thickness;
g.origin.y -= c->border_thickness;
g.size.w += (c->border_thickness * 2);
g.size.h += (c->border_thickness * 2);
break;
case B_NORMAL:
g.origin.x -= c->border_thickness;
uint32_t title_bar_height = config->font_height + 4; // borders + padding
g.origin.y -= title_bar_height;
g.size.w += (c->border_thickness * 2);
g.size.h += (c->border_thickness + title_bar_height);
struct wlc_geometry title_bar = {
.origin = {
.x = g.origin.x,
.y = g.origin.y
},
.size = {
.w = g.size.w,
.h = title_bar_height
}
};
c->title_bar_geometry = title_bar;
break;
}
c->border_geometry = g;
*geometry = c->actual_geometry;
update_view_border(c);
}
void update_geometry(swayc_t *container) { void update_geometry(swayc_t *container) {
if (container->type != C_VIEW) { if (container->type != C_VIEW) {
return; return;
@ -426,6 +467,81 @@ void update_geometry(swayc_t *container) {
geometry.size.h = ws->y + ws->height - geometry.origin.y; geometry.size.h = ws->y + ws->height - geometry.origin.y;
} }
} }
if (swayc_is_fullscreen(container)) {
container->border_geometry = (const struct wlc_geometry){0};
container->title_bar_geometry = (const struct wlc_geometry){0};
} else if (container->is_floating) { // allocate border for floating window
update_border_geometry_floating(container, &geometry);
} else if (!container->is_floating) { // allocate border for titled window
container->border_geometry = geometry;
int border_top = container->border_thickness;
int border_bottom = container->border_thickness;
int border_left = container->border_thickness;
int border_right = container->border_thickness;
// handle hide_edge_borders
if (config->hide_edge_borders != E_NONE && gap <= 0) {
swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
const struct wlc_size *size = wlc_output_get_resolution(output->handle);
if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) {
if (geometry.origin.x == 0) {
border_left = 0;
}
if (geometry.origin.x + geometry.size.w == size->w) {
border_right = 0;
}
}
if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) {
if (geometry.origin.y == 0) {
border_top = 0;
}
if (geometry.origin.y + geometry.size.h == size->h) {
border_bottom = 0;
}
}
}
switch (container->border_type) {
case B_NONE:
break;
case B_PIXEL:
geometry.origin.x += border_left;
geometry.origin.y += border_top;
geometry.size.w -= (border_left + border_right);
geometry.size.h -= (border_top + border_bottom);
break;
case B_NORMAL:
{
struct wlc_geometry title_bar = {
.origin = {
.x = container->border_geometry.origin.x,
.y = container->border_geometry.origin.y
},
.size = {
.w = container->border_geometry.size.w,
.h = config->font_height + 4 // borders + padding
}
};
geometry.origin.x += border_left;
geometry.origin.y += title_bar.size.h;
geometry.size.w -= (border_left + border_right);
geometry.size.h -= (border_bottom + title_bar.size.h);
container->title_bar_geometry = title_bar;
break;
}
}
container->actual_geometry = geometry;
update_view_border(container);
}
wlc_view_set_geometry(container->handle, 0, &geometry); wlc_view_set_geometry(container->handle, 0, &geometry);
} }

View file

@ -43,6 +43,15 @@ The following commands may only be used in the configuration file.
The following commands cannot be used directly in the configuration file. The following commands cannot be used directly in the configuration file.
They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). They are expected to be used with **bindsym** or at runtime through **swaymsg**(1).
**border** <normal|pixel> [<n>]::
Set border style for windows. _normal_ includes a border of thickness _n_ and
a title bar. _pixel_ is just the border without title bar. Default is _normal_
with border thickness 2.
**border** <none|toggle>::
Set border style to _none_ or _toggle_ between the available border styles:
_normal_, _pixel_, _none_.
**exit**:: **exit**::
Exit sway and end your Wayland session. Exit sway and end your Wayland session.
@ -67,6 +76,9 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**(
**fullscreen**:: **fullscreen**::
Toggles fullscreen status for the focused view. Toggles fullscreen status for the focused view.
**hide_edge_borders** <none|vertical|horizontal|both>::
Hide window borders adjacent to the screen edges. Default is _none_.
**layout** <mode>:: **layout** <mode>::
Sets the layout mode of the focused container. _mode_ can be one of _splith_, Sets the layout mode of the focused container. _mode_ can be one of _splith_,
_splitv_, or _toggle split_. _splitv_, or _toggle split_.

View file

@ -35,9 +35,11 @@ uint32_t parse_position(const char *position) {
char *parse_font(const char *font) { char *parse_font(const char *font) {
char *new_font = NULL; char *new_font = NULL;
if (strncmp("pango:", font, 6) == 0) { if (strncmp("pango:", font, 6) == 0) {
new_font = strdup(font + 6); font += 6;
} }
new_font = strdup(font);
return new_font; return new_font;
} }

View file

@ -50,7 +50,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y
static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge) { static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge) {
int width, height, sep_width; int width, height, sep_width;
get_text_size(window, &width, &height, "%s", block->full_text); get_text_size(window->cairo, window->font, &width, &height, "%s", block->full_text);
int textwidth = width; int textwidth = width;
double block_width = width; double block_width = width;
@ -74,7 +74,7 @@ static void render_block(struct window *window, struct config *config, struct st
// Add separator // Add separator
if (!edge) { if (!edge) {
if (config->sep_symbol) { if (config->sep_symbol) {
get_text_size(window, &sep_width, &height, "%s", config->sep_symbol); get_text_size(window->cairo, window->font, &sep_width, &height, "%s", config->sep_symbol);
if (sep_width > block->separator_block_width) { if (sep_width > block->separator_block_width) {
block->separator_block_width = sep_width + margin * 2; block->separator_block_width = sep_width + margin * 2;
} }
@ -136,7 +136,7 @@ static void render_block(struct window *window, struct config *config, struct st
cairo_move_to(window->cairo, offset, margin); cairo_move_to(window->cairo, offset, margin);
cairo_set_source_u32(window->cairo, block->color); cairo_set_source_u32(window->cairo, block->color);
pango_printf(window, "%s", block->full_text); pango_printf(window->cairo, window->font, "%s", block->full_text);
pos += width; pos += width;
@ -159,7 +159,7 @@ static void render_block(struct window *window, struct config *config, struct st
if (config->sep_symbol) { if (config->sep_symbol) {
offset = pos + (block->separator_block_width - sep_width) / 2; offset = pos + (block->separator_block_width - sep_width) / 2;
cairo_move_to(window->cairo, offset, margin); cairo_move_to(window->cairo, offset, margin);
pango_printf(window, "%s", config->sep_symbol); pango_printf(window->cairo, window->font, "%s", config->sep_symbol);
} else { } else {
cairo_set_line_width(window->cairo, 1); cairo_set_line_width(window->cairo, 1);
cairo_move_to(window->cairo, pos + block->separator_block_width/2, cairo_move_to(window->cairo, pos + block->separator_block_width/2,
@ -201,7 +201,7 @@ static void render_workspace_button(struct window *window, struct config *config
char *name = handle_workspace_number(config->strip_workspace_numbers, ws->name); char *name = handle_workspace_number(config->strip_workspace_numbers, ws->name);
int width, height; int width, height;
get_text_size(window, &width, &height, "%s", name); get_text_size(window->cairo, window->font, &width, &height, "%s", name);
struct box_colors box_colors; struct box_colors box_colors;
if (ws->urgent) { if (ws->urgent) {
box_colors = config->colors.urgent_workspace; box_colors = config->colors.urgent_workspace;
@ -228,7 +228,7 @@ static void render_workspace_button(struct window *window, struct config *config
// text // text
cairo_set_source_u32(window->cairo, box_colors.text); cairo_set_source_u32(window->cairo, box_colors.text);
cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin);
pango_printf(window, "%s", name); pango_printf(window->cairo, window->font, "%s", name);
*x += width + ws_horizontal_padding * 2 + ws_spacing; *x += width + ws_horizontal_padding * 2 + ws_spacing;
@ -237,7 +237,7 @@ static void render_workspace_button(struct window *window, struct config *config
static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) {
int width, height; int width, height;
get_text_size(window, &width, &height, "%s", config->mode); get_text_size(window->cairo, window->font, &width, &height, "%s", config->mode);
// background // background
cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); cairo_set_source_u32(window->cairo, config->colors.binding_mode.background);
@ -254,7 +254,7 @@ static void render_binding_mode_indicator(struct window *window, struct config *
// text // text
cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); cairo_set_source_u32(window->cairo, config->colors.binding_mode.text);
cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin);
pango_printf(window, "%s", config->mode); pango_printf(window->cairo, window->font, "%s", config->mode);
} }
void render(struct output *output, struct config *config, struct status_line *line) { void render(struct output *output, struct config *config, struct status_line *line) {
@ -278,9 +278,9 @@ void render(struct output *output, struct config *config, struct status_line *li
int width, height; int width, height;
if (line->protocol == TEXT) { if (line->protocol == TEXT) {
get_text_size(window, &width, &height, "%s", line->text_line); get_text_size(window->cairo, window->font, &width, &height, "%s", line->text_line);
cairo_move_to(cairo, window->width - margin - width, margin); cairo_move_to(cairo, window->width - margin - width, margin);
pango_printf(window, "%s", line->text_line); pango_printf(window->cairo, window->font, "%s", line->text_line);
} else if (line->protocol == I3BAR && line->block_line) { } else if (line->protocol == I3BAR && line->block_line) {
double pos = window->width - 0.5; double pos = window->width - 0.5;
bool edge = true; bool edge = true;
@ -312,7 +312,7 @@ void render(struct output *output, struct config *config, struct status_line *li
void set_window_height(struct window *window, int height) { void set_window_height(struct window *window, int height) {
int text_width, text_height; int text_width, text_height;
get_text_size(window, &text_width, &text_height, "Test string for measuring purposes"); get_text_size(window->cairo, window->font, &text_width, &text_height, "Test string for measuring purposes");
if (height > 0) { if (height > 0) {
margin = (height - text_height) / 2; margin = (height - text_height) / 2;
ws_vertical_padding = margin - 1.5; ws_vertical_padding = margin - 1.5;

View file

@ -4,21 +4,19 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include "client/window.h"
#include "client/buffer.h"
#include "log.h" #include "log.h"
PangoLayout *get_pango_layout(struct window *window, const char *text) { PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text) {
PangoLayout *layout = pango_cairo_create_layout(window->cairo); PangoLayout *layout = pango_cairo_create_layout(cairo);
pango_layout_set_text(layout, text, -1); pango_layout_set_text(layout, text, -1);
PangoFontDescription *desc = pango_font_description_from_string(window->font); PangoFontDescription *desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc); pango_layout_set_font_description(layout, desc);
pango_layout_set_single_paragraph_mode(layout, 1); pango_layout_set_single_paragraph_mode(layout, 1);
pango_font_description_free(desc); pango_font_description_free(desc);
return layout; return layout;
} }
void get_text_size(struct window *window, int *width, int *height, const char *fmt, ...) { void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, const char *fmt, ...) {
char *buf = malloc(2048); char *buf = malloc(2048);
va_list args; va_list args;
@ -28,8 +26,8 @@ void get_text_size(struct window *window, int *width, int *height, const char *f
} }
va_end(args); va_end(args);
PangoLayout *layout = get_pango_layout(window, buf); PangoLayout *layout = get_pango_layout(cairo, font, buf);
pango_cairo_update_layout(window->cairo, layout); pango_cairo_update_layout(cairo, layout);
pango_layout_get_pixel_size(layout, width, height); pango_layout_get_pixel_size(layout, width, height);
@ -38,7 +36,7 @@ void get_text_size(struct window *window, int *width, int *height, const char *f
free(buf); free(buf);
} }
void pango_printf(struct window *window, const char *fmt, ...) { void pango_printf(cairo_t *cairo, const char *font, const char *fmt, ...) {
char *buf = malloc(2048); char *buf = malloc(2048);
va_list args; va_list args;
@ -48,10 +46,10 @@ void pango_printf(struct window *window, const char *fmt, ...) {
} }
va_end(args); va_end(args);
PangoLayout *layout = get_pango_layout(window, buf); PangoLayout *layout = get_pango_layout(cairo, font, buf);
pango_cairo_update_layout(window->cairo, layout); pango_cairo_update_layout(cairo, layout);
pango_cairo_show_layout(window->cairo, layout); pango_cairo_show_layout(cairo, layout);
g_object_unref(layout); g_object_unref(layout);