Move everything to sway/old/

This commit is contained in:
Drew DeVault 2017-11-18 11:22:02 -05:00
parent 0c8491f7d0
commit 733993a651
128 changed files with 76 additions and 430 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,451 +0,0 @@
#define _XOPEN_SOURCE 700
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <pcre.h>
#include "sway/criteria.h"
#include "sway/container.h"
#include "sway/config.h"
#include "stringop.h"
#include "list.h"
#include "log.h"
enum criteria_type { // *must* keep in sync with criteria_strings[]
CRIT_CLASS,
CRIT_CON_ID,
CRIT_CON_MARK,
CRIT_FLOATING,
CRIT_ID,
CRIT_INSTANCE,
CRIT_TILING,
CRIT_TITLE,
CRIT_URGENT,
CRIT_WINDOW_ROLE,
CRIT_WINDOW_TYPE,
CRIT_WORKSPACE,
CRIT_LAST
};
static const char * const criteria_strings[CRIT_LAST] = {
[CRIT_CLASS] = "class",
[CRIT_CON_ID] = "con_id",
[CRIT_CON_MARK] = "con_mark",
[CRIT_FLOATING] = "floating",
[CRIT_ID] = "id",
[CRIT_INSTANCE] = "instance",
[CRIT_TILING] = "tiling",
[CRIT_TITLE] = "title",
[CRIT_URGENT] = "urgent", // either "latest" or "oldest" ...
[CRIT_WINDOW_ROLE] = "window_role",
[CRIT_WINDOW_TYPE] = "window_type",
[CRIT_WORKSPACE] = "workspace"
};
/**
* A single criteria token (ie. value/regex pair),
* e.g. 'class="some class regex"'.
*/
struct crit_token {
enum criteria_type type;
pcre *regex;
char *raw;
};
static void free_crit_token(struct crit_token *crit) {
pcre_free(crit->regex);
free(crit->raw);
free(crit);
}
static void free_crit_tokens(list_t *crit_tokens) {
for (int i = 0; i < crit_tokens->length; i++) {
free_crit_token(crit_tokens->items[i]);
}
list_free(crit_tokens);
}
// Extracts criteria string from its brackets. Returns new (duplicate)
// substring.
static char *criteria_from(const char *arg) {
char *criteria = NULL;
if (*arg == '[') {
criteria = strdup(arg + 1);
} else {
criteria = strdup(arg);
}
int last = strlen(criteria) - 1;
if (criteria[last] == ']') {
criteria[last] = '\0';
}
return criteria;
}
// Return instances of c found in str.
static int countchr(char *str, char c) {
int found = 0;
for (int i = 0; str[i]; i++) {
if (str[i] == c) {
++found;
}
}
return found;
}
// criteria_str is e.g. '[class="some class regex" instance="instance name"]'.
//
// Will create array of pointers in buf, where first is duplicate of given
// string (must be freed) and the rest are pointers to names and values in the
// base string (every other, naturally). argc will be populated with the length
// of buf.
//
// Returns error string or NULL if successful.
static char *crit_tokens(int *argc, char ***buf, const char * const criteria_str) {
sway_log(L_DEBUG, "Parsing criteria: '%s'", criteria_str);
char *base = criteria_from(criteria_str);
char *head = base;
char *namep = head; // start of criteria name
char *valp = NULL; // start of value
// We're going to place EOS markers where we need to and fill up an array
// of pointers to the start of each token (either name or value).
int pairs = countchr(base, '=');
int max_tokens = pairs * 2 + 1; // this gives us at least enough slots
char **argv = *buf = calloc(max_tokens, sizeof(char*));
argv[0] = base; // this needs to be freed by caller
bool quoted = true;
*argc = 1; // uneven = name, even = value
while (*head && *argc < max_tokens) {
if (namep != head && *(head - 1) == '\\') {
// escaped character: don't try to parse this
} else if (*head == '=' && namep != head) {
if (*argc % 2 != 1) {
// we're not expecting a name
return strdup("Unable to parse criteria: "
"Found out of place equal sign");
} else {
// name ends here
char *end = head; // don't want to rewind the head
while (*(end - 1) == ' ') {
--end;
}
*end = '\0';
if (*(namep) == ' ') {
namep = strrchr(namep, ' ') + 1;
}
argv[*argc] = namep;
*argc += 1;
}
} else if (*head == '"') {
if (*argc % 2 != 0) {
// we're not expecting a value
return strdup("Unable to parse criteria: "
"Found quoted value where it was not expected");
} else if (!valp) { // value starts here
valp = head + 1;
quoted = true;
} else {
// value ends here
argv[*argc] = valp;
*argc += 1;
*head = '\0';
valp = NULL;
namep = head + 1;
}
} else if (*argc % 2 == 0 && *head != ' ') {
// parse unquoted values
if (!valp) {
quoted = false;
valp = head; // value starts here
}
} else if (valp && !quoted && *head == ' ') {
// value ends here
argv[*argc] = valp;
*argc += 1;
*head = '\0';
valp = NULL;
namep = head + 1;
}
head++;
}
// catch last unquoted value if needed
if (valp && !quoted && !*head) {
argv[*argc] = valp;
*argc += 1;
}
return NULL;
}
// Returns error string on failure or NULL otherwise.
static char *parse_criteria_name(enum criteria_type *type, char *name) {
*type = CRIT_LAST;
for (int i = 0; i < CRIT_LAST; i++) {
if (strcmp(criteria_strings[i], name) == 0) {
*type = (enum criteria_type) i;
break;
}
}
if (*type == CRIT_LAST) {
const char *fmt = "Criteria type '%s' is invalid or unsupported.";
int len = strlen(name) + strlen(fmt) - 1;
char *error = malloc(len);
snprintf(error, len, fmt, name);
return error;
} else if (*type == CRIT_URGENT || *type == CRIT_WINDOW_ROLE ||
*type == CRIT_WINDOW_TYPE) {
// (we're just being helpful here)
const char *fmt = "\"%s\" criteria currently unsupported, "
"no window will match this";
int len = strlen(fmt) + strlen(name) - 1;
char *error = malloc(len);
snprintf(error, len, fmt, name);
return error;
}
return NULL;
}
// Returns error string on failure or NULL otherwise.
static char *generate_regex(pcre **regex, char *value) {
const char *reg_err;
int offset;
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
if (!*regex) {
const char *fmt = "Regex compilation (for '%s') failed: %s";
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3;
char *error = malloc(len);
snprintf(error, len, fmt, value, reg_err);
return error;
}
return NULL;
}
// Test whether the criterion corresponds to the currently focused window
static bool crit_is_focused(const char *value) {
return !strcmp(value, "focused") || !strcmp(value, "__focused__");
}
// Populate list with crit_tokens extracted from criteria string, returns error
// string or NULL if successful.
char *extract_crit_tokens(list_t *tokens, const char * const criteria) {
int argc;
char **argv = NULL, *error = NULL;
if ((error = crit_tokens(&argc, &argv, criteria))) {
goto ect_cleanup;
}
for (int i = 1; i + 1 < argc; i += 2) {
char* name = argv[i], *value = argv[i + 1];
struct crit_token *token = calloc(1, sizeof(struct crit_token));
token->raw = strdup(value);
if ((error = parse_criteria_name(&token->type, name))) {
free_crit_token(token);
goto ect_cleanup;
} else if (token->type == CRIT_URGENT || crit_is_focused(value)) {
sway_log(L_DEBUG, "%s -> \"%s\"", name, value);
list_add(tokens, token);
} else if((error = generate_regex(&token->regex, value))) {
free_crit_token(token);
goto ect_cleanup;
} else {
sway_log(L_DEBUG, "%s -> /%s/", name, value);
list_add(tokens, token);
}
}
ect_cleanup:
free(argv[0]); // base string
free(argv);
return error;
}
static int regex_cmp(const char *item, const pcre *regex) {
return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
}
// test a single view if it matches list of criteria tokens (all of them).
static bool criteria_test(swayc_t *cont, list_t *tokens) {
if (cont->type != C_VIEW) {
return false;
}
int matches = 0;
for (int i = 0; i < tokens->length; i++) {
struct crit_token *crit = tokens->items[i];
switch (crit->type) {
case CRIT_CLASS:
if (!cont->class) {
// ignore
} else if (crit_is_focused(crit->raw)) {
swayc_t *focused = get_focused_view(&root_container);
if (focused->class && strcmp(cont->class, focused->class) == 0) {
matches++;
}
} else if (crit->regex && regex_cmp(cont->class, crit->regex) == 0) {
matches++;
}
break;
case CRIT_CON_ID: {
char *endptr;
size_t crit_id = strtoul(crit->raw, &endptr, 10);
if (*endptr == 0 && cont->id == crit_id) {
++matches;
}
break;
}
case CRIT_CON_MARK:
if (crit->regex && cont->marks && (list_seq_find(cont->marks, (int (*)(const void *, const void *))regex_cmp, crit->regex) != -1)) {
// Make sure it isn't matching the NUL string
if ((strcmp(crit->raw, "") == 0) == (list_seq_find(cont->marks, (int (*)(const void *, const void *))strcmp, "") != -1)) {
++matches;
}
}
break;
case CRIT_FLOATING:
if (cont->is_floating) {
matches++;
}
break;
case CRIT_ID:
if (!cont->app_id) {
// ignore
} else if (crit->regex && regex_cmp(cont->app_id, crit->regex) == 0) {
matches++;
}
break;
case CRIT_INSTANCE:
if (!cont->instance) {
// ignore
} else if (crit_is_focused(crit->raw)) {
swayc_t *focused = get_focused_view(&root_container);
if (focused->instance && strcmp(cont->instance, focused->instance) == 0) {
matches++;
}
} else if (crit->regex && regex_cmp(cont->instance, crit->regex) == 0) {
matches++;
}
break;
case CRIT_TILING:
if (!cont->is_floating) {
matches++;
}
break;
case CRIT_TITLE:
if (!cont->name) {
// ignore
} else if (crit_is_focused(crit->raw)) {
swayc_t *focused = get_focused_view(&root_container);
if (focused->name && strcmp(cont->name, focused->name) == 0) {
matches++;
}
} else if (crit->regex && regex_cmp(cont->name, crit->regex) == 0) {
matches++;
}
break;
case CRIT_URGENT: // "latest" or "oldest"
break;
case CRIT_WINDOW_ROLE:
break;
case CRIT_WINDOW_TYPE:
// TODO wlc indeed exposes this information
break;
case CRIT_WORKSPACE: ;
swayc_t *cont_ws = swayc_parent_by_type(cont, C_WORKSPACE);
if (!cont_ws || !cont_ws->name) {
// ignore
} else if (crit_is_focused(crit->raw)) {
swayc_t *focused_ws = swayc_active_workspace();
if (focused_ws->name && strcmp(cont_ws->name, focused_ws->name) == 0) {
matches++;
}
} else if (crit->regex && regex_cmp(cont_ws->name, crit->regex) == 0) {
matches++;
}
break;
default:
sway_abort("Invalid criteria type (%i)", crit->type);
break;
}
}
return matches == tokens->length;
}
int criteria_cmp(const void *a, const void *b) {
if (a == b) {
return 0;
} else if (!a) {
return -1;
} else if (!b) {
return 1;
}
const struct criteria *crit_a = a, *crit_b = b;
int cmp = lenient_strcmp(crit_a->cmdlist, crit_b->cmdlist);
if (cmp != 0) {
return cmp;
}
return lenient_strcmp(crit_a->crit_raw, crit_b->crit_raw);
}
void free_criteria(struct criteria *crit) {
if (crit->tokens) {
free_crit_tokens(crit->tokens);
}
if (crit->cmdlist) {
free(crit->cmdlist);
}
if (crit->crit_raw) {
free(crit->crit_raw);
}
free(crit);
}
bool criteria_any(swayc_t *cont, list_t *criteria) {
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
if (criteria_test(cont, bc->tokens)) {
return true;
}
}
return false;
}
list_t *criteria_for(swayc_t *cont) {
list_t *criteria = config->criteria, *matches = create_list();
for (int i = 0; i < criteria->length; i++) {
struct criteria *bc = criteria->items[i];
if (criteria_test(cont, bc->tokens)) {
list_add(matches, bc);
}
}
return matches;
}
struct list_tokens {
list_t *list;
list_t *tokens;
};
static void container_match_add(swayc_t *container, struct list_tokens *list_tokens) {
if (criteria_test(container, list_tokens->tokens)) {
list_add(list_tokens->list, container);
}
}
list_t *container_for(list_t *tokens) {
struct list_tokens list_tokens = (struct list_tokens){create_list(), tokens};
container_map(&root_container, (void (*)(swayc_t *, void *))container_match_add, &list_tokens);
for (int i = 0; i < scratchpad->length; ++i) {
swayc_t *c = scratchpad->items[i];
if (criteria_test(c, tokens)) {
list_add(list_tokens.list, c);
}
}
return list_tokens.list;
}

View file

@ -1,278 +0,0 @@
#include "stdbool.h"
#include <wlc/wlc.h>
#include "sway/focus.h"
#include "sway/workspace.h"
#include "sway/layout.h"
#include "sway/config.h"
#include "sway/input_state.h"
#include "sway/ipc-server.h"
#include "sway/border.h"
#include "log.h"
bool locked_container_focus = false;
bool suspend_workspace_cleanup = false;
// switches parent focus to c. will switch it accordingly
static void update_focus(swayc_t *c) {
// Handle if focus switches
swayc_t *parent = c->parent;
if (!parent) return;
if (parent->focused != c) {
// Get previous focus
swayc_t *prev = parent->focused;
// Set new focus
parent->focused = c;
switch (c->type) {
// Shouldn't happen
case C_ROOT: return;
// Case where output changes
case C_OUTPUT:
wlc_output_focus(c->handle);
break;
// Case where workspace changes
case C_WORKSPACE:
if (prev) {
ipc_event_workspace(prev, c, "focus");
// if the old workspace has no children, destroy it
if(prev->children->length == 0 && prev->floating->length == 0 && !suspend_workspace_cleanup) {
destroy_workspace(prev);
} else {
// update visibility of old workspace
update_visibility(prev);
}
}
// Update visibility of newly focused workspace
update_visibility(c);
break;
default:
case C_VIEW:
case C_CONTAINER:
break;
}
}
}
bool move_focus(enum movement_direction direction) {
swayc_t *old_view = get_focused_container(&root_container);
swayc_t *new_view = get_swayc_in_direction(old_view, direction);
if (!new_view) {
return false;
} else if (new_view->type == C_ROOT) {
sway_log(L_DEBUG, "Not setting focus above the workspace level");
return false;
} else if (new_view->type == C_OUTPUT) {
return set_focused_container(swayc_active_workspace_for(new_view));
} else if (direction == MOVE_PARENT || direction == MOVE_CHILD) {
return set_focused_container(new_view);
} else if (config->mouse_warping) {
swayc_t *old_op = old_view->type == C_OUTPUT ?
old_view : swayc_parent_by_type(old_view, C_OUTPUT);
swayc_t *focused = get_focused_view(new_view);
if (set_focused_container(focused)) {
if (old_op != swayc_active_output() && focused && focused->type == C_VIEW) {
center_pointer_on(focused);
}
return true;
}
} else {
return set_focused_container(get_focused_view(new_view));
}
return false;
}
swayc_t *get_focused_container(swayc_t *parent) {
if (!parent) {
return swayc_active_workspace();
}
while (!parent->is_focused && parent->focused) {
parent = parent->focused;
}
return parent;
}
bool set_focused_container(swayc_t *c) {
if (locked_container_focus || !c || !c->parent) {
return false;
}
// current ("old") workspace for sending workspace change event later
swayc_t *old_ws = swayc_active_workspace();
// keep track of child count so we can determine if it gets destroyed
int old_ws_child_count = 0;
if (old_ws) {
old_ws_child_count = old_ws->children->length + old_ws->floating->length;
}
// current ("old") focused container
swayc_t *old_focus = get_focused_container(&root_container);
// if old_focus is a workspace, then it's the same workspace as
// old_ws, and we'll need to null its pointer too, since it will
// be destroyed in the update_focus() call
bool old_focus_was_ws = (old_focus->type == C_WORKSPACE);
// workspace of new focused container
swayc_t *workspace = swayc_active_workspace_for(c);
if (swayc_is_fullscreen(get_focused_container(workspace))) {
// if switching to a workspace with a fullscreen view,
// focus on the fullscreen view
c = get_focused_container(workspace);
}
swayc_log(L_DEBUG, c, "Setting focus to %p:%" PRIuPTR, c, c->handle);
if (c->type == C_VIEW) {
// dispatch a window event
ipc_event_window(c, "focus");
}
// update the global pointer
current_focus = c;
// update container focus from here to root, making necessary changes along
// the way
swayc_t *p = c;
if (p->type != C_OUTPUT && p->type != C_ROOT) {
p->is_focused = true;
}
while (p != &root_container) {
update_focus(p);
p = p->parent;
p->is_focused = false;
}
if (old_focus_was_ws && old_ws_child_count == 0) {
// this workspace was destroyed in update_focus(), so null the pointers
old_focus = NULL;
old_ws = NULL;
}
if (!(wlc_view_get_type(p->handle) & WLC_BIT_POPUP)) {
if (old_focus) {
if (old_focus->type == C_VIEW) {
wlc_view_set_state(old_focus->handle, WLC_BIT_ACTIVATED, false);
}
update_container_border(old_focus);
}
if (c->type == C_VIEW) {
wlc_view_set_state(c->handle, WLC_BIT_ACTIVATED, true);
}
/* TODO WLR
if (!desktop_shell.is_locked) {
// If the system is locked, we do everything _but_ actually setting
// focus. This includes making our internals think that this view is
// focused.
wlc_view_focus(c->handle);
}
*/
if (c->parent->layout != L_TABBED && c->parent->layout != L_STACKED) {
update_container_border(c);
}
swayc_t *parent = swayc_tabbed_stacked_ancestor(c);
if (parent != NULL) {
arrange_backgrounds();
arrange_windows(parent, -1, -1);
}
}
if (old_ws != workspace) {
// old_ws might be NULL here but that's ok
ipc_event_workspace(old_ws, workspace, "focus");
}
return true;
}
bool set_focused_container_for(swayc_t *a, swayc_t *c) {
if (locked_container_focus || !c) {
return false;
}
swayc_t *find = c;
while (find != a && (find = find->parent)) {
if (find == &root_container) {
return false;
}
}
// Get workspace for c, get that workspaces current focused container.
swayc_t *workspace = swayc_active_workspace_for(c);
swayc_t *focused = get_focused_view(workspace);
// if the workspace we are changing focus to has a fullscreen view return
if (swayc_is_fullscreen(focused) && c != focused) {
return false;
}
// Check if we are changing a parent container that will see change
bool effective = true;
while (find != &root_container) {
if (find->parent->focused != find) {
effective = false;
}
find = find->parent;
}
if (effective) {
// Go to set_focused_container
return set_focused_container(c);
}
sway_log(L_DEBUG, "Setting focus for %p:%" PRIuPTR " to %p:%" PRIuPTR,
a, a->handle, c, c->handle);
c->is_focused = true;
swayc_t *p = c;
while (p != a) {
update_focus(p);
p = p->parent;
p->is_focused = false;
}
return true;
}
swayc_t *get_focused_view(swayc_t *parent) {
swayc_t *c = parent;
while (c && c->type != C_VIEW) {
if (c->type == C_WORKSPACE && c->focused == NULL) {
return c;
}
c = c->focused;
}
if (c == NULL) {
c = swayc_active_workspace_for(parent);
}
return c;
}
swayc_t *get_focused_float(swayc_t *ws) {
if(!sway_assert(ws->type == C_WORKSPACE, "must be of workspace type")) {
ws = swayc_active_workspace();
}
if (ws->floating->length) {
return ws->floating->items[ws->floating->length - 1];
}
return NULL;
}
swayc_t *get_focused_view_include_floating(swayc_t *parent) {
swayc_t *c = parent;
swayc_t *f = NULL;
while (c && c->type != C_VIEW) {
if (c->type == C_WORKSPACE && c->focused == NULL) {
return ((f = get_focused_float(c))) ? f : c;
}
c = c->focused;
}
if (c == NULL) {
c = swayc_active_workspace_for(parent);
}
return c;
}

File diff suppressed because it is too large Load diff

View file

@ -1,278 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include "sway/output.h"
#include "log.h"
#include "list.h"
void output_get_scaled_size(wlc_handle handle, struct wlc_size *size) {
*size = *wlc_output_get_resolution(handle);
uint32_t scale = wlc_output_get_scale(handle);
size->w /= scale;
size->h /= scale;
}
swayc_t *output_by_name(const char* name, const struct wlc_point *abs_pos) {
swayc_t *output = NULL;
// If there is no output directly next to the current one, use
// swayc_opposite_output to wrap.
if (strcasecmp(name, "left") == 0) {
output = swayc_adjacent_output(NULL, MOVE_LEFT, abs_pos, true);
if (!output) {
output = swayc_opposite_output(MOVE_RIGHT, abs_pos);
}
} else if (strcasecmp(name, "right") == 0) {
output = swayc_adjacent_output(NULL, MOVE_RIGHT, abs_pos, true);
if (!output) {
output = swayc_opposite_output(MOVE_LEFT, abs_pos);
}
} else if (strcasecmp(name, "up") == 0) {
output = swayc_adjacent_output(NULL, MOVE_UP, abs_pos, true);
if (!output) {
output = swayc_opposite_output(MOVE_DOWN, abs_pos);
}
} else if (strcasecmp(name, "down") == 0) {
output = swayc_adjacent_output(NULL, MOVE_DOWN, abs_pos, true);
if (!output) {
output = swayc_opposite_output(MOVE_UP, abs_pos);
}
} else {
for(int i = 0; i < root_container.children->length; ++i) {
swayc_t *c = root_container.children->items[i];
if (c->type == C_OUTPUT && strcasecmp(c->name, name) == 0) {
return c;
}
}
}
return output;
}
swayc_t *swayc_opposite_output(enum movement_direction dir,
const struct wlc_point *abs_pos) {
// Search through all the outputs and pick the output whose edge covers the
// given position, and is at leftmost/rightmost/upmost/downmost side of the
// screen (decided by the direction given).
swayc_t *opposite = NULL;
char *dir_text = NULL;
switch(dir) {
case MOVE_LEFT:
case MOVE_RIGHT: ;
for (int i = 0; i < root_container.children->length; ++i) {
swayc_t *c = root_container.children->items[i];
if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
if (!opposite) {
opposite = c;
} else if ((dir == MOVE_LEFT && c->x < opposite->x)
|| (dir == MOVE_RIGHT && c->x > opposite->x)) {
opposite = c;
}
}
}
dir_text = dir == MOVE_LEFT ? "leftmost" : "rightmost";
break;
case MOVE_UP:
case MOVE_DOWN: ;
for (int i = 0; i < root_container.children->length; ++i) {
swayc_t *c = root_container.children->items[i];
if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
if (!opposite) {
opposite = c;
} else if ((dir == MOVE_UP && c->y < opposite->y)
|| (dir == MOVE_DOWN && c->y > opposite->y)) {
opposite = c;
}
}
}
dir_text = dir == MOVE_UP ? "upmost" : "downmost";
break;
default:
sway_abort("Function called with invalid argument.");
break;
}
if (opposite) {
sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s from y-position %i",
opposite->name, opposite->width, opposite->height, opposite->x, opposite->y,
dir_text, abs_pos->y);
}
return opposite;
}
// Position is where on the edge (as absolute position) the adjacent output should be searched for.
swayc_t *swayc_adjacent_output(swayc_t *output, enum movement_direction dir,
const struct wlc_point *abs_pos, bool pick_closest) {
if (!output) {
output = swayc_active_output();
}
// In order to find adjacent outputs we need to test that the outputs are
// aligned on one axis (decided by the direction given) and that the given
// position is within the edge of the adjacent output. If no such output
// exists we pick the adjacent output within the edge that is closest to
// the given position, if any.
swayc_t *adjacent = NULL;
char *dir_text = NULL;
switch(dir) {
case MOVE_LEFT:
case MOVE_RIGHT: ;
double delta_y = 0;
for(int i = 0; i < root_container.children->length; ++i) {
swayc_t *c = root_container.children->items[i];
if (c == output || c->type != C_OUTPUT) {
continue;
}
bool x_aligned = dir == MOVE_LEFT ?
c->x + c->width == output->x :
c->x == output->x + output->width;
if (!x_aligned) {
continue;
}
if (abs_pos->y >= c->y && abs_pos->y <= c->y + c->height) {
delta_y = 0;
adjacent = c;
break;
} else if (pick_closest) {
// track closest adjacent output
double top_y = c->y, bottom_y = c->y + c->height;
if (top_y >= output->y && top_y <= output->y + output->height) {
double delta = top_y - abs_pos->y;
if (delta < 0) delta = -delta;
if (delta < delta_y || !adjacent) {
delta_y = delta;
adjacent = c;
}
}
// we check both points and pick the closest
if (bottom_y >= output->y && bottom_y <= output->y + output->height) {
double delta = bottom_y - abs_pos->y;
if (delta < 0) delta = -delta;
if (delta < delta_y || !adjacent) {
delta_y = delta;
adjacent = c;
}
}
}
}
dir_text = dir == MOVE_LEFT ? "left of" : "right of";
if (adjacent && delta_y == 0) {
sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i)",
adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
dir_text, output->name, abs_pos->y);
} else if (adjacent) {
// so we end up picking the closest adjacent output because
// there is no directly adjacent to the given position
sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (y-position %i, delta: %.0f)",
adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
dir_text, output->name, abs_pos->y, delta_y);
}
break;
case MOVE_UP:
case MOVE_DOWN: ;
double delta_x = 0;
for(int i = 0; i < root_container.children->length; ++i) {
swayc_t *c = root_container.children->items[i];
if (c == output || c->type != C_OUTPUT) {
continue;
}
bool y_aligned = dir == MOVE_UP ?
c->y + c->height == output->y :
c->y == output->y + output->height;
if (!y_aligned) {
continue;
}
if (abs_pos->x >= c->x && abs_pos->x <= c->x + c->width) {
delta_x = 0;
adjacent = c;
break;
} else if (pick_closest) {
// track closest adjacent output
double left_x = c->x, right_x = c->x + c->width;
if (left_x >= output->x && left_x <= output->x + output->width) {
double delta = left_x - abs_pos->x;
if (delta < 0) delta = -delta;
if (delta < delta_x || !adjacent) {
delta_x = delta;
adjacent = c;
}
}
// we check both points and pick the closest
if (right_x >= output->x && right_x <= output->x + output->width) {
double delta = right_x - abs_pos->x;
if (delta < 0) delta = -delta;
if (delta < delta_x || !adjacent) {
delta_x = delta;
adjacent = c;
}
}
}
}
dir_text = dir == MOVE_UP ? "above" : "below";
if (adjacent && delta_x == 0) {
sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i)",
adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
dir_text, output->name, abs_pos->x);
} else if (adjacent) {
// so we end up picking the closest adjacent output because
// there is no directly adjacent to the given position
sway_log(L_DEBUG, "%s (%.0fx%.0f+%.0f+%.0f) is %s current output %s (x-position %i, delta: %.0f)",
adjacent->name, adjacent->width, adjacent->height, adjacent->x, adjacent->y,
dir_text, output->name, abs_pos->x, delta_x);
}
break;
default:
sway_abort("Function called with invalid argument.");
break;
}
return adjacent;
}
void get_absolute_position(swayc_t *container, struct wlc_point *point) {
if (!container || !point)
sway_abort("Need container and wlc_point (was %p, %p).", container, point);
if (container->type == C_OUTPUT) {
// Coordinates are already absolute.
point->x = container->x;
point->y = container->y;
} else {
swayc_t *output = swayc_parent_by_type(container, C_OUTPUT);
if (container->type == C_WORKSPACE) {
// Workspace coordinates are actually wrong/arbitrary, but should
// be same as output.
point->x = output->x;
point->y = output->y;
} else {
point->x = output->x + container->x;
point->y = output->y + container->y;
}
}
}
void get_absolute_center_position(swayc_t *container, struct wlc_point *point) {
get_absolute_position(container, point);
point->x += container->width/2;
point->y += container->height/2;
}
static int sort_workspace_cmp_qsort(const void *_a, const void *_b) {
swayc_t *a = *(void **)_a;
swayc_t *b = *(void **)_b;
int retval = 0;
if (isdigit(a->name[0]) && isdigit(b->name[0])) {
int a_num = strtol(a->name, NULL, 10);
int b_num = strtol(b->name, NULL, 10);
retval = (a_num < b_num) ? -1 : (a_num > b_num);
} else if (isdigit(a->name[0])) {
retval = -1;
} else if (isdigit(b->name[0])) {
retval = 1;
}
return retval;
}
void sort_workspaces(swayc_t *output) {
list_stable_sort(output->children, sort_workspace_cmp_qsort);
}

View file

@ -1,373 +0,0 @@
#define _XOPEN_SOURCE 500
#include <stdlib.h>
#include <stdbool.h>
#include <limits.h>
#include <ctype.h>
#include <wlc/wlc.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include "sway/ipc-server.h"
#include "sway/workspace.h"
#include "sway/layout.h"
#include "sway/container.h"
#include "sway/handlers.h"
#include "sway/config.h"
#include "sway/focus.h"
#include "stringop.h"
#include "util.h"
#include "list.h"
#include "log.h"
#include "ipc.h"
char *prev_workspace_name = NULL;
struct workspace_by_number_data {
int len;
const char *cset;
const char *name;
};
static bool workspace_valid_on_output(const char *output_name, const char *ws_name) {
int i;
for (i = 0; i < config->workspace_outputs->length; ++i) {
struct workspace_output *wso = config->workspace_outputs->items[i];
if (strcasecmp(wso->workspace, ws_name) == 0) {
if (strcasecmp(wso->output, output_name) != 0) {
return false;
}
}
}
return true;
}
char *workspace_next_name(const char *output_name) {
sway_log(L_DEBUG, "Workspace: Generating new workspace name for output %s", output_name);
int i;
int l = 1;
// Scan all workspace bindings to find the next available workspace name,
// if none are found/available then default to a number
struct sway_mode *mode = config->current_mode;
int order = INT_MAX;
char *target = NULL;
for (i = 0; i < mode->bindings->length; ++i) {
struct sway_binding *binding = mode->bindings->items[i];
char *cmdlist = strdup(binding->command);
char *dup = cmdlist;
char *name = NULL;
// workspace n
char *cmd = argsep(&cmdlist, " ");
if (cmdlist) {
name = argsep(&cmdlist, ",;");
}
if (strcmp("workspace", cmd) == 0 && name) {
sway_log(L_DEBUG, "Got valid workspace command for target: '%s'", name);
char *_target = strdup(name);
strip_quotes(_target);
while (isspace(*_target))
_target++;
// Make sure that the command references an actual workspace
// not a command about workspaces
if (strcmp(_target, "next") == 0 ||
strcmp(_target, "prev") == 0 ||
strcmp(_target, "next_on_output") == 0 ||
strcmp(_target, "prev_on_output") == 0 ||
strcmp(_target, "number") == 0 ||
strcmp(_target, "back_and_forth") == 0 ||
strcmp(_target, "current") == 0)
{
free(_target);
free(dup);
continue;
}
// Make sure that the workspace doesn't already exist
if (workspace_by_name(_target)) {
free(_target);
free(dup);
continue;
}
// make sure that the workspace can appear on the given
// output
if (!workspace_valid_on_output(output_name, _target)) {
free(_target);
free(dup);
continue;
}
if (binding->order < order) {
order = binding->order;
free(target);
target = _target;
sway_log(L_DEBUG, "Workspace: Found free name %s", _target);
}
}
free(dup);
}
if (target != NULL) {
return target;
}
// As a fall back, get the current number of active workspaces
// and return that + 1 for the next workspace's name
int ws_num = root_container.children->length;
if (ws_num >= 10) {
l = 2;
} else if (ws_num >= 100) {
l = 3;
}
char *name = malloc(l + 1);
if (!name) {
sway_log(L_ERROR, "Could not allocate workspace name");
return NULL;
}
sprintf(name, "%d", ws_num++);
return name;
}
swayc_t *workspace_create(const char* name) {
swayc_t *parent;
// Search for workspace<->output pair
int i, e = config->workspace_outputs->length;
for (i = 0; i < e; ++i) {
struct workspace_output *wso = config->workspace_outputs->items[i];
if (strcasecmp(wso->workspace, name) == 0)
{
// Find output to use if it exists
e = root_container.children->length;
for (i = 0; i < e; ++i) {
parent = root_container.children->items[i];
if (strcmp(parent->name, wso->output) == 0) {
return new_workspace(parent, name);
}
}
break;
}
}
// Otherwise create a new one
parent = get_focused_container(&root_container);
parent = swayc_parent_by_type(parent, C_OUTPUT);
return new_workspace(parent, name);
}
static bool _workspace_by_name(swayc_t *view, void *data) {
return (view->type == C_WORKSPACE) &&
(strcasecmp(view->name, (char *) data) == 0);
}
swayc_t *workspace_by_name(const char* name) {
if (strcmp(name, "prev") == 0) {
return workspace_prev();
}
else if (strcmp(name, "prev_on_output") == 0) {
return workspace_output_prev();
}
else if (strcmp(name, "next") == 0) {
return workspace_next();
}
else if (strcmp(name, "next_on_output") == 0) {
return workspace_output_next();
}
else if (strcmp(name, "current") == 0) {
return swayc_active_workspace();
}
else {
return swayc_by_test(&root_container, _workspace_by_name, (void *) name);
}
}
static bool _workspace_by_number(swayc_t *view, void *data) {
if (view->type != C_WORKSPACE) {
return false;
}
struct workspace_by_number_data *wbnd = data;
int a = strspn(view->name, wbnd->cset);
return a == wbnd->len && strncmp(view->name, wbnd->name, a) == 0;
}
swayc_t *workspace_by_number(const char* name) {
struct workspace_by_number_data wbnd = {0, "1234567890", name};
wbnd.len = strspn(name, wbnd.cset);
if (wbnd.len <= 0) {
return NULL;
}
return swayc_by_test(&root_container, _workspace_by_number, (void *) &wbnd);
}
/**
* Get the previous or next workspace on the specified output.
* Wraps around at the end and beginning.
* If next is false, the previous workspace is returned, otherwise the next one is returned.
*/
swayc_t *workspace_output_prev_next_impl(swayc_t *output, bool next) {
if (!sway_assert(output->type == C_OUTPUT, "Argument must be an output, is %d", output->type)) {
return NULL;
}
int i;
for (i = 0; i < output->children->length; i++) {
if (output->children->items[i] == output->focused) {
return output->children->items[wrap(i + (next ? 1 : -1), output->children->length)];
}
}
// Doesn't happen, at worst the for loop returns the previously active workspace
return NULL;
}
/**
* Get the previous or next workspace. If the first/last workspace on an output is active,
* proceed to the previous/next output's previous/next workspace.
* If next is false, the previous workspace is returned, otherwise the next one is returned.
*/
swayc_t *workspace_prev_next_impl(swayc_t *workspace, bool next) {
if (!sway_assert(workspace->type == C_WORKSPACE, "Argument must be a workspace, is %d", workspace->type)) {
return NULL;
}
swayc_t *current_output = workspace->parent;
int offset = next ? 1 : -1;
int start = next ? 0 : 1;
int end = next ? (current_output->children->length) - 1 : current_output->children->length;
int i;
for (i = start; i < end; i++) {
if (current_output->children->items[i] == workspace) {
return current_output->children->items[i + offset];
}
}
// Given workspace is the first/last on the output, jump to the previous/next output
int num_outputs = root_container.children->length;
for (i = 0; i < num_outputs; i++) {
if (root_container.children->items[i] == current_output) {
swayc_t *next_output = root_container.children->items[wrap(i + offset, num_outputs)];
return workspace_output_prev_next_impl(next_output, next);
}
}
// Doesn't happen, at worst the for loop returns the previously active workspace on the active output
return NULL;
}
swayc_t *workspace_output_next() {
return workspace_output_prev_next_impl(swayc_active_output(), true);
}
swayc_t *workspace_next() {
return workspace_prev_next_impl(swayc_active_workspace(), true);
}
swayc_t *workspace_output_prev() {
return workspace_output_prev_next_impl(swayc_active_output(), false);
}
swayc_t *workspace_prev() {
return workspace_prev_next_impl(swayc_active_workspace(), false);
}
bool workspace_switch(swayc_t *workspace) {
if (!workspace) {
return false;
}
swayc_t *active_ws = swayc_active_workspace();
if (config->auto_back_and_forth && active_ws == workspace && prev_workspace_name) {
swayc_t *new_ws = workspace_by_name(prev_workspace_name);
workspace = new_ws ? new_ws : workspace_create(prev_workspace_name);
}
if (!prev_workspace_name
|| (strcmp(prev_workspace_name, active_ws->name)
&& active_ws != workspace)) {
free(prev_workspace_name);
prev_workspace_name = malloc(strlen(active_ws->name) + 1);
if (!prev_workspace_name) {
sway_log(L_ERROR, "Unable to allocate previous workspace name");
return false;
}
strcpy(prev_workspace_name, active_ws->name);
}
// move sticky containers
if (swayc_parent_by_type(active_ws, C_OUTPUT) == swayc_parent_by_type(workspace, C_OUTPUT)) {
// don't change list while traversing it, use intermediate list instead
list_t *stickies = create_list();
for (int i = 0; i < active_ws->floating->length; i++) {
swayc_t *cont = active_ws->floating->items[i];
if (cont->sticky) {
list_add(stickies, cont);
}
}
for (int i = 0; i < stickies->length; i++) {
swayc_t *cont = stickies->items[i];
sway_log(L_DEBUG, "Moving sticky container %p to %p:%s",
cont, workspace, workspace->name);
swayc_t *parent = remove_child(cont);
add_floating(workspace, cont);
// Destroy old container if we need to
destroy_container(parent);
}
list_free(stickies);
}
sway_log(L_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name);
if (!set_focused_container(get_focused_view(workspace))) {
return false;
}
swayc_t *output = swayc_parent_by_type(workspace, C_OUTPUT);
arrange_backgrounds();
arrange_windows(output, -1, -1);
return true;
}
swayc_t *workspace_for_pid(pid_t pid) {
int i;
swayc_t *ws = NULL;
struct pid_workspace *pw = NULL;
sway_log(L_DEBUG, "looking for workspace for pid %d", pid);
// leaving this here as it's useful for debugging
// sway_log(L_DEBUG, "all pid_workspaces");
// for (int k = 0; k < config->pid_workspaces->length; k++) {
// pw = config->pid_workspaces->items[k];
// sway_log(L_DEBUG, "pid %d workspace %s time_added %li", *pw->pid, pw->workspace, *pw->time_added);
// }
do {
for (i = 0; i < config->pid_workspaces->length; i++) {
pw = config->pid_workspaces->items[i];
pid_t *pw_pid = pw->pid;
if (pid == *pw_pid) {
sway_log(L_DEBUG, "found pid_workspace for pid %d, workspace %s", pid, pw->workspace);
break; // out of for loop
}
pw = NULL;
}
if (pw) {
break; // out of do-while loop
}
pid = get_parent_pid(pid);
// no sense in looking for matches for pid 0.
// also, if pid == getpid(), that is the compositor's
// pid, which definitely isn't helpful
} while (pid > 0 && pid != getpid());
if (pw) {
ws = workspace_by_name(pw->workspace);
if (!ws) {
sway_log(L_DEBUG, "Creating workspace %s for pid %d because it disappeared", pw->workspace, pid);
ws = workspace_create(pw->workspace);
}
list_del(config->pid_workspaces, i);
}
return ws;
}