Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement display_sort_order config option #554

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion doc/yabai.1
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ When focusing a window, put the mouse at its center.
Automatically focus the window under the mouse.
.RE
.sp
\fBdisplay_sort_order\fP [\fIhorizontal|vertical|none\fP]
.RS 4
Specify how displays are ordered.
.br
\fIhorizontal\fP|\fIvertical\fP: order by the display\(cqs relative position.
.br
\fInone\fP: (default) use the native ordering.
.br
The position of a display is determined by its center point.
.RE
.sp
\fBwindow_placement\fP [\fIfirst_child|second_child\fP]
.RS 4
Specify whether managed windows should become the first or second leaf\-node.
Expand Down Expand Up @@ -870,4 +881,4 @@ Arbitrary command executed through \fB/usr/bin/env sh \-c\fP
If \fByabai\fP can\(cqt handle a message, it will return a non\-zero exit code.
.SH "AUTHOR"
.sp
Åsmund Vikane <aasvi93 at gmail.com>
Åsmund Vikane <aasvi93 at gmail.com>
6 changes: 6 additions & 0 deletions doc/yabai.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ Global Settings
*focus_follows_mouse* ['autofocus|autoraise|off']::
Automatically focus the window under the mouse.

*display_sort_order* ['horizontal|vertical|none']::
Specify how displays are ordered. +
'horizontal'|'vertical': order by the display's relative position. +
'none': (default) use the native ordering. +
The position of a display is determined by its center point.

*window_placement* ['first_child|second_child']::
Specify whether managed windows should become the first or second leaf-node.

Expand Down
15 changes: 15 additions & 0 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ CGRect display_bounds_constrained(uint32_t did)
return frame;
}

static inline float display_frame_center(enum display_sort_order axis, CFStringRef uuid_str) {
CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL, uuid_str);
uint32_t did = CGDisplayGetDisplayIDFromUUID(uuid_ref);
CGRect frame = CGDisplayBounds(did);

switch(axis) {
case DISPLAY_SORT_HORIZ: return frame.origin.x + (frame.size.width) / 2;
case DISPLAY_SORT_VERT: return frame.origin.y + (frame.size.height) / 2;

// should not get here, but if we do it'll just result in not sorting
// (considering every display to be "equal")
default: return 0.0;
}
}

CGPoint display_center(uint32_t did)
{
CGRect bounds = display_bounds(did);
Expand Down
80 changes: 70 additions & 10 deletions src/display_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,76 @@ uint32_t display_manager_point_display_id(CGPoint point)
return result;
}

static inline enum display_sort_order rotate_axis(enum display_sort_order axis) {
switch(axis) {
case DISPLAY_SORT_NONE: return DISPLAY_SORT_NONE;
case DISPLAY_SORT_VERT: return DISPLAY_SORT_HORIZ;
case DISPLAY_SORT_HORIZ: return DISPLAY_SORT_VERT;
}
}

static inline float display_sort_lookup(CGPoint point, enum display_sort_order axis) {
switch(axis) {
case DISPLAY_SORT_NONE: return 0.0;
case DISPLAY_SORT_VERT: return point.y;
case DISPLAY_SORT_HORIZ: return point.x;
}
}

static enum CFComparisonResult coordinate_comparator(const void *a_p, const void *b_p, void *context_p) {
CFStringRef a = (CFStringRef) a_p;
CFStringRef b = (CFStringRef) b_p;
enum display_sort_order axis = *((enum display_sort_order *) context_p);

CFUUIDRef uuid_a = CFUUIDCreateFromString(NULL, a);
CFUUIDRef uuid_b = CFUUIDCreateFromString(NULL, b);

uint32_t did_a = CGDisplayGetDisplayIDFromUUID(uuid_a);
uint32_t did_b = CGDisplayGetDisplayIDFromUUID(uuid_b);

// if (uuid_a) CFRelease(uuid_a);
// if (uuid_b) CFRelease(uuid_b);

CGPoint center_a = display_center(did_a);
CGPoint center_b = display_center(did_b);

float coord_a, coord_b;

coord_a = display_sort_lookup(center_a, axis);
coord_b = display_sort_lookup(center_b, axis);
if (coord_a < coord_b) return kCFCompareLessThan;
if (coord_a > coord_b) return kCFCompareGreaterThan;

// if equal, fall back to the other axis
axis = rotate_axis(axis);

coord_a = display_sort_lookup(center_a, axis);
coord_b = display_sort_lookup(center_b, axis);
if (coord_a < coord_b) return kCFCompareLessThan;
if (coord_a > coord_b) return kCFCompareGreaterThan;

// the screens share a center point, so don't re-order them at all
return kCFCompareEqualTo;
}

CFStringRef display_manager_arrangement_display_uuid(int arrangement)
{
CFStringRef result = NULL;
CFArrayRef displays = SLSCopyManagedDisplays(g_connection);

int count = CFArrayGetCount(displays);
int index = arrangement - 1;

if (in_range_ie(index, 0, count)) {
result = CFRetain(CFArrayGetValueAtIndex(displays, index));
if (g_display_manager.sort_order == DISPLAY_SORT_NONE) {
result = CFRetain(CFArrayGetValueAtIndex(displays, index));
} else {
// copy the array to a mutable one, then sort it in place
CFRange all = CFRangeMake((long) 0, (long) count);
CFMutableArrayRef mut_displays = CFArrayCreateMutableCopy(NULL, count, displays);
CFArraySortValues(mut_displays, all, &coordinate_comparator, &g_display_manager.sort_order);
result = CFRetain(CFArrayGetValueAtIndex(mut_displays, index));
CFRelease(mut_displays);
}
}

CFRelease(displays);
Expand All @@ -99,16 +159,15 @@ CFStringRef display_manager_arrangement_display_uuid(int arrangement)
uint32_t display_manager_arrangement_display_id(int arrangement)
{
uint32_t result = 0;
CFArrayRef displays = SLSCopyManagedDisplays(g_connection);

int count = CFArrayGetCount(displays);
int index = arrangement - 1;
CFStringRef uuid = display_manager_arrangement_display_uuid(arrangement);
if (!uuid) return result;
CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL, uuid);
CFRelease(uuid);
if (!uuid_ref) return result;

if (in_range_ie(index, 0, count)) {
result = display_id(CFArrayGetValueAtIndex(displays, index));
}
result = CGDisplayGetDisplayIDFromUUID(uuid_ref);
CFRelease(uuid_ref);

CFRelease(displays);
return result;
}

Expand Down Expand Up @@ -333,6 +392,7 @@ bool display_manager_begin(struct display_manager *dm)
dm->current_display_id = display_manager_active_display_id();
dm->last_display_id = dm->current_display_id;
dm->mode = EXTERNAL_BAR_OFF;
dm->sort_order = DISPLAY_SORT_NONE;
dm->top_padding = 0;
dm->bottom_padding = 0;
return CGDisplayRegisterReconfigurationCallback(display_handler, NULL) == kCGErrorSuccess;
Expand Down
16 changes: 16 additions & 0 deletions src/display_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@
#define DOCK_ORIENTATION_LEFT 3
#define DOCK_ORIENTATION_RIGHT 4

enum display_sort_order
{
DISPLAY_SORT_NONE,
DISPLAY_SORT_HORIZ,
DISPLAY_SORT_VERT
};

static const char *display_sort_order_str[] =
{
"none",
"horizontal",
"vertical"
};

enum external_bar_mode
{
EXTERNAL_BAR_OFF,
Expand All @@ -24,6 +38,8 @@ struct display_manager
uint32_t current_display_id;
uint32_t last_display_id;

enum display_sort_order sort_order;

int top_padding;
int bottom_padding;
enum external_bar_mode mode;
Expand Down
17 changes: 17 additions & 0 deletions src/message.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ extern bool g_verbose;
#define COMMAND_CONFIG_MFF "mouse_follows_focus"
#define COMMAND_CONFIG_FFM "focus_follows_mouse"
#define COMMAND_CONFIG_WINDOW_PLACEMENT "window_placement"
#define COMMAND_CONFIG_DISPLAY_SORT "display_sort_order"
#define COMMAND_CONFIG_TOPMOST "window_topmost"
#define COMMAND_CONFIG_OPACITY "window_opacity"
#define COMMAND_CONFIG_OPACITY_DURATION "window_opacity_duration"
Expand Down Expand Up @@ -50,6 +51,9 @@ extern bool g_verbose;

#define ARGUMENT_CONFIG_FFM_AUTOFOCUS "autofocus"
#define ARGUMENT_CONFIG_FFM_AUTORAISE "autoraise"
#define ARGUMENT_CONFIG_DISPLAY_SORT_NONE "none"
#define ARGUMENT_CONFIG_DISPLAY_SORT_HORIZ "horizontal"
#define ARGUMENT_CONFIG_DISPLAY_SORT_VERT "vertical"
#define ARGUMENT_CONFIG_WINDOW_PLACEMENT_FST "first_child"
#define ARGUMENT_CONFIG_WINDOW_PLACEMENT_SND "second_child"
#define ARGUMENT_CONFIG_SHADOW_FLT "float"
Expand Down Expand Up @@ -995,6 +999,19 @@ 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.length, value.text, command.length, command.text, domain.length, domain.text);
}
} else if (token_equals(command, COMMAND_CONFIG_DISPLAY_SORT)) {
struct token value = get_token(&message);
if (!token_is_valid(value)) {
fprintf(rsp, "%s\n", window_node_child_str[g_space_manager.window_placement]);
} else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_SORT_NONE)) {
g_display_manager.sort_order = DISPLAY_SORT_NONE;
} else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_SORT_HORIZ)) {
g_display_manager.sort_order = DISPLAY_SORT_HORIZ;
} else if (token_equals(value, ARGUMENT_CONFIG_DISPLAY_SORT_VERT)) {
g_display_manager.sort_order = DISPLAY_SORT_VERT;
} else {
daemon_fail(rsp, "unknown value '%.*s' given to command '%.*s' for domain '%.*s'\n", value.length, value.text, command.length, command.text, domain.length, domain.text);
}
} else if (token_equals(command, COMMAND_CONFIG_TOPMOST)) {
struct token value = get_token(&message);
if (!token_is_valid(value)) {
Expand Down