diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ac4d03e..19dd9706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed - Don't draw borders for minimized or hidden windows when a display is (dis)connected [#250](https://github.com/koekeishiya/yabai/issues/250) - Sticky windows are no longer automatically topmost. New option to toggle window always on top through command or rule. New attribute topmost returned in window queries. [#255](https://github.com/koekeishiya/yabai/issues/255) +- Prevent the last user-space from being destroyed or moved to another display, because macOS does not actually support this [#182](https://github.com/koekeishiya/yabai/issues/182) ## [2.0.1] - 2019-09-04 ### Changed diff --git a/src/message.c b/src/message.c index a0ddacda..4bd3057c 100644 --- a/src/message.c +++ b/src/message.c @@ -1201,12 +1201,28 @@ static void handle_domain_space(FILE *rsp, struct token domain, char *message) } else if (token_equals(command, COMMAND_SPACE_DISPLAY)) { struct selector selector = parse_display_selector(rsp, &message, display_manager_active_display_id()); if (selector.did_parse && selector.did) { - space_manager_move_space_to_display(&g_space_manager, acting_sid, selector.did); + enum space_op_error result = space_manager_move_space_to_display(&g_space_manager, acting_sid, selector.did); + if (result == SPACE_OP_ERROR_MISSING_SRC) { + daemon_fail(rsp, "could not locate the space to act on.\n"); + } else if (result == SPACE_OP_ERROR_MISSING_DST) { + daemon_fail(rsp, "could not locate the active space of the given display.\n"); + } else if (result == SPACE_OP_ERROR_INVALID_SRC) { + daemon_fail(rsp, "acting space is the last user-space on the source display and cannot be moved.\n"); + } else if (result == SPACE_OP_ERROR_INVALID_DST) { + daemon_fail(rsp, "acting space is already located on the given display.\n"); + } } } else if (token_equals(command, COMMAND_SPACE_CREATE)) { space_manager_add_space(acting_sid); } else if (token_equals(command, COMMAND_SPACE_DESTROY)) { - space_manager_destroy_space(acting_sid); + enum space_op_error result = space_manager_destroy_space(acting_sid); + if (result == SPACE_OP_ERROR_MISSING_SRC) { + daemon_fail(rsp, "could not locate the space to act on.\n"); + } else if (result == SPACE_OP_ERROR_INVALID_SRC) { + daemon_fail(rsp, "acting space is the last user-space on the source display and cannot be destroyed.\n"); + } else if (result == SPACE_OP_ERROR_INVALID_TYPE) { + daemon_fail(rsp, "cannot destroy a macOS fullscreen space.\n"); + } } else if (token_equals(command, COMMAND_SPACE_BALANCE)) { space_manager_balance_space(&g_space_manager, acting_sid); } else if (token_equals(command, COMMAND_SPACE_MIRROR)) { diff --git a/src/space_manager.c b/src/space_manager.c index 3f354616..a36987c8 100644 --- a/src/space_manager.c +++ b/src/space_manager.c @@ -619,17 +619,41 @@ void space_manager_move_space_after_space(uint64_t src_sid, uint64_t dst_sid, bo socket_close(sockfd); } -void space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, uint32_t did) +static inline bool +space_manager_is_space_last_user_space(uint64_t sid) +{ + bool result = true; + + int count; + uint64_t *space_list = display_space_list(space_display_id(sid), &count); + if (!space_list) return true; + + for (int i = 0; i < count; ++i) { + uint64_t c_sid = space_list[i]; + if (sid == c_sid) continue; + + if (space_is_user(c_sid)) { + result = false; + break; + } + } + free(space_list); + + return result; +} + +enum space_op_error space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, uint32_t did) { int sockfd; uint64_t d_sid; char message[MAXLEN]; - if (!sid) return; - if (space_display_id(sid) == did) return; + if (!sid) return SPACE_OP_ERROR_MISSING_SRC; + if (space_display_id(sid) == did) return SPACE_OP_ERROR_INVALID_DST; + if (space_manager_is_space_last_user_space(sid)) return SPACE_OP_ERROR_INVALID_SRC; d_sid = display_space_id(did); - if (!d_sid) return; + if (!d_sid) return SPACE_OP_ERROR_MISSING_DST; if (socket_connect_un(&sockfd, g_sa_socket_file)) { snprintf(message, sizeof(message), "space_move %lld %lld 1", sid, d_sid); @@ -640,14 +664,18 @@ void space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, space_manager_mark_view_invalid(sm, sid); space_manager_focus_space(sid); + + return SPACE_OP_ERROR_SUCCESS; } -void space_manager_destroy_space(uint64_t sid) +enum space_op_error space_manager_destroy_space(uint64_t sid) { int sockfd; char message[MAXLEN]; - if (!sid || !space_is_user(sid)) return; + if (!sid) return SPACE_OP_ERROR_MISSING_SRC; + if (!space_is_user(sid)) return SPACE_OP_ERROR_INVALID_TYPE; + if (space_manager_is_space_last_user_space(sid)) return SPACE_OP_ERROR_INVALID_SRC; if (socket_connect_un(&sockfd, g_sa_socket_file)) { snprintf(message, sizeof(message), "space_destroy %lld", sid); @@ -655,6 +683,8 @@ void space_manager_destroy_space(uint64_t sid) socket_wait(sockfd); } socket_close(sockfd); + + return SPACE_OP_ERROR_SUCCESS; } void space_manager_add_space(uint64_t sid) diff --git a/src/space_manager.h b/src/space_manager.h index d09c80e5..3a505c79 100644 --- a/src/space_manager.h +++ b/src/space_manager.h @@ -27,6 +27,16 @@ struct space_manager bool auto_balance; }; +enum space_op_error +{ + SPACE_OP_ERROR_SUCCESS = 0, + SPACE_OP_ERROR_MISSNG_SRC = 1, + SPACE_OP_ERROR_MISSNG_DST = 2, + SPACE_OP_ERROR_INVALID_SRC = 3, + SPACE_OP_ERROR_INVALID_DST = 4, + SPACE_OP_ERROR_INVALID_TYPE = 5, +}; + bool space_manager_has_separate_spaces(void); bool space_manager_query_active_space(FILE *rsp); bool space_manager_query_spaces_for_window(FILE *rsp, struct window *window); @@ -69,8 +79,8 @@ void space_manager_remove_window_from_space(uint64_t sid, struct window *window) void space_manager_add_window_to_space(uint64_t sid, struct window *window); void space_manager_focus_space(uint64_t sid); void space_manager_move_space_after_space(uint64_t src_sid, uint64_t dst_sid, bool focus); -void space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, uint32_t did); -void space_manager_destroy_space(uint64_t sid); +enum space_op_error space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, uint32_t did); +enum space_op_error space_manager_destroy_space(uint64_t sid); void space_manager_add_space(uint64_t sid); void space_manager_assign_process_to_space(pid_t pid, uint64_t sid); void space_manager_assign_process_to_all_spaces(pid_t pid);