Skip to content

Commit

Permalink
#148 transactional animations
Browse files Browse the repository at this point in the history
  • Loading branch information
koekeishiya committed Sep 12, 2022
1 parent 269d29d commit 45e18e0
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 9 deletions.
2 changes: 1 addition & 1 deletion examples/yabairc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ yabai -m config \
window_shadow on \
window_opacity off \
window_opacity_duration 0.0 \
window_animation_duration 0.10 \
window_animation_duration 0.35 \
active_window_opacity 1.0 \
normal_window_opacity 0.90 \
window_border off \
Expand Down
2 changes: 1 addition & 1 deletion makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ install: clean-build $(BINS)

$(OSAX_SRC): $(OSAX_PATH)/loader.m $(OSAX_PATH)/payload.m
xcrun clang $(OSAX_PATH)/loader.m -shared -O2 -mmacosx-version-min=10.13 -arch x86_64 -o $(OSAX_PATH)/loader -framework Foundation
xcrun clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/payload -framework Foundation -framework Carbon
xcrun clang $(OSAX_PATH)/payload.m -shared -fPIC -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/payload -framework Foundation -framework Carbon -F/System/Library/PrivateFrameworks -framework SkyLight
xcrun clang $(OSAX_PATH)/mach_loader.m -O2 -mmacosx-version-min=10.13 -arch x86_64 -arch arm64e -o $(OSAX_PATH)/mach_loader -framework Cocoa
xxd -i -a $(OSAX_PATH)/loader $(OSAX_PATH)/loader_bin.c
xxd -i -a $(OSAX_PATH)/payload $(OSAX_PATH)/payload_bin.c
Expand Down
101 changes: 99 additions & 2 deletions src/osax/payload.m
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@
extern void CGSShowSpaces(int cid, CFArrayRef spaces);
extern void CGSHideSpaces(int cid, CFArrayRef spaces);

extern CFTypeRef SLSTransactionCreate(int cid);
extern CGError SLSTransactionCommit(CFTypeRef transaction, int unknown); // passed 0 as unknown
extern CGError SLSTransactionSetWindowTransform(CFTypeRef transaction, uint32_t wid, int unknown, int unknown2, CGAffineTransform t);
extern CGError SLSTransactionMoveWindowWithGroup(CFTypeRef transaction, uint32_t wid, CGPoint point);

struct window_animation_context
{
uint32_t wid;
float x, y, w, h;
CGRect frame;
};

struct window_fade_context
{
pthread_t thread;
Expand Down Expand Up @@ -614,8 +626,7 @@ static void do_window_animate_frame(char *message)

CGRect frame = {};
CGSGetWindowBounds(_connection, wid, &frame);
CGAffineTransform original_transform = CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y);
CGSSetWindowTransform(_connection, wid, original_transform);
CGSSetWindowTransform(_connection, wid, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y));

int frame_duration = 4;
int total_duration = (int)(animation_duration * 1000.0f);
Expand Down Expand Up @@ -646,12 +657,94 @@ static void do_window_animate_frame(char *message)
CGSSetWindowTransform(_connection, wid, CGAffineTransformConcat(transform, scale));
}

static void do_window_list_animate(char *message)
{
int window_count;
unpack(message, window_count);
if (!window_count) return;

struct window_animation_context *window_list = malloc(sizeof(struct window_animation_context) * window_count);
for (int i = 0; i < window_count; ++i) {
unpack(message, window_list[i].wid);
unpack(message, window_list[i].x);
unpack(message, window_list[i].y);
unpack(message, window_list[i].w);
unpack(message, window_list[i].h);
}

float animation_duration;
unpack(message, animation_duration);

CFTypeRef transaction1 = SLSTransactionCreate(_connection);
for (int i = 0; i < window_count; ++i) {
CGSGetWindowBounds(_connection, window_list[i].wid, &window_list[i].frame);
SLSTransactionSetWindowTransform(transaction1, window_list[i].wid, 0, 0, CGAffineTransformMakeTranslation(-window_list[i].frame.origin.x, -window_list[i].frame.origin.y));
}
SLSTransactionCommit(transaction1, 0);
CFRelease(transaction1);

int frame_duration = 4;
int total_duration = (int)(animation_duration * 1000.0f);
int frame_count = (int)(((float) total_duration / (float) frame_duration) + 0.5f);

int duration = 0;
for (int i = 0; i < frame_count; ++i) {
float t = (float) duration / (float) total_duration;
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;

float mt = ease_in_out_back(t);

CFTypeRef transaction2 = SLSTransactionCreate(_connection);
for (int j = 0; j < window_count; ++j) {
float target_x = lerp(window_list[j].frame.origin.x, mt, window_list[j].x);
float target_y = lerp(window_list[j].frame.origin.y, mt, window_list[j].y);
float target_w = lerp(window_list[j].frame.size.width, mt, window_list[j].w);
float target_h = lerp(window_list[j].frame.size.height, mt, window_list[j].h);

CGAffineTransform transform = CGAffineTransformMakeTranslation(-target_x, -target_y);
CGAffineTransform scale = CGAffineTransformMakeScale(window_list[j].frame.size.width / target_w, window_list[j].frame.size.height / target_h);
SLSTransactionSetWindowTransform(transaction2, window_list[j].wid, 0, 0, CGAffineTransformConcat(transform, scale));
}
SLSTransactionCommit(transaction2, 0);
CFRelease(transaction2);

duration += frame_duration;
usleep(frame_duration*1000);
}

CFTypeRef transaction3 = SLSTransactionCreate(_connection);
for (int i = 0; i < window_count; ++i) {
CGAffineTransform transform = CGAffineTransformMakeTranslation(-window_list[i].x, -window_list[i].y);
CGAffineTransform scale = CGAffineTransformMakeScale(window_list[i].frame.size.width / window_list[i].w, window_list[i].frame.size.height / window_list[i].h);
SLSTransactionSetWindowTransform(transaction3, window_list[i].wid, 0, 0, CGAffineTransformConcat(transform, scale));
}
SLSTransactionCommit(transaction3, 0);
CFRelease(transaction3);

free(window_list);
}

static void do_window_transform_reset(char *message)
{
uint32_t wid;
unpack(message, wid);
if (!wid) return;

#if 0
float x, y;
unpack(message, x);
unpack(message, y);

CGRect frame = {};
CGSGetWindowBounds(_connection, wid, &frame);

CFTypeRef transaction = SLSTransactionCreate(_connection);
SLSTransactionSetWindowTransform(transaction, wid, 0, 0, CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y));
SLSTransactionMoveWindowWithGroup(transaction, wid, CGPointMake(x, y));
SLSTransactionCommit(transaction, 0);
CFRelease(transaction);
#else
float x, y;
unpack(message, x);
unpack(message, y);
Expand All @@ -667,6 +760,7 @@ static void do_window_transform_reset(char *message)

CGAffineTransform original_transform = CGAffineTransformMakeTranslation(-frame.origin.x, -frame.origin.y);
CGSSetWindowTransform(_connection, wid, original_transform);
#endif
}

static void do_window_move(char *message)
Expand Down Expand Up @@ -912,6 +1006,9 @@ static void handle_message(int sockfd, char *message)
case 0x0F: {
do_window_transform_reset(message);
} break;
case 0x10: {
do_window_list_animate(message);
} break;
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/sa.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define PAYLOAD_STATUS_NO_ATTRIB 2
#define PAYLOAD_STATUS_CON_ERROR 3

struct window_animation;

int scripting_addition_check(void);
int scripting_addition_load(void);
bool scripting_addition_is_installed(void);
Expand All @@ -25,6 +27,7 @@ bool scripting_addition_focus_window(uint32_t wid);
bool scripting_addition_scale_window(uint32_t wid, float x, float y, float w, float h);
bool scripting_addition_animate_frame(uint32_t wid, float x, float y, float w, float h, float duration);
bool scripting_addition_reset_transform(uint32_t wid, float x, float y);
bool scripting_addition_animate_window_list(struct window_animation *window_list, int window_count, float duration);

extern bool workspace_is_macos_monterey(void);
extern bool workspace_is_macos_bigsur(void);
Expand Down
23 changes: 23 additions & 0 deletions src/sa.m
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,26 @@ bool scripting_addition_reset_transform(uint32_t wid, float x, float y)

return scripting_addition_send_bytes(bytes, length);
}

bool scripting_addition_animate_window_list(struct window_animation *window_list, int window_count, float duration)
{
char bytes[0x100];

char length = 2;
pack(bytes, window_count, length);

// NOTE(koekeishiya): We can only send bytes to animate 12 windows for now.
for (int i = 0; i < window_count && i < 12; ++i) {
pack(bytes, window_list[i].window->id, length);
pack(bytes, window_list[i].area.x, length);
pack(bytes, window_list[i].area.y, length);
pack(bytes, window_list[i].area.w, length);
pack(bytes, window_list[i].area.h, length);
}

pack(bytes, duration, length);
bytes[1] = 0x10;
bytes[0] = length-1;

return scripting_addition_send_bytes(bytes, length);
}
18 changes: 13 additions & 5 deletions src/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -303,27 +303,35 @@ static void window_node_clear_zoom(struct window_node *node)
}
}

void window_node_flush(struct window_node *node)
void window_node_capture_windows(struct window_node *node, struct window_animation **window_list)
{
if (window_node_is_occupied(node)) {
for (int i = 0; i < node->window_count; ++i) {
struct window *window = window_manager_find_window(&g_window_manager, node->window_list[i]);
if (window) {
if (node->zoom) {
window_manager_animate_window_frame(window, node->zoom->area.x, node->zoom->area.y, node->zoom->area.w, node->zoom->area.h);
buf_push(*window_list, ((struct window_animation) { .window = window, .area = node->zoom->area }));
} else {
window_manager_animate_window_frame(window, node->area.x, node->area.y, node->area.w, node->area.h);
buf_push(*window_list, ((struct window_animation) { .window = window, .area = node->area }));
}
}
}
}

if (!window_node_is_leaf(node)) {
window_node_flush(node->left);
window_node_flush(node->right);
window_node_capture_windows(node->left, window_list);
window_node_capture_windows(node->right, window_list);
}
}

void window_node_flush(struct window_node *node)
{
struct window_animation *window_list = NULL;
window_node_capture_windows(node, &window_list);
window_manager_animate_window_list(window_list, buf_len(window_list));
buf_free(window_list);
}

bool window_node_contains_window(struct window_node *node, uint32_t window_id)
{
for (int i = 0; i < node->window_count; ++i) {
Expand Down
6 changes: 6 additions & 0 deletions src/view.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ struct area
float h;
};

struct window_animation
{
struct window *window;
struct area area;
};

struct equalize_node
{
int y_count;
Expand Down
31 changes: 31 additions & 0 deletions src/window_manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,21 @@ void window_manager_resize_window(struct window *window, float width, float heig
CFRelease(size_ref);
}

void window_manager_animate_window_list(struct window_animation *window_list, int window_count)
{
if (g_window_manager.window_animation_duration) {
scripting_addition_animate_window_list(window_list, window_count, g_window_manager.window_animation_duration);
for (int i = 0; i < window_count; ++i) {
window_manager_set_window_frame(window_list[i].window, window_list[i].area.x, window_list[i].area.y, window_list[i].area.w, window_list[i].area.h);
scripting_addition_reset_transform(window_list[i].window->id, window_list[i].area.x, window_list[i].area.y);
}
} else {
for (int i = 0; i < window_count; ++i) {
window_manager_set_window_frame(window_list[i].window, window_list[i].area.x, window_list[i].area.y, window_list[i].area.w, window_list[i].area.h);
}
}
}

void window_manager_animate_window_frame(struct window *window, float x, float y, float width, float height)
{
if (g_window_manager.window_animation_duration) {
Expand Down Expand Up @@ -1313,8 +1328,16 @@ enum window_op_error window_manager_warp_window(struct space_manager *sm, struct

window_node_swap_window_list(a_node, b_node);

#if 0
window_node_flush(a_node);
window_node_flush(b_node);
#else
struct window_animation *window_list = NULL;
window_node_capture_windows(a_node, &window_list);
window_node_capture_windows(b_node, &window_list);
window_manager_animate_window_list(window_list, buf_len(window_list));
buf_free(window_list);
#endif
}
} else {
space_manager_untile_window(sm, a_view, a);
Expand Down Expand Up @@ -1391,8 +1414,16 @@ enum window_op_error window_manager_swap_window(struct space_manager *sm, struct
}
}

#if 0
window_node_flush(a_node);
window_node_flush(b_node);
#else
struct window_animation *window_list = NULL;
window_node_capture_windows(a_node, &window_list);
window_node_capture_windows(b_node, &window_list);
window_manager_animate_window_list(window_list, buf_len(window_list));
buf_free(window_list);
#endif

return WINDOW_OP_ERROR_SUCCESS;
}
Expand Down
1 change: 1 addition & 0 deletions src/window_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ void window_manager_move_window(struct window *window, float x, float y);
void window_manager_resize_window(struct window *window, float width, float height);
enum window_op_error window_manager_adjust_window_ratio(struct window_manager *wm, struct window *window, int action, float ratio);
void window_manager_animate_window_frame(struct window *window, float x, float y, float width, float height);
void window_manager_animate_window_list(struct window_animation *window_list, int window_count);
void window_manager_set_window_frame(struct window *window, float x, float y, float width, float height);
int window_manager_find_rank_of_window_in_list(uint32_t wid, uint32_t *window_list, int window_count);
struct window *window_manager_find_window_on_space_by_rank_filtering_window(struct window_manager *wm, uint64_t sid, int rank, uint32_t filter_wid);
Expand Down

0 comments on commit 45e18e0

Please sign in to comment.