Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#148 #1430 improve window borders
Browse files Browse the repository at this point in the history
koekeishiya committed Sep 20, 2022
1 parent e33fc6f commit a125bdb
Showing 12 changed files with 138 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix regression causing window sticky to not work properly [#1424](https://github.com/koekeishiya/yabai/issues/1424)
- Make window zoom more flexible, allow parent-zoomed window to enter fullscreen and vice versa [#1429](https://github.com/koekeishiya/yabai/issues/1429)
- Added new command to manually specify the default *split_type* [#1423](https://github.com/koekeishiya/yabai/issues/1423)
- Borders are now placed below and outside the window, corners rounded, and with a filled blur to act as a backdrop [#1430](https://github.com/koekeishiya/yabai/issues/1430)

## [4.0.4] - 2022-09-08
### Changed
9 changes: 7 additions & 2 deletions doc/yabai.1
Original file line number Diff line number Diff line change
@@ -2,12 +2,12 @@
.\" Title: yabai
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 2.0.16
.\" Date: 2022-09-19
.\" Date: 2022-09-20
.\" Manual: Yabai Manual
.\" Source: Yabai
.\" Language: English
.\"
.TH "YABAI" "1" "2022-09-19" "Yabai" "Yabai Manual"
.TH "YABAI" "1" "2022-09-20" "Yabai" "Yabai Manual"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@@ -215,6 +215,11 @@ Draw border for windows.
Width of window borders. If the given width is an odd number, it will be incremented by 1.
.RE
.sp
\fBwindow_border_radius\fP [\fI<integer number>\fP]
.RS 4
Radius of window borders.
.RE
.sp
\fBactive_window_border_color\fP [\fI0xAARRGGBB\fP]
.RS 4
Color of the border of the focused window.
3 changes: 3 additions & 0 deletions doc/yabai.asciidoc
Original file line number Diff line number Diff line change
@@ -158,6 +158,9 @@ Global Settings
*window_border_width* ['<even integer number>']::
Width of window borders. If the given width is an odd number, it will be incremented by 1.

*window_border_radius* ['<integer number>']::
Radius of window borders.

*active_window_border_color* ['0xAARRGGBB']::
Color of the border of the focused window.

19 changes: 10 additions & 9 deletions examples/yabairc
Original file line number Diff line number Diff line change
@@ -22,28 +22,29 @@ yabai -m config \
window_placement second_child \
window_topmost off \
window_shadow on \
window_opacity off \
window_opacity_duration 0.0 \
window_animation_duration 0.0 \
window_opacity_duration 0.0 \
active_window_opacity 1.0 \
normal_window_opacity 0.90 \
window_border off \
window_border_width 6 \
window_opacity off \
window_border_width 4 \
window_border_radius 12 \
active_window_border_color 0xff775759 \
normal_window_border_color 0xff555555 \
insert_feedback_color 0xffd75f5f \
window_border off \
split_ratio 0.50 \
split_type auto \
auto_balance off \
mouse_modifier fn \
mouse_action1 move \
mouse_action2 resize \
mouse_drop_action swap \
top_padding 12 \
bottom_padding 12 \
left_padding 12 \
right_padding 12 \
window_gap 06 \
layout bsp
layout bsp \
mouse_modifier fn \
mouse_action1 move \
mouse_action2 resize \
mouse_drop_action swap

echo "yabai configuration loaded.."
103 changes: 62 additions & 41 deletions src/border.c
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ void border_show_all(void)
if (bucket->value) {
struct window *window = bucket->value;
if (window->border.id && border_should_order_in(window)) {
SLSOrderWindow(g_connection, window->border.id, 1, window->id);
SLSOrderWindow(g_connection, window->border.id, -1, window->id);
}
}

@@ -68,12 +68,60 @@ void border_redraw(struct window *window)
{
SLSDisableUpdate(g_connection);
CGContextClearRect(window->border.context, window->border.frame);
CGContextAddPath(window->border.context, window->border.path);
CGContextStrokePath(window->border.context);
CGContextAddPath(window->border.context, window->border.path_ref);
CGContextDrawPath(window->border.context, kCGPathFillStroke);
CGContextFlush(window->border.context);
SLSReenableUpdate(g_connection);
}

static inline float border_radius_clamp_x(CGRect frame, float radius)
{
if (radius * 2 > CGRectGetWidth(frame)) {
radius = CGRectGetWidth(frame) / 2;
}
return radius;
}

static inline float border_radius_clamp_y(CGRect frame, float radius)
{
if (radius * 2 > CGRectGetHeight(frame)) {
radius = CGRectGetHeight(frame) / 2;
}
return radius;
}

void border_resize(struct window *window, CGRect frame)
{
if (window->border.region) CFRelease(window->border.region);
if (window->border.path_ref) CGPathRelease(window->border.path_ref);

frame = CGRectInset(frame, -g_window_manager.border_width, -g_window_manager.border_width);
CGSNewRegionWithRect(&frame, &window->border.region);
window->border.frame.size = frame.size;

window->border.path = (CGRect) {{ 0.5f*g_window_manager.border_width, 0.5f*g_window_manager.border_width }, { frame.size.width - g_window_manager.border_width, frame.size.height - g_window_manager.border_width }};
window->border.path_ref = CGPathCreateWithRoundedRect(window->border.path, border_radius_clamp_x(window->border.path, g_window_manager.border_radius), border_radius_clamp_y(window->border.path, g_window_manager.border_radius), NULL);

uint8_t is_ordered_in = false;
SLSWindowIsOrderedIn(g_connection, window->border.id, &is_ordered_in);

SLSDisableUpdate(g_connection);
if (is_ordered_in) SLSOrderWindow(g_connection, window->border.id, 0, 0);
SLSSetWindowShape(g_connection, window->border.id, 0.0f, 0.0f, window->border.region);
CGContextClearRect(window->border.context, window->border.frame);
CGContextAddPath(window->border.context, window->border.path_ref);
CGContextDrawPath(window->border.context, kCGPathFillStroke);
CGContextFlush(window->border.context);
if (is_ordered_in) SLSOrderWindow(g_connection, window->border.id, -1, window->id);
SLSReenableUpdate(g_connection);
}

void border_move(struct window *window, CGRect frame)
{
frame = CGRectInset(frame, -g_window_manager.border_width, -g_window_manager.border_width);
SLSMoveWindow(g_connection, window->border.id, &frame.origin);
}

void border_activate(struct window *window)
{
if (!window->border.id) return;
@@ -125,32 +173,7 @@ void border_show(struct window *window)
{
if (!window->border.id) return;

SLSOrderWindow(g_connection, window->border.id, 1, window->id);
}

void border_resize(struct window *window)
{
struct border *border = &window->border;

if (border->region) CFRelease(border->region);
if (border->path) CGPathRelease(border->path);

CGRect frame = {};
SLSGetWindowBounds(g_connection, window->id, &frame);

CGSNewRegionWithRect(&frame, &border->region);
border->frame.size = frame.size;

border->path = CGPathCreateMutable();
CGPathAddRoundedRect(border->path, NULL, border->frame, 0, 0);

SLSOrderWindow(g_connection, border->id, 0, 0);
SLSSetWindowShape(g_connection, border->id, 0.0f, 0.0f, border->region);
CGContextClearRect(border->context, border->frame);
CGContextAddPath(border->context, border->path);
CGContextStrokePath(border->context);
CGContextFlush(border->context);
SLSOrderWindow(g_connection, border->id, 1, window->id);
SLSOrderWindow(g_connection, window->border.id, -1, window->id);
}

void border_create(struct window *window)
@@ -163,16 +186,17 @@ void border_create(struct window *window)
return;
}

CGSNewRegionWithRect(&window->frame, &window->border.region);
window->border.frame.size = window->frame.size;
CGRect frame = CGRectNull;
CGSNewRegionWithRect(&frame, &window->border.region);

uint64_t tag = 1ULL << 46;
SLSNewWindow(g_connection, 2, 0, 0, window->border.region, &window->border.id);
SLSSetWindowTags(g_connection, window->border.id, &tag, 64);
sls_window_disable_shadow(window->border.id);
SLSSetWindowResolution(g_connection, window->border.id, 1.0f);
SLSSetWindowResolution(g_connection, window->border.id, 2.0f);
SLSSetWindowOpacity(g_connection, window->border.id, 0);
SLSSetWindowLevel(g_connection, window->border.id, window_level(window));
SLSSetWindowBackgroundBlurRadiusStyle(g_connection, window->border.id, 20, 1);

window->border.id_ref = cfarray_of_cfnumbers(&window->border.id, sizeof(uint32_t), 1, kCFNumberSInt32Type);
window->border.context = SLWindowContextCreate(g_connection, window->border.id, 0);
@@ -182,15 +206,12 @@ void border_create(struct window *window)
g_window_manager.normal_border_color.g,
g_window_manager.normal_border_color.b,
g_window_manager.normal_border_color.a);

window->border.path = CGPathCreateMutable();
CGPathAddRoundedRect(window->border.path, NULL, window->border.frame, 0, 0);

border_redraw(window);
CGContextSetRGBFillColor(window->border.context, 0.96f, 0.96f, 0.96f, 0.075f);
border_resize(window, window->frame);

if (border_should_order_in(window)) {
border_ensure_same_space(window);
SLSOrderWindow(g_connection, window->border.id, 1, window->id);
SLSOrderWindow(g_connection, window->border.id, -1, window->id);
}

border_update_window_notifications(window->id);
@@ -200,9 +221,9 @@ void border_destroy(struct window *window)
{
if (!window->border.id) return;

if (window->border.id_ref) CFRelease(window->border.id_ref);
if (window->border.region) CFRelease(window->border.region);
if (window->border.path) CGPathRelease(window->border.path);
if (window->border.id_ref) CFRelease(window->border.id_ref);
if (window->border.region) CFRelease(window->border.region);
if (window->border.path_ref) CGPathRelease(window->border.path_ref);

CGContextRelease(window->border.context);
SLSReleaseWindow(g_connection, window->border.id);
6 changes: 4 additions & 2 deletions src/border.h
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ struct border
CGContextRef context;
CFTypeRef region;
CGRect frame;
CGMutablePathRef path;
CGRect path;
CGPathRef path_ref;
};

struct window;
@@ -19,7 +20,8 @@ void border_hide(struct window *window);
void border_show(struct window *window);
void border_ensure_same_space(struct window *window);
void border_redraw(struct window *window);
void border_resize(struct window *window);
void border_resize(struct window *window, CGRect frame);
void border_move(struct window *window, CGRect frame);
void border_activate(struct window *window);
void border_deactivate(struct window *window);
void border_create(struct window *window);
14 changes: 7 additions & 7 deletions src/event.c
Original file line number Diff line number Diff line change
@@ -669,7 +669,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_WINDOW_DEMINIMIZED)
debug("%s: window %s %d is deminimized on active space\n", __FUNCTION__, window->application->name, window->id);
if (window->border.id && border_should_order_in(window)) {
border_ensure_same_space(window);
SLSOrderWindow(g_connection, window->border.id, 1, window->id);
SLSOrderWindow(g_connection, window->border.id, -1, window->id);
}
if (window_manager_should_manage_window(window) && !window_manager_find_managed_window(&g_window_manager, window)) {
struct window *last_window = window_manager_find_window(&g_window_manager, g_window_manager.last_window_id);
@@ -724,7 +724,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_SLS_WINDOW_MOVED)
if (border->id && border_should_order_in(window)) {
CGRect frame = {};
SLSGetWindowBounds(g_connection, window_id, &frame);
SLSMoveWindow(g_connection, border->id, &frame.origin);
border_move(window, frame);
}

return EVENT_SUCCESS;
@@ -745,9 +745,9 @@ static EVENT_CALLBACK(EVENT_HANDLER_SLS_WINDOW_RESIZED)

struct border *border = &window->border;
if (border->id && border_should_order_in(window)) {
SLSDisableUpdate(g_connection);
border_resize(window);
SLSReenableUpdate(g_connection);
CGRect frame = {};
SLSGetWindowBounds(g_connection, window_id, &frame);
border_resize(window, frame);
}

return EVENT_SUCCESS;
@@ -773,7 +773,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_SLS_WINDOW_ORDER_CHANGED)
int window_level = 0;
SLSGetWindowLevel(g_connection, window_id, &window_level);
SLSSetWindowLevel(g_connection, border->id, window_level);
SLSOrderWindow(g_connection, border->id, 1, window_id);
SLSOrderWindow(g_connection, border->id, -1, window_id);
}

return EVENT_SUCCESS;
@@ -797,7 +797,7 @@ static EVENT_CALLBACK(EVENT_HANDLER_SLS_WINDOW_IS_VISIBLE)
struct border *border = &window->border;
if (border->id && border_should_order_in(window)) {
border_ensure_same_space(window);
SLSOrderWindow(g_connection, border->id, 1, window_id);
SLSOrderWindow(g_connection, border->id, -1, window_id);
}

return EVENT_SUCCESS;
10 changes: 10 additions & 0 deletions src/message.c
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ extern bool g_verbose;
#define COMMAND_CONFIG_ANIMATION_DURATION "window_animation_duration"
#define COMMAND_CONFIG_BORDER "window_border"
#define COMMAND_CONFIG_BORDER_WIDTH "window_border_width"
#define COMMAND_CONFIG_BORDER_RADIUS "window_border_radius"
#define COMMAND_CONFIG_BORDER_ACTIVE_COLOR "active_window_border_color"
#define COMMAND_CONFIG_BORDER_NORMAL_COLOR "normal_window_border_color"
#define COMMAND_CONFIG_SHADOW "window_shadow"
@@ -1094,6 +1095,15 @@ static void handle_domain_config(FILE *rsp, struct token domain, char *message)
} else {
daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.token.length, value.token.text, command.length, command.text, domain.length, domain.text);
}
} else if (token_equals(command, COMMAND_CONFIG_BORDER_RADIUS)) {
struct token_value value = token_to_value(get_token(&message), false);
if (value.type == TOKEN_TYPE_INVALID) {
fprintf(rsp, "%.4f\n", g_window_manager.border_radius);
} else if (value.type == TOKEN_TYPE_INT) {
window_manager_set_window_border_radius(&g_window_manager, value.int_value);
} else {
daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.token.length, value.token.text, command.length, command.text, domain.length, domain.text);
}
} else if (token_equals(command, COMMAND_CONFIG_BORDER_ACTIVE_COLOR)) {
struct token_value value = token_to_value(get_token(&message), false);
if (value.type == TOKEN_TYPE_INVALID) {
2 changes: 2 additions & 0 deletions src/misc/extern.h
Original file line number Diff line number Diff line change
@@ -21,7 +21,9 @@ extern CGError SLSSetWindowTags(int cid, uint32_t wid, uint64_t *tags, int tag_s
extern CGError SLSClearWindowTags(int cid, uint32_t wid, uint64_t *tags, int tag_size);
extern CGError SLSSetWindowShape(int cid, uint32_t wid, float x_offset, float y_offset, CFTypeRef shape);
extern CGError SLSSetWindowOpacity(int cid, uint32_t wid, bool opaque);
extern CGError SLSSetWindowBackgroundBlurRadiusStyle(int cid, uint32_t wid, int radius, int style);
extern CGError SLSOrderWindow(int cid, uint32_t wid, int mode, uint32_t rel_wid);
extern CGError SLSWindowIsOrderedIn(int cid, uint32_t wid, uint8_t *value);
extern CGError SLSSetWindowLevel(int cid, uint32_t wid, int level);
extern CGContextRef SLWindowContextCreate(int cid, uint32_t wid, CFDictionaryRef options);
extern CGError CGSNewRegionWithRect(CGRect *rect, CFTypeRef *region);
6 changes: 6 additions & 0 deletions src/view.c
Original file line number Diff line number Diff line change
@@ -321,6 +321,12 @@ void window_node_capture_windows(struct window_node *node, struct window_capture
struct window *window = window_manager_find_window(&g_window_manager, node->window_list[i]);
if (window) {
struct area area = node->zoom ? node->zoom->area : node->area;
if (window->border.id) {
area.x += g_window_manager.border_width;
area.y += g_window_manager.border_width;
area.w -= g_window_manager.border_width * 2.0f;
area.h -= g_window_manager.border_width * 2.0f;
}
ts_buf_push(*window_list, ((struct window_capture) { .window = window, .x = area.x, .y = area.y, .w = area.w, .h = area.h }));
}
}
Loading

0 comments on commit a125bdb

Please sign in to comment.