text: #include "space_manager.h"
  
  extern struct window_manager g_window_manager;
  extern char g_sa_socket_file[MAXLEN];
  extern bool g_mission_control_active;
  extern int g_connection;
  
  static TABLE_HASH_FUNC(hash_view)
  {
      unsigned long result = *(uint64_t *) key;
      result = (result + 0x7ed55d16) + (result << 12);
      result = (result ^ 0xc761c23c) ^ (result >> 19);
      result = (result + 0x165667b1) + (result << 5);
      result = (result + 0xd3a2646c) ^ (result << 9);
      result = (result + 0xfd7046c5) + (result << 3);
      result = (result ^ 0xb55a4f09) ^ (result >> 16);
      return result;
  }
  
  static TABLE_COMPARE_FUNC(compare_view)
  {
      return *(uint64_t *) key_a == *(uint64_t *) key_b;
  }
  
  bool space_manager_has_separate_spaces(void)
  {
      return SLSGetSpaceManagementMode(g_connection) == 1;
  }
  
  bool space_manager_query_active_space(FILE *rsp)
  {
      struct view *view = space_manager_query_view(&g_space_manager, space_manager_active_space());
      if (!view) return false;
  
      view_serialize(rsp, view);
      fprintf(rsp, "\n");
      return true;
  }
  
  bool space_manager_query_spaces_for_window(FILE *rsp, struct window *window)
  {
      int space_count;
      uint64_t *space_list = window_space_list(window, &space_count);
      if (!space_list) return false;
  
      fprintf(rsp, "[");
      for (int i = 0; i < space_count; ++i) {
          struct view *view = space_manager_query_view(&g_space_manager, space_list[i]);
          if (!view) continue;
  
          view_serialize(rsp, view);
          fprintf(rsp, "%c", i < space_count - 1 ? ',' : ']');
      }
      fprintf(rsp, "\n");
  
      free(space_list);
      return true;
  }
  
  bool space_manager_query_spaces_for_display(FILE *rsp, uint32_t did)
  {
      int space_count;
      uint64_t *space_list = display_space_list(did, &space_count);
      if (!space_list) return false;
  
      fprintf(rsp, "[");
      for (int i = 0; i < space_count; ++i) {
          struct view *view = space_manager_query_view(&g_space_manager, space_list[i]);
          if (!view) continue;
  
          view_serialize(rsp, view);
          fprintf(rsp, "%c", i < space_count - 1 ? ',' : ']');
      }
      fprintf(rsp, "\n");
  
      free(space_list);
      return true;
  }
  
  bool space_manager_query_spaces_for_displays(FILE *rsp)
  {
      uint32_t display_count;
      uint32_t *display_list = display_manager_active_display_list(&display_count);
      if (!display_list) return false;
  
      fprintf(rsp, "[");
      for (int i = 0; i < display_count; ++i) {
          int space_count;
          uint64_t *space_list = display_space_list(display_list[i], &space_count);
          if (!space_list) continue;
  
          for (int j = 0; j < space_count; ++j) {
              struct view *view = space_manager_query_view(&g_space_manager, space_list[j]);
              if (!view) continue;
  
              view_serialize(rsp, view);
              if (j < space_count - 1) fprintf(rsp, ",");
          }
  
          free(space_list);
          fprintf(rsp, "%c", i < display_count - 1 ? ',' : ']');
      }
      fprintf(rsp, "\n");
  
      free(display_list);
      return true;
  }
  
  struct view *space_manager_query_view(struct space_manager *sm, uint64_t sid)
  {
      if (sm->did_begin) return space_manager_find_view(sm, sid);
      return table_find(&sm->view, &sid);
  }
  
  struct view *space_manager_find_view(struct space_manager *sm, uint64_t sid)
  {
      struct view *view = table_find(&sm->view, &sid);
      if (!view) {
          view = view_create(sid);
          table_add(&sm->view, &sid, view);
      }
      return view;
  }
  
  void space_manager_refresh_view(struct space_manager *sm, uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return;
  
      view_update(view);
      view_flush(view);
  }
  
  void space_manager_mark_view_invalid(struct space_manager *sm,  uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return;
  
      view->is_valid = false;
  }
  
  void space_manager_mark_view_dirty(struct space_manager *sm,  uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return;
  
      view->is_dirty = true;
  }
  
  void space_manager_untile_window(struct space_manager *sm, struct view *view, struct window *window)
  {
      if (view->layout != VIEW_BSP) return;
  
      view_remove_window_node(view, window);
      view_flush(view);
  
      if (!space_is_visible(view->sid)) {
          view->is_dirty = true;
      }
  }
  
  struct space_label *space_manager_get_label_for_space(struct space_manager *sm, uint64_t sid)
  {
      for (int i = 0; i < buf_len(sm->labels); ++i) {
          struct space_label *space_label = &sm->labels[i];
          if (space_label->sid == sid) {
              return space_label;
          }
      }
  
      return NULL;
  }
  
  struct space_label *space_manager_get_space_for_label(struct space_manager *sm, struct token label)
  {
      for (int i = 0; i < buf_len(sm->labels); ++i) {
          struct space_label *space_label = &sm->labels[i];
          if (token_equals(label, space_label->label)) {
              return space_label;
          }
      }
  
      return NULL;
  }
  
  void space_manager_set_label_for_space(struct space_manager *sm, uint64_t sid, char *label)
  {
      for (int i = 0; i < buf_len(sm->labels); ++i) {
          struct space_label *space_label = &sm->labels[i];
          if (space_label->sid == sid) {
              free(space_label->label);
              buf_del(sm->labels, i);
              break;
          }
      }
  
      for (int i = 0; i < buf_len(sm->labels); ++i) {
          struct space_label *space_label = &sm->labels[i];
          if (string_equals(space_label->label, label)) {
              free(space_label->label);
              buf_del(sm->labels, i);
              break;
          }
      }
  
      buf_push(sm->labels, ((struct space_label) {
          .sid   = sid,
          .label = label
      }));
  }
  
  void space_manager_set_layout_for_space(struct space_manager *sm, uint64_t sid, enum view_type layout)
  {
      struct view *view = space_manager_find_view(sm, sid);
      view->layout = layout;
  
      if (view->layout == VIEW_BSP) {
          window_manager_check_for_windows_on_space(sm, &g_window_manager, sid);
      } else if (view->layout == VIEW_FLOAT) {
          view_clear(view);
      }
  }
  
  bool space_manager_set_gap_for_space(struct space_manager *sm, uint64_t sid, int type, int gap)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      if (type == TYPE_ABS) {
          view->window_gap = gap;
      } else if (type == TYPE_REL) {
          view->window_gap = add_and_clamp_to_zero(view->window_gap, gap);
      }
  
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  bool space_manager_toggle_gap_for_space(struct space_manager *sm, uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      view->enable_gap = !view->enable_gap;
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  void space_manager_toggle_mission_control(uint64_t sid)
  {
      space_manager_focus_space(sid);
      CoreDockSendNotification(CFSTR("com.apple.expose.awake"), 0);
  }
  
  void space_manager_toggle_show_desktop(uint64_t sid)
  {
      space_manager_focus_space(sid);
      CoreDockSendNotification(CFSTR("com.apple.showdesktop.awake"), 0);
  }
  
  void space_manager_set_layout_for_all_spaces(struct space_manager *sm, enum view_type layout)
  {
      sm->layout = layout;
      for (int i = 0; i < sm->view.capacity; ++i) {
          struct bucket *bucket = sm->view.buckets[i];
          while (bucket) {
              if (bucket->value) {
                  struct view *view = bucket->value;
                  if (!view->custom_layout) {
                      if (space_is_user(view->sid)) {
                          view->layout = layout;
                          if (view->layout == VIEW_BSP) {
                              window_manager_check_for_windows_on_space(sm, &g_window_manager, view->sid);
                          } else if (view->layout == VIEW_FLOAT) {
                              view_clear(view);
                          }
                      }
                  }
              }
              bucket = bucket->next;
          }
      }
  }
  
  #define VIEW_SET_PROPERTY(p) \
      sm->p = p; \
      for (int i = 0; i < sm->view.capacity; ++i) { \
          struct bucket *bucket = sm->view.buckets[i]; \
          while (bucket) { \
              if (bucket->value) { \
                  struct view *view = bucket->value; \
                  if (!view->custom_##p) view->p = p; \
                  view_update(view); \
                  view_flush(view); \
              } \
              bucket = bucket->next; \
          } \
      }
  
  void space_manager_set_window_gap_for_all_spaces(struct space_manager *sm, int window_gap)
  {
      VIEW_SET_PROPERTY(window_gap);
  }
  
  void space_manager_set_top_padding_for_all_spaces(struct space_manager *sm, int top_padding)
  {
      VIEW_SET_PROPERTY(top_padding);
  }
  
  void space_manager_set_bottom_padding_for_all_spaces(struct space_manager *sm, int bottom_padding)
  {
      VIEW_SET_PROPERTY(bottom_padding);
  }
  
  void space_manager_set_left_padding_for_all_spaces(struct space_manager *sm, int left_padding)
  {
      VIEW_SET_PROPERTY(left_padding);
  }
  
  void space_manager_set_right_padding_for_all_spaces(struct space_manager *sm, int right_padding)
  {
      VIEW_SET_PROPERTY(right_padding);
  }
  
  #undef VIEW_SET_PROPERTY
  
  bool space_manager_set_padding_for_space(struct space_manager *sm, uint64_t sid, int type, int top, int bottom, int left, int right)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      if (type == TYPE_ABS) {
          view->top_padding    = top;
          view->bottom_padding = bottom;
          view->left_padding   = left;
          view->right_padding  = right;
      } else if (type == TYPE_REL) {
          view->top_padding    = add_and_clamp_to_zero(view->top_padding, top);
          view->bottom_padding = add_and_clamp_to_zero(view->bottom_padding, bottom);
          view->left_padding   = add_and_clamp_to_zero(view->left_padding, left);
          view->right_padding  = add_and_clamp_to_zero(view->right_padding, right);
      }
  
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  bool space_manager_toggle_padding_for_space(struct space_manager *sm, uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      view->enable_padding = !view->enable_padding;
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  bool space_manager_rotate_space(struct space_manager *sm, uint64_t sid, int degrees)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      window_node_rotate(view->root, degrees);
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  bool space_manager_mirror_space(struct space_manager *sm, uint64_t sid, enum window_node_split axis)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      window_node_mirror(view->root, axis);
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  bool space_manager_balance_space(struct space_manager *sm, uint64_t sid)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return false;
  
      window_node_equalize(view->root);
      view_update(view);
      view_flush(view);
  
      return true;
  }
  
  struct view *space_manager_tile_window_on_space_with_insertion_point(struct space_manager *sm, struct window *window, uint64_t sid, uint32_t insertion_point)
  {
      struct view *view = space_manager_find_view(sm, sid);
      if (view->layout != VIEW_BSP) return view;
  
      if (insertion_point) view->insertion_point = insertion_point;
      view_add_window_node(view, window);
      view_flush(view);
  
      if (!space_is_visible(view->sid)) {
          view->is_dirty = true;
      }
  
      return view;
  }
  
  struct view *space_manager_tile_window_on_space(struct space_manager *sm, struct window *window, uint64_t sid)
  {
      return space_manager_tile_window_on_space_with_insertion_point(sm, window, sid, 0);
  }
  
  void space_manager_toggle_window_split(struct space_manager *sm, struct window *window)
  {
      struct view *view = space_manager_find_view(sm, space_manager_active_space());
      if (view->layout != VIEW_BSP) return;
  
      struct window_node *node = view_find_window_node(view, window->id);
      if (node && window_node_is_intermediate(node)) {
          node->parent->split = node->parent->split == SPLIT_Y ? SPLIT_X : SPLIT_Y;
  
          if (sm->auto_balance) {
              window_node_equalize(view->root);
              view_update(view);
              view_flush(view);
          } else {
              window_node_update(view, node->parent);
              window_node_flush(node->parent);
          }
      }
  }
  
  int space_manager_mission_control_index(uint64_t sid)
  {
      uint64_t result = 0;
      int desktop_cnt = 1;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      int display_spaces_count = CFArrayGetCount(display_spaces_ref);
  
      for (int i = 0; i < display_spaces_count; ++i) {
          CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);
          CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
          int spaces_count = CFArrayGetCount(spaces_ref);
  
          for (int j = 0; j < spaces_count; ++j) {
              CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);
              CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
              CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &result);
              if (sid == result) goto out;
  
              ++desktop_cnt;
          }
      }
  
      desktop_cnt = 0;
  out:
      CFRelease(display_spaces_ref);
      return desktop_cnt;
  }
  
  uint64_t space_manager_mission_control_space(int desktop_id)
  {
      uint64_t result = 0;
      int desktop_cnt = 1;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      int display_spaces_count = CFArrayGetCount(display_spaces_ref);
  
      for (int i = 0; i < display_spaces_count; ++i) {
          CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);
          CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
          int spaces_count = CFArrayGetCount(spaces_ref);
  
          for (int j = 0; j < spaces_count; ++j) {
              CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);
              CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
              CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &result);
              if (desktop_cnt == desktop_id) goto out;
  
              ++desktop_cnt;
          }
      }
  
      result = 0;
  out:
      CFRelease(display_spaces_ref);
      return result;
  }
  
  uint64_t space_manager_prev_space(uint64_t sid)
  {
      uint64_t p_sid = 0;
      uint64_t n_sid = 0;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      int display_spaces_count = CFArrayGetCount(display_spaces_ref);
  
      for (int i = 0; i < display_spaces_count; ++i) {
          CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);
          CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
          int spaces_count = CFArrayGetCount(spaces_ref);
  
          for (int j = 0; j < spaces_count; ++j) {
              CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);
              CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
              CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &n_sid);
              if (n_sid == sid) goto out;
  
              p_sid = n_sid;
          }
      }
  
  out:
      CFRelease(display_spaces_ref);
      return p_sid != sid ? p_sid : 0;
  }
  
  uint64_t space_manager_next_space(uint64_t sid)
  {
      uint64_t n_sid = 0;
      bool found_sid = false;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      int display_spaces_count = CFArrayGetCount(display_spaces_ref);
  
      for (int i = 0; i < display_spaces_count; ++i) {
          CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);
          CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
          int spaces_count = CFArrayGetCount(spaces_ref);
  
          for (int j = 0; j < spaces_count; ++j) {
              CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);
              CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
              CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &n_sid);
              if (found_sid) goto out;
  
              found_sid = n_sid == sid;
          }
      }
  
  out:
      CFRelease(display_spaces_ref);
      return n_sid != sid ? n_sid : 0;
  }
  
  uint64_t space_manager_first_space(void)
  {
      uint64_t sid = 0;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, 0);
      CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
  
      CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, 0);
      CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
      CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &sid);
  
      CFRelease(display_spaces_ref);
      return sid;
  }
  
  uint64_t space_manager_last_space(void)
  {
      uint64_t sid = 0;
  
      CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);
      int display_spaces_count = CFArrayGetCount(display_spaces_ref);
  
      CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, display_spaces_count-1);
      CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR("Spaces"));
      int spaces_count = CFArrayGetCount(spaces_ref);
  
      CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, spaces_count-1);
      CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR("id64"));
      CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &sid);
  
      CFRelease(display_spaces_ref);
      return sid;
  }
  
  uint64_t space_manager_active_space(void)
  {
      uint32_t did = 0;
      struct window *window = window_manager_focused_window(&g_window_manager);
  
      if (window) did = window_display_id(window);
      if (!did)   did = display_manager_active_display_id();
      if (!did)   return 0;
  
      return display_space_id(did);
  }
  
  void space_manager_move_window_to_space(uint64_t sid, struct window *window)
  {
      uint32_t window_list[2] = { window->id, window->border.id };
      int window_count = window->border.id ? 2 : 1;
      CFArrayRef window_list_ref = cfarray_of_cfnumbers(window_list, sizeof(uint32_t), window_count, kCFNumberSInt32Type);
      SLSMoveWindowsToManagedSpace(g_connection, window_list_ref, sid);
      CFRelease(window_list_ref);
  }
  
  enum space_op_error space_manager_focus_space(uint64_t sid)
  {
      int sockfd;
      char message[MAXLEN];
  
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
  
      uint64_t cur_sid = space_manager_active_space();
      if (cur_sid == sid) return SPACE_OP_ERROR_SAME_SPACE;
  
      uint32_t cur_did = space_display_id(cur_sid);
      uint32_t new_did = space_display_id(sid);
      bool focus_display = cur_did != new_did;
  
      bool is_animating = display_manager_display_is_animating(new_did);
      if (is_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      if (socket_connect_un(&sockfd, g_sa_socket_file)) {
          snprintf(message, sizeof(message), "space %lld", sid);
          socket_write(sockfd, message);
          socket_wait(sockfd);
  
          if (focus_display) display_manager_focus_display(new_did);
      }
      socket_close(sockfd);
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  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;
  }
  
  static void space_manager_move_space_after_space(uint64_t src_sid, uint64_t dst_sid, bool focus)
  {
      int sockfd;
      char message[MAXLEN];
  
      if (!src_sid) return;
      if (!dst_sid) return;
  
      if (socket_connect_un(&sockfd, g_sa_socket_file)) {
          snprintf(message, sizeof(message), "space_move %lld %lld %d", src_sid, dst_sid, focus);
          socket_write(sockfd, message);
          socket_wait(sockfd);
      }
      socket_close(sockfd);
  }
  
  enum space_op_error space_manager_swap_space_with_space(uint64_t acting_sid, uint64_t selector_sid)
  {
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
  
      uint32_t acting_did = space_display_id(acting_sid);
      uint32_t selector_did = space_display_id(selector_sid);
  
      if (acting_sid == selector_sid) return SPACE_OP_ERROR_SAME_SPACE;
      if (acting_did != selector_did) return SPACE_OP_ERROR_SAME_DISPLAY;
  
      bool is_animating = display_manager_display_is_animating(acting_did);
      if (is_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      uint64_t acting_prev_sid = space_manager_prev_space(acting_sid);
      uint64_t selector_prev_sid = space_manager_prev_space(selector_sid);
  
      uint32_t acting_prev_did = acting_prev_sid ? space_display_id(acting_prev_sid) : 0;
      uint32_t selector_prev_did = selector_prev_sid ? space_display_id(selector_prev_sid) : 0;
  
      bool acting_sid_is_first = !acting_prev_sid || acting_prev_did != acting_did;
      bool selector_sid_is_first = !selector_prev_sid || selector_prev_did != selector_did;
  
      int acting_mci = space_manager_mission_control_index(acting_sid);
      int selector_mci = space_manager_mission_control_index(selector_sid);
  
      if (acting_sid_is_first && !selector_sid_is_first && selector_mci - acting_mci == 1) {
          space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
      } else if (!acting_sid_is_first && selector_sid_is_first && acting_mci - selector_mci == 1) {
          space_manager_move_space_after_space(selector_sid, acting_sid, selector_sid == space_manager_active_space());
      } else if (acting_sid_is_first && !selector_sid_is_first) {
          space_manager_move_space_after_space(selector_sid, acting_sid, false);
          space_manager_move_space_after_space(acting_sid, selector_prev_sid, acting_sid == space_manager_active_space());
      } else if (!acting_sid_is_first && selector_sid_is_first) {
          space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
          space_manager_move_space_after_space(selector_sid, acting_prev_sid, false);
      } else if (!acting_sid_is_first && !selector_sid_is_first) {
          if (acting_mci > selector_mci) {
              space_manager_move_space_after_space(selector_sid, acting_sid, false);
              space_manager_move_space_after_space(acting_sid, selector_prev_sid, acting_sid == space_manager_active_space());
          } else {
              space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
              space_manager_move_space_after_space(selector_sid, acting_prev_sid, false);
          }
      }
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  enum space_op_error space_manager_move_space_to_space(uint64_t acting_sid, uint64_t selector_sid)
  {
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
  
      uint32_t acting_did = space_display_id(acting_sid);
      uint32_t selector_did = space_display_id(selector_sid);
  
      if (acting_sid == selector_sid) return SPACE_OP_ERROR_SAME_SPACE;
      if (acting_did != selector_did) return SPACE_OP_ERROR_SAME_DISPLAY;
  
      bool is_animating = display_manager_display_is_animating(acting_did);
      if (is_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      uint64_t acting_prev_sid = space_manager_prev_space(acting_sid);
      uint64_t selector_prev_sid = space_manager_prev_space(selector_sid);
  
      uint32_t acting_prev_did = acting_prev_sid ? space_display_id(acting_prev_sid) : 0;
      uint32_t selector_prev_did = selector_prev_sid ? space_display_id(selector_prev_sid) : 0;
  
      bool acting_sid_is_first = !acting_prev_sid || acting_prev_did != acting_did;
      bool selector_sid_is_first = !selector_prev_sid || selector_prev_did != selector_did;
  
      if (acting_sid_is_first && !selector_sid_is_first) {
          space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
      } else if (!acting_sid_is_first && selector_sid_is_first) {
          space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
          space_manager_move_space_after_space(selector_sid, acting_sid, false);
      } else if (!acting_sid_is_first && !selector_sid_is_first) {
          if (space_manager_mission_control_index(acting_sid) > space_manager_mission_control_index(selector_sid)) {
              space_manager_move_space_after_space(acting_sid, selector_prev_sid, acting_sid == space_manager_active_space());
          } else {
              space_manager_move_space_after_space(acting_sid, selector_sid, acting_sid == space_manager_active_space());
          }
      }
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  enum space_op_error space_manager_move_space_to_display(struct space_manager *sm, uint64_t sid, uint32_t did)
  {
      int sockfd;
      char message[MAXLEN];
  
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
      if (!sid)     return SPACE_OP_ERROR_MISSING_SRC;
  
      uint32_t s_did = space_display_id(sid);
      if (s_did == did) return SPACE_OP_ERROR_INVALID_DST;
  
      bool is_src_animating = display_manager_display_is_animating(s_did);
      if (is_src_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      bool last_space = space_manager_is_space_last_user_space(sid);
      if (last_space) return SPACE_OP_ERROR_INVALID_SRC;
  
      bool is_dst_animating = display_manager_display_is_animating(did);
      if (is_dst_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      uint64_t d_sid = display_space_id(did);
      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);
          socket_write(sockfd, message);
          socket_wait(sockfd);
      }
      socket_close(sockfd);
  
      space_manager_mark_view_invalid(sm, sid);
      space_manager_focus_space(sid);
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  enum space_op_error space_manager_destroy_space(uint64_t sid)
  {
      int sockfd;
      char message[MAXLEN];
  
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
  
      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;
  
      bool is_animating = display_manager_display_is_animating(space_display_id(sid));
      if (is_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      if (socket_connect_un(&sockfd, g_sa_socket_file)) {
          snprintf(message, sizeof(message), "space_destroy %lld", sid);
          socket_write(sockfd, message);
          socket_wait(sockfd);
      }
      socket_close(sockfd);
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  enum space_op_error space_manager_add_space(uint64_t sid)
  {
      int sockfd;
      char message[MAXLEN];
  
      bool is_in_mc = g_mission_control_active;
      if (is_in_mc) return SPACE_OP_ERROR_IN_MISSION_CONTROL;
      if (!sid)     return SPACE_OP_ERROR_MISSING_SRC;
  
      bool is_animating = display_manager_display_is_animating(space_display_id(sid));
      if (is_animating) return SPACE_OP_ERROR_DISPLAY_IS_ANIMATING;
  
      if (socket_connect_un(&sockfd, g_sa_socket_file)) {
          snprintf(message, sizeof(message), "space_create %lld", sid);
          socket_write(sockfd, message);
          socket_wait(sockfd);
      }
      socket_close(sockfd);
  
      return SPACE_OP_ERROR_SUCCESS;
  }
  
  void space_manager_assign_process_to_space(pid_t pid, uint64_t sid)
  {
      SLSProcessAssignToSpace(g_connection, pid, sid);
  }
  
  void space_manager_assign_process_to_all_spaces(pid_t pid)
  {
      SLSProcessAssignToAllSpaces(g_connection, pid);
  }
  
  bool space_manager_is_window_on_active_space(struct window *window)
  {
      uint64_t sid = space_manager_active_space();
      bool result = space_manager_is_window_on_space(sid, window);
      return result;
  }
  
  bool space_manager_is_window_on_space(uint64_t sid, struct window *window)
  {
      bool result = false;
  
      int space_count;
      uint64_t *space_list = window_space_list(window, &space_count);
      if (!space_list) goto out;
  
      for (int i = 0; i < space_count; ++i) {
          if (sid == space_list[i]) {
              result = true;
              break;
          }
      }
  
      free(space_list);
  out:
      return result;
  }
  
  void space_manager_mark_spaces_invalid_for_display(struct space_manager *sm, uint32_t did)
  {
      int space_count;
      uint64_t *space_list = display_space_list(did, &space_count);
      if (!space_list) return;
  
      uint64_t sid = display_space_id(did);
      for (int i = 0; i < space_count; ++i) {
          if (space_list[i] == sid) {
              space_manager_refresh_view(sm, sid);
          } else {
              space_manager_mark_view_invalid(sm, space_list[i]);
          }
      }
  
      free(space_list);
  }
  
  void space_manager_mark_spaces_invalid(struct space_manager *sm)
  {
      uint32_t display_count = 0;
      uint32_t *display_list = display_manager_active_display_list(&display_count);
      if (!display_list) return;
  
      for (int i = 0; i < display_count; ++i) {
          space_manager_mark_spaces_invalid_for_display(sm, display_list[i]);
      }
  
      free(display_list);
  }
  
  bool space_manager_refresh_application_windows(struct space_manager *sm)
  {
      int window_count = g_window_manager.window.count;
      for (int i = 0; i < g_window_manager.application.capacity; ++i) {
          struct bucket *bucket = g_window_manager.application.buckets[i];
          while (bucket) {
              if (bucket->value) {
                  struct application *application = bucket->value;
                  window_manager_add_application_windows(sm, &g_window_manager, application);
              }
              bucket = bucket->next;
          }
      }
  
      return window_count != g_window_manager.window.count;
  }
  
  void space_manager_handle_display_add(struct space_manager *sm, uint32_t did)
  {
      int space_count;
      uint64_t *space_list = display_space_list(did, &space_count);
      if (!space_list) return;
  
      int list_count = 0;
      struct view *view_list[sm->view.count];
      CFStringRef uuid_list[sm->view.count];
  
      for (int i = 0; i < sm->view.capacity; ++i) {
          struct bucket *bucket = sm->view.buckets[i];
          while (bucket) {
              if (bucket->value) {
                  struct view *view = bucket->value;
                  view_list[list_count] = view;
                  uuid_list[list_count] = view->suuid;
                  ++list_count;
              }
              bucket = bucket->next;
          }
      }
  
      for (int i = 0; i < space_count; ++i) {
          uint64_t sid = space_list[i];
          CFStringRef uuid = space_uuid(sid);
          if (!uuid) continue;
  
          for (int j = 0; j < list_count; ++j) {
              CFStringRef view_uuid = uuid_list[j];
              if (!view_uuid) continue;
  
              if (CFEqual(view_uuid, uuid)) {
                  struct view *view = view_list[j];
  
                  uuid_list[j] = NULL;
                  view_list[j] = NULL;
  
                  table_remove(&sm->view, &view->sid);
                  CFRelease(view->suuid);
  
                  struct space_label *label = space_manager_get_label_for_space(sm, view->sid);
                  if (label) label->sid = sid;
  
                  view->sid = sid;
                  view->suuid = CFRetain(uuid);
  
                  table_add(&sm->view, &sid, view);
                  break;
              }
          }
  
          CFRelease(uuid);
      }
  
      sm->current_space_id = space_manager_active_space();
      sm->last_space_id = sm->current_space_id;
  
      free(space_list);
  }
  
  void space_manager_init(struct space_manager *sm)
  {
      sm->layout = VIEW_FLOAT;
      sm->split_ratio = 0.5f;
      sm->auto_balance = false;
      sm->window_placement = CHILD_SECOND;
      sm->labels = NULL;
  
      table_init(&sm->view, 23, hash_view, compare_view);
  
      uint32_t display_count;
      uint32_t *display_list = display_manager_active_display_list(&display_count);
      if (!display_list) return;
  
      for (int i = 0; i < display_count; ++i) {
          int space_count;
          uint64_t *space_list = display_space_list(display_list[i], &space_count);
          if (!space_list) continue;
  
          for (int j = 0; j < space_count; ++j) {
              struct view *view = view_create(space_list[j]);
              table_add(&sm->view, &space_list[j], view);
          }
  
          free(space_list);
      }
  
      free(display_list);
  }
  
  void space_manager_begin(struct space_manager *sm)
  {
      sm->current_space_id = space_manager_active_space();
      sm->last_space_id = sm->current_space_id;
      sm->did_begin = true;
  }
  