|
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
#include <xkbcommon/xkbcommon-names.h>
|
|
|
|
#include <wlc/wlc.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <strings.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <wordexp.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include "stringop.h"
|
|
|
|
#include "layout.h"
|
|
|
|
#include "focus.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
#include "commands.h"
|
|
|
|
#include "container.h"
|
|
|
|
#include "output.h"
|
|
|
|
#include "handlers.h"
|
|
|
|
#include "sway.h"
|
|
|
|
#include "resize.h"
|
|
|
|
#include "input_state.h"
|
|
|
|
#include "criteria.h"
|
|
|
|
#include "ipc-server.h"
|
|
|
|
|
|
|
|
typedef struct cmd_results *sway_cmd(int argc, char **argv);
|
|
|
|
|
|
|
|
struct cmd_handler {
|
|
|
|
char *command;
|
|
|
|
sway_cmd *handle;
|
|
|
|
};
|
|
|
|
|
|
|
|
static sway_cmd cmd_bar;
|
|
|
|
static sway_cmd cmd_bindsym;
|
|
|
|
static sway_cmd cmd_debuglog;
|
|
|
|
static sway_cmd cmd_exec;
|
|
|
|
static sway_cmd cmd_exec_always;
|
|
|
|
static sway_cmd cmd_exit;
|
|
|
|
static sway_cmd cmd_floating;
|
|
|
|
static sway_cmd cmd_floating_mod;
|
|
|
|
static sway_cmd cmd_focus;
|
|
|
|
static sway_cmd cmd_focus_follows_mouse;
|
|
|
|
static sway_cmd cmd_for_window;
|
|
|
|
static sway_cmd cmd_fullscreen;
|
|
|
|
static sway_cmd cmd_gaps;
|
|
|
|
static sway_cmd cmd_kill;
|
|
|
|
static sway_cmd cmd_layout;
|
|
|
|
static sway_cmd cmd_log_colors;
|
|
|
|
static sway_cmd cmd_mode;
|
|
|
|
static sway_cmd cmd_mouse_warping;
|
|
|
|
static sway_cmd cmd_move;
|
|
|
|
static sway_cmd cmd_orientation;
|
|
|
|
static sway_cmd cmd_output;
|
|
|
|
static sway_cmd cmd_reload;
|
|
|
|
static sway_cmd cmd_resize;
|
|
|
|
static sway_cmd cmd_scratchpad;
|
|
|
|
static sway_cmd cmd_set;
|
|
|
|
static sway_cmd cmd_split;
|
|
|
|
static sway_cmd cmd_splith;
|
|
|
|
static sway_cmd cmd_splitv;
|
|
|
|
static sway_cmd cmd_sticky;
|
|
|
|
static sway_cmd cmd_workspace;
|
|
|
|
static sway_cmd cmd_ws_auto_back_and_forth;
|
|
|
|
|
|
|
|
static sway_cmd bar_cmd_binding_mode_indicator;
|
|
|
|
static sway_cmd bar_cmd_bindsym;
|
|
|
|
static sway_cmd bar_cmd_colors;
|
|
|
|
static sway_cmd bar_cmd_font;
|
|
|
|
static sway_cmd bar_cmd_mode;
|
|
|
|
static sway_cmd bar_cmd_modifier;
|
|
|
|
static sway_cmd bar_cmd_output;
|
|
|
|
static sway_cmd bar_cmd_height;
|
|
|
|
static sway_cmd bar_cmd_hidden_state;
|
|
|
|
static sway_cmd bar_cmd_id;
|
|
|
|
static sway_cmd bar_cmd_position;
|
|
|
|
static sway_cmd bar_cmd_separator_symbol;
|
|
|
|
static sway_cmd bar_cmd_status_command;
|
|
|
|
static sway_cmd bar_cmd_strip_workspace_numbers;
|
|
|
|
static sway_cmd bar_cmd_swaybar_command;
|
|
|
|
static sway_cmd bar_cmd_tray_output;
|
|
|
|
static sway_cmd bar_cmd_tray_padding;
|
|
|
|
static sway_cmd bar_cmd_workspace_buttons;
|
|
|
|
|
|
|
|
static sway_cmd bar_colors_cmd_active_workspace;
|
|
|
|
static sway_cmd bar_colors_cmd_background;
|
|
|
|
static sway_cmd bar_colors_cmd_background;
|
|
|
|
static sway_cmd bar_colors_cmd_binding_mode;
|
|
|
|
static sway_cmd bar_colors_cmd_focused_workspace;
|
|
|
|
static sway_cmd bar_colors_cmd_inactive_workspace;
|
|
|
|
static sway_cmd bar_colors_cmd_separator;
|
|
|
|
static sway_cmd bar_colors_cmd_statusline;
|
|
|
|
static sway_cmd bar_colors_cmd_urgent_workspace;
|
|
|
|
|
|
|
|
swayc_t *sp_view;
|
|
|
|
int sp_index = 0;
|
|
|
|
|
|
|
|
static struct modifier_key {
|
|
|
|
char *name;
|
|
|
|
uint32_t mod;
|
|
|
|
} modifiers[] = {
|
|
|
|
{ XKB_MOD_NAME_SHIFT, WLC_BIT_MOD_SHIFT },
|
|
|
|
{ XKB_MOD_NAME_CAPS, WLC_BIT_MOD_CAPS },
|
|
|
|
{ XKB_MOD_NAME_CTRL, WLC_BIT_MOD_CTRL },
|
|
|
|
{ "Ctrl", WLC_BIT_MOD_CTRL },
|
|
|
|
{ XKB_MOD_NAME_ALT, WLC_BIT_MOD_ALT },
|
|
|
|
{ "Alt", WLC_BIT_MOD_ALT },
|
|
|
|
{ XKB_MOD_NAME_NUM, WLC_BIT_MOD_MOD2 },
|
|
|
|
{ "Mod3", WLC_BIT_MOD_MOD3 },
|
|
|
|
{ XKB_MOD_NAME_LOGO, WLC_BIT_MOD_LOGO },
|
|
|
|
{ "Mod5", WLC_BIT_MOD_MOD5 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *bg_options[] = {
|
|
|
|
"stretch",
|
|
|
|
"center",
|
|
|
|
"fill",
|
|
|
|
"fit",
|
|
|
|
"tile"
|
|
|
|
};
|
|
|
|
|
|
|
|
enum expected_args {
|
|
|
|
EXPECTED_MORE_THAN,
|
|
|
|
EXPECTED_AT_LEAST,
|
|
|
|
EXPECTED_LESS_THAN,
|
|
|
|
EXPECTED_EQUAL_TO
|
|
|
|
};
|
|
|
|
|
|
|
|
// Returns error object, or NULL if check succeeds.
|
|
|
|
static struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
switch (type) {
|
|
|
|
case EXPECTED_MORE_THAN:
|
|
|
|
if (argc > val) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
|
|
|
|
"(expected more than %d argument%s, got %d)",
|
|
|
|
name, val, (char*[2]){"s", ""}[argc==1], argc);
|
|
|
|
break;
|
|
|
|
case EXPECTED_AT_LEAST:
|
|
|
|
if (argc >= val) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
|
|
|
|
"(expected at least %d argument%s, got %d)",
|
|
|
|
name, val, (char*[2]){"s", ""}[argc==1], argc);
|
|
|
|
break;
|
|
|
|
case EXPECTED_LESS_THAN:
|
|
|
|
if (argc < val) {
|
|
|
|
return NULL;
|
|
|
|
};
|
|
|
|
error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
|
|
|
|
"(expected less than %d argument%s, got %d)",
|
|
|
|
name, val, (char*[2]){"s", ""}[argc==1], argc);
|
|
|
|
break;
|
|
|
|
case EXPECTED_EQUAL_TO:
|
|
|
|
if (argc == val) {
|
|
|
|
return NULL;
|
|
|
|
};
|
|
|
|
error = cmd_results_new(CMD_INVALID, name, "Invalid %s command "
|
|
|
|
"(expected %d arguments, got %d)", name, val, argc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int binding_order = 0;
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_bindsym(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
|
|
|
|
return error;
|
|
|
|
} else if (!config->reading) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_binding *binding = malloc(sizeof(struct sway_binding));
|
|
|
|
binding->keys = create_list();
|
|
|
|
binding->modifiers = 0;
|
|
|
|
binding->command = join_args(argv + 1, argc - 1);
|
|
|
|
|
|
|
|
list_t *split = split_string(argv[0], "+");
|
|
|
|
for (int i = 0; i < split->length; ++i) {
|
|
|
|
// Check for a modifier key
|
|
|
|
int j;
|
|
|
|
bool is_mod = false;
|
|
|
|
for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) {
|
|
|
|
if (strcasecmp(modifiers[j].name, split->items[i]) == 0) {
|
|
|
|
binding->modifiers |= modifiers[j].mod;
|
|
|
|
is_mod = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_mod) continue;
|
|
|
|
// Check for xkb key
|
|
|
|
xkb_keysym_t sym = xkb_keysym_from_name(split->items[i], XKB_KEYSYM_CASE_INSENSITIVE);
|
|
|
|
if (!sym) {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "bindsym", "Unknown key '%s'", (char *)split->items[i]);
|
|
|
|
free_sway_binding(binding);
|
|
|
|
list_free(split);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t));
|
|
|
|
*key = sym;
|
|
|
|
list_add(binding->keys, key);
|
|
|
|
}
|
|
|
|
free_flat_list(split);
|
|
|
|
|
|
|
|
struct sway_mode *mode = config->current_mode;
|
|
|
|
int i = list_seq_find(mode->bindings, sway_binding_cmp_keys, binding);
|
|
|
|
if (i > -1) {
|
|
|
|
sway_log(L_DEBUG, "bindsym - '%s' already exists, overwriting", argv[0]);
|
|
|
|
struct sway_binding *dup = mode->bindings->items[i];
|
|
|
|
free_sway_binding(dup);
|
|
|
|
list_del(mode->bindings, i);
|
|
|
|
}
|
|
|
|
binding->order = binding_order++;
|
|
|
|
list_add(mode->bindings, binding);
|
replace non-standard qsort_r with qsort
I've tried to make as few changes, as possible.
Usually the reason for using qsort_r is, that you can pass an extra userdata pointer to the
compare function. However, in sway list_sort wrapped qsort_r and always called a wrapper
function for comparing, the wrapper function then had the real compare function as argument.
The only thing, that the wrapper function does, is dereferencing the 'left' and 'right' function
arguments before passing them to the real compare function.
I have renamed list_sort to list_qsort to avoid confusion (so nobody tries to use list_qsort like
list_sort) and removed the wrapper functionality. Now the dereferencing must be done in the
compare function, that gets passed.
Some compare functions were used in both list_sort and list_seq_find. To make the difference
clear, I've added a '_qsort' suffix to the compare functions, that are intended to be used with
the new list_qsort. (In other words: list_qsort is not compatible anymore with list_seq_find).
- Changed and renamed function (it isn't used anywhere but in commands.c, and only for sorting):
compare_set -> compare_set_qsort
- New wrapper functions:
sway_binding_cmp_qsort (for sway_binding_cmp)
sway_mouse_binding_cmp_qsort (for sway_mouse_binding_cmp)
9 years ago
|
|
|
list_qsort(mode->bindings, sway_binding_cmp_qsort);
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "bindsym - Bound %s to command %s", argv[0], binding->command);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_exec_always(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL);
|
|
|
|
if ((error = checkarg(argc, "exec_always", EXPECTED_MORE_THAN, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *tmp = NULL;
|
|
|
|
if (strcmp((char*)*argv, "--no-startup-id") == 0) {
|
|
|
|
sway_log(L_INFO, "exec switch '--no-startup-id' not supported, ignored.");
|
|
|
|
if ((error = checkarg(argc - 1, "exec_always", EXPECTED_MORE_THAN, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = join_args(argv + 1, argc - 1);
|
|
|
|
} else {
|
|
|
|
tmp = join_args(argv, argc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Put argument into cmd array
|
|
|
|
char cmd[4096];
|
|
|
|
strcpy(cmd, tmp);
|
|
|
|
free(tmp);
|
|
|
|
sway_log(L_DEBUG, "Executing %s", cmd);
|
|
|
|
|
|
|
|
int fd[2];
|
|
|
|
pipe(fd);
|
|
|
|
|
|
|
|
pid_t pid;
|
|
|
|
pid_t *child = malloc(sizeof(pid_t)); // malloc'd so that Linux can avoid copying the process space
|
|
|
|
// Fork process
|
|
|
|
if ((pid = fork()) == 0) {
|
|
|
|
// Fork child process again
|
|
|
|
setsid();
|
|
|
|
if ((*child = fork()) == 0) {
|
|
|
|
execl("/bin/sh", "/bin/sh", "-c", cmd, (void *)NULL);
|
|
|
|
/* Not reached */
|
|
|
|
}
|
|
|
|
close(fd[0]);
|
|
|
|
write(fd[1], child, sizeof(pid_t));
|
|
|
|
close(fd[1]);
|
|
|
|
_exit(0); // Close child process
|
|
|
|
} else if (pid < 0) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "exec_always", "Command failed (sway could not fork).");
|
|
|
|
}
|
|
|
|
close(fd[1]); // close write
|
|
|
|
read(fd[0], child, sizeof(pid_t));
|
|
|
|
close(fd[0]);
|
|
|
|
// cleanup child process
|
|
|
|
wait(0);
|
|
|
|
if (*child > 0) {
|
|
|
|
sway_log(L_DEBUG, "Child process created with pid %d", *child);
|
|
|
|
// TODO: keep track of this pid and open the corresponding view on the current workspace
|
|
|
|
// blocked pending feature in wlc
|
|
|
|
}
|
|
|
|
free(child);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_debuglog(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "debuglog", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
} else if (strcasecmp(argv[0], "toggle") == 0) {
|
|
|
|
if (config->reading) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "debuglog toggle", "Can't be used in config file.");
|
|
|
|
}
|
|
|
|
if (toggle_debug_logging()) {
|
|
|
|
sway_log(L_DEBUG, "Debuglog turned on.");
|
|
|
|
}
|
|
|
|
} else if (strcasecmp(argv[0], "on") == 0) {
|
|
|
|
set_log_level(L_DEBUG);
|
|
|
|
sway_log(L_DEBUG, "Debuglog turned on.");
|
|
|
|
} else if (strcasecmp(argv[0], "off") == 0) {
|
|
|
|
reset_log_level();
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "debuglog", "Expected 'debuglog on|off|toggle'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_exec(int argc, char **argv) {
|
|
|
|
if (!config->active) return cmd_results_new(CMD_DEFER, "exec", NULL);
|
|
|
|
if (config->reloading) {
|
|
|
|
char *args = join_args(argv, argc);
|
|
|
|
sway_log(L_DEBUG, "Ignoring 'exec %s' due to reload", args);
|
|
|
|
free(args);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
return cmd_exec_always(argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void kill_views(swayc_t *container, void *data) {
|
|
|
|
if (container->type == C_VIEW) {
|
|
|
|
wlc_view_close(container->handle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_exit(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "exit", "Can't be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "exit", EXPECTED_EQUAL_TO, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
// Close all views
|
|
|
|
container_map(&root_container, kill_views, NULL);
|
|
|
|
sway_terminate();
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_floating(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "floating", "Can't be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "floating", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
swayc_t *view = get_focused_container(&root_container);
|
|
|
|
bool wants_floating;
|
|
|
|
if (strcasecmp(argv[0], "enable") == 0) {
|
|
|
|
wants_floating = true;
|
|
|
|
} else if (strcasecmp(argv[0], "disable") == 0) {
|
|
|
|
wants_floating = false;
|
|
|
|
} else if (strcasecmp(argv[0], "toggle") == 0) {
|
|
|
|
wants_floating = !view->is_floating;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "floating",
|
|
|
|
"Expected 'floating <enable|disable|toggle>");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prevent running floating commands on things like workspaces
|
|
|
|
if (view->type != C_VIEW) {
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change from nonfloating to floating
|
|
|
|
if (!view->is_floating && wants_floating) {
|
|
|
|
// Remove view from its current location
|
|
|
|
destroy_container(remove_child(view));
|
|
|
|
|
|
|
|
// and move it into workspace floating
|
|
|
|
add_floating(swayc_active_workspace(), view);
|
|
|
|
view->x = (swayc_active_workspace()->width - view->width)/2;
|
|
|
|
view->y = (swayc_active_workspace()->height - view->height)/2;
|
|
|
|
if (view->desired_width != -1) {
|
|
|
|
view->width = view->desired_width;
|
|
|
|
}
|
|
|
|
if (view->desired_height != -1) {
|
|
|
|
view->height = view->desired_height;
|
|
|
|
}
|
|
|
|
arrange_windows(swayc_active_workspace(), -1, -1);
|
|
|
|
|
|
|
|
} else if (view->is_floating && !wants_floating) {
|
|
|
|
// Delete the view from the floating list and unset its is_floating flag
|
|
|
|
remove_child(view);
|
|
|
|
view->is_floating = false;
|
|
|
|
// Get the properly focused container, and add in the view there
|
|
|
|
swayc_t *focused = container_under_pointer();
|
|
|
|
// If focused is null, it's because the currently focused container is a workspace
|
|
|
|
if (focused == NULL || focused->is_floating) {
|
|
|
|
focused = swayc_active_workspace();
|
|
|
|
}
|
|
|
|
set_focused_container(focused);
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "Non-floating focused container is %p", focused);
|
|
|
|
|
|
|
|
// Case of focused workspace, just create as child of it
|
|
|
|
if (focused->type == C_WORKSPACE) {
|
|
|
|
add_child(focused, view);
|
|
|
|
}
|
|
|
|
// Regular case, create as sibling of current container
|
|
|
|
else {
|
|
|
|
add_sibling(focused, view);
|
|
|
|
}
|
|
|
|
// Refocus on the view once its been put back into the layout
|
|
|
|
view->width = view->height = 0;
|
|
|
|
arrange_windows(swayc_active_workspace(), -1, -1);
|
|
|
|
remove_view_from_scratchpad(view);
|
|
|
|
}
|
|
|
|
set_focused_container(view);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_floating_mod(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
int i, j;
|
|
|
|
list_t *split = split_string(argv[0], "+");
|
|
|
|
config->floating_mod = 0;
|
|
|
|
|
|
|
|
// set modifier keys
|
|
|
|
for (i = 0; i < split->length; ++i) {
|
|
|
|
for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) {
|
|
|
|
if (strcasecmp(modifiers[j].name, split->items[i]) == 0) {
|
|
|
|
config->floating_mod |= modifiers[j].mod;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_flat_list(split);
|
|
|
|
if (!config->floating_mod) {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "floating_modifier", "Unknown keys %s", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc >= 2) {
|
|
|
|
if (strcasecmp("inverse", argv[1]) == 0) {
|
|
|
|
config->dragging_key = M_RIGHT_CLICK;
|
|
|
|
config->resizing_key = M_LEFT_CLICK;
|
|
|
|
} else if (strcasecmp("normal", argv[1]) == 0) {
|
|
|
|
config->dragging_key = M_LEFT_CLICK;
|
|
|
|
config->resizing_key = M_RIGHT_CLICK;
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "floating_modifier", "Invalid definition %s", argv[1]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_focus(int argc, char **argv) {
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "focus", "Can't be used in config file.");
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (argc > 0 && strcasecmp(argv[0], "output") == 0) {
|
|
|
|
swayc_t *output = NULL;
|
|
|
|
struct wlc_point abs_pos;
|
|
|
|
get_absolute_center_position(get_focused_container(&root_container), &abs_pos);
|
|
|
|
if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 2))) {
|
|
|
|
return error;
|
|
|
|
} else if (!(output = output_by_name(argv[1], &abs_pos))) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "focus output",
|
|
|
|
"Can't find output with name/at direction '%s' @ (%i,%i)", argv[1], abs_pos.x, abs_pos.y);
|
|
|
|
} else if (!workspace_switch(swayc_active_workspace_for(output))) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "focus output",
|
|
|
|
"Switching to workspace on output '%s' was blocked", argv[1]);
|
|
|
|
} else if (config->mouse_warping) {
|
|
|
|
swayc_t *focused = get_focused_view(output);
|
|
|
|
if (focused && focused->type == C_VIEW) {
|
|
|
|
center_pointer_on(focused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
} else if ((error = checkarg(argc, "focus", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
static int floating_toggled_index = 0;
|
|
|
|
static int tiled_toggled_index = 0;
|
|
|
|
if (strcasecmp(argv[0], "left") == 0) {
|
|
|
|
move_focus(MOVE_LEFT);
|
|
|
|
} else if (strcasecmp(argv[0], "right") == 0) {
|
|
|
|
move_focus(MOVE_RIGHT);
|
|
|
|
} else if (strcasecmp(argv[0], "up") == 0) {
|
|
|
|
move_focus(MOVE_UP);
|
|
|
|
} else if (strcasecmp(argv[0], "down") == 0) {
|
|
|
|
move_focus(MOVE_DOWN);
|
|
|
|
} else if (strcasecmp(argv[0], "parent") == 0) {
|
|
|
|
move_focus(MOVE_PARENT);
|
|
|
|
} else if (strcasecmp(argv[0], "mode_toggle") == 0) {
|
|
|
|
int i;
|
|
|
|
swayc_t *workspace = swayc_active_workspace();
|
|
|
|
swayc_t *focused = get_focused_view(workspace);
|
|
|
|
if (focused->is_floating) {
|
|
|
|
if (workspace->children->length > 0) {
|
|
|
|
for (i = 0;i < workspace->floating->length; i++) {
|
|
|
|
if (workspace->floating->items[i] == focused) {
|
|
|
|
floating_toggled_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (workspace->children->length > tiled_toggled_index) {
|
|
|
|
set_focused_container(get_focused_view(workspace->children->items[tiled_toggled_index]));
|
|
|
|
} else {
|
|
|
|
set_focused_container(get_focused_view(workspace->children->items[0]));
|
|
|
|
tiled_toggled_index = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (workspace->floating->length > 0) {
|
|
|
|
for (i = 0;i < workspace->children->length; i++) {
|
|
|
|
if (workspace->children->items[i] == focused) {
|
|
|
|
tiled_toggled_index = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (workspace->floating->length > floating_toggled_index) {
|
|
|
|
swayc_t *floating = workspace->floating->items[floating_toggled_index];
|
|
|
|
set_focused_container(get_focused_view(floating));
|
|
|
|
} else {
|
|
|
|
swayc_t *floating = workspace->floating->items[workspace->floating->length - 1];
|
|
|
|
set_focused_container(get_focused_view(floating));
|
|
|
|
tiled_toggled_index = workspace->floating->length - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "focus",
|
|
|
|
"Expected 'focus <direction|parent|mode_toggle>' or 'focus output <direction|name>'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->focus_follows_mouse = !strcasecmp(argv[0], "yes");
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_seamless_mouse(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "seamless_mouse", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
config->seamless_mouse = (strcasecmp(argv[0], "on") == 0 || strcasecmp(argv[0], "yes") == 0);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hide_view_in_scratchpad(swayc_t *sp_view) {
|
|
|
|
if (sp_view == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wlc_view_set_mask(sp_view->handle, 0);
|
|
|
|
sp_view->visible = false;
|
|
|
|
swayc_t *ws = sp_view->parent;
|
|
|
|
remove_child(sp_view);
|
|
|
|
if (swayc_active_workspace() != ws && ws->floating->length == 0 && ws->children->length == 0) {
|
|
|
|
destroy_workspace(ws);
|
|
|
|
}
|
|
|
|
arrange_windows(ws, -1, -1);
|
|
|
|
set_focused_container(container_under_pointer());
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_mode(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
char *mode_name = join_args(argv, argc);
|
|
|
|
int mode_len = strlen(mode_name);
|
|
|
|
bool mode_make = mode_name[mode_len-1] == '{';
|
|
|
|
if (mode_make) {
|
|
|
|
if (!config->reading)
|
|
|
|
return cmd_results_new(CMD_FAILURE, "mode", "Can only be used in config file.");
|
|
|
|
// Trim trailing spaces
|
|
|
|
do {
|
|
|
|
mode_name[--mode_len] = 0;
|
|
|
|
} while(isspace(mode_name[mode_len-1]));
|
|
|
|
}
|
|
|
|
struct sway_mode *mode = NULL;
|
|
|
|
// Find mode
|
|
|
|
int i, len = config->modes->length;
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
|
|
struct sway_mode *find = config->modes->items[i];
|
|
|
|
if (strcasecmp(find->name, mode_name) == 0) {
|
|
|
|
mode = find;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create mode if it doesnt exist
|
|
|
|
if (!mode && mode_make) {
|
|
|
|
mode = malloc(sizeof*mode);
|
|
|
|
mode->name = strdup(mode_name);
|
|
|
|
mode->bindings = create_list();
|
|
|
|
list_add(config->modes, mode);
|
|
|
|
}
|
|
|
|
if (!mode) {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "mode", "Unknown mode `%s'", mode_name);
|
|
|
|
free(mode_name);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((config->reading && mode_make) || (!config->reading && !mode_make)) {
|
|
|
|
sway_log(L_DEBUG, "Switching to mode `%s'",mode->name);
|
|
|
|
}
|
|
|
|
free(mode_name);
|
|
|
|
// Set current mode
|
|
|
|
config->current_mode = mode;
|
|
|
|
return cmd_results_new(mode_make ? CMD_BLOCK_MODE : CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_mouse_warping(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "mouse_warping", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
} else if (strcasecmp(argv[0], "output") == 0) {
|
|
|
|
config->mouse_warping = true;
|
|
|
|
} else if (strcasecmp(argv[0], "none") == 0) {
|
|
|
|
config->mouse_warping = false;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "mouse_warping", "Expected 'mouse_warping output|none'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_move(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "move", "Can't be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
const char* expected_syntax = "Expected 'move <left|right|up|down>' or "
|
|
|
|
"'move <container|window> to workspace <name>' or "
|
|
|
|
"'move <container|window|workspace> to output <name|direction>'";
|
|
|
|
swayc_t *view = get_focused_container(&root_container);
|
|
|
|
|
|
|
|
if (strcasecmp(argv[0], "left") == 0) {
|
|
|
|
move_container(view, MOVE_LEFT);
|
|
|
|
} else if (strcasecmp(argv[0], "right") == 0) {
|
|
|
|
move_container(view, MOVE_RIGHT);
|
|
|
|
} else if (strcasecmp(argv[0], "up") == 0) {
|
|
|
|
move_container(view, MOVE_UP);
|
|
|
|
} else if (strcasecmp(argv[0], "down") == 0) {
|
|
|
|
move_container(view, MOVE_DOWN);
|
|
|
|
} else if (strcasecmp(argv[0], "container") == 0 || strcasecmp(argv[0], "window") == 0) {
|
|
|
|
// "move container ...
|
|
|
|
if ((error = checkarg(argc, "move container/window", EXPECTED_AT_LEAST, 4))) {
|
|
|
|
return error;
|
|
|
|
} else if ( strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "workspace") == 0) {
|
|
|
|
// move container to workspace x
|
|
|
|
if (view->type != C_CONTAINER && view->type != C_VIEW) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *ws_name = argv[3];
|
|
|
|
swayc_t *ws;
|
|
|
|
if (argc == 5 && strcasecmp(ws_name, "number") == 0) {
|
|
|
|
// move "container to workspace number x"
|
|
|
|
ws_name = argv[4];
|
|
|
|
ws = workspace_by_number(ws_name);
|
|
|
|
} else {
|
|
|
|
ws = workspace_by_name(ws_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ws == NULL) {
|
|
|
|
ws = workspace_create(ws_name);
|
|
|
|
}
|
|
|
|
move_container_to(view, get_focused_container(ws));
|
|
|
|
} else if (strcasecmp(argv[1], "to") == 0 && strcasecmp(argv[2], "output") == 0) {
|
|
|
|
// move container to output x
|
|
|
|
swayc_t *output = NULL;
|
|
|
|
struct wlc_point abs_pos;
|
|
|
|
get_absolute_center_position(view, &abs_pos);
|
|
|
|
if (view->type != C_CONTAINER && view->type != C_VIEW) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "move", "Can only move containers and views.");
|
|
|
|
} else if (!(output = output_by_name(argv[3], &abs_pos))) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "move",
|
|
|
|
"Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
|
|
|
|
} else {
|
|
|
|
swayc_t *container = get_focused_container(output);
|
|
|
|
if (container->is_floating) {
|
|
|
|
move_container_to(view, container->parent);
|
|
|
|
} else {
|
|
|
|
move_container_to(view, container);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
|
|
|
|
}
|
|
|
|
} else if (strcasecmp(argv[0], "workspace") == 0) {
|
|
|
|
// move workspace (to output x)
|
|
|
|
swayc_t *output = NULL;
|
|
|
|
struct wlc_point abs_pos;
|
|
|
|
get_absolute_center_position(view, &abs_pos);
|
|
|
|
if ((error = checkarg(argc, "move workspace", EXPECTED_EQUAL_TO, 4))) {
|
|
|
|
return error;
|
|
|
|
} else if (strcasecmp(argv[1], "to") != 0 || strcasecmp(argv[2], "output") != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
|
|
|
|
} else if (!(output = output_by_name(argv[3], &abs_pos))) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "move workspace",
|
|
|
|
"Can't find output with name/direction '%s' @ (%i,%i)", argv[3], abs_pos.x, abs_pos.y);
|
|
|
|
}
|
|
|
|
if (view->type == C_WORKSPACE) {
|
|
|
|
// This probably means we're moving an empty workspace, but
|
|
|
|
// that's fine.
|
|
|
|
move_workspace_to(view, output);
|
|
|
|
} else {
|
|
|
|
swayc_t *workspace = swayc_parent_by_type(view, C_WORKSPACE);
|
|
|
|
move_workspace_to(workspace, output);
|
|
|
|
}
|
|
|
|
} else if (strcasecmp(argv[0], "scratchpad") == 0) {
|
|
|
|
// move scratchpad ...
|
|
|
|
if (view->type != C_CONTAINER && view->type != C_VIEW) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "move scratchpad", "Can only move containers and views.");
|
|
|
|
}
|
|
|
|
swayc_t *view = get_focused_container(&root_container);
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < scratchpad->length; i++) {
|
|
|
|
if (scratchpad->items[i] == view) {
|
|
|
|
hide_view_in_scratchpad(view);
|
|
|
|
sp_view = NULL;
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
list_add(scratchpad, view);
|
|
|
|
if (!view->is_floating) {
|
|
|
|
destroy_container(remove_child(view));
|
|
|
|
} else {
|
|
|
|
remove_child(view);
|
|
|
|
}
|
|
|
|
wlc_view_set_mask(view->handle, 0);
|
|
|
|
arrange_windows(swayc_active_workspace(), -1, -1);
|
|
|
|
swayc_t *focused = container_under_pointer();
|
|
|
|
if (focused == NULL) {
|
|
|
|
focused = swayc_active_workspace();
|
|
|
|
}
|
|
|
|
set_focused_container(focused);
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "move", expected_syntax);
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_orientation(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (!config->reading) return cmd_results_new(CMD_FAILURE, "orientation", "Can only be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "orientation", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (strcasecmp(argv[0], "horizontal") == 0) {
|
|
|
|
config->default_orientation = L_HORIZ;
|
|
|
|
} else if (strcasecmp(argv[0], "vertical") == 0) {
|
|
|
|
config->default_orientation = L_VERT;
|
|
|
|
} else if (strcasecmp(argv[0], "auto") == 0) {
|
|
|
|
// Do nothing
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "orientation", "Expected 'orientation <horizontal|vertical|auto>'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_output(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "output", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
const char *name = argv[0];
|
|
|
|
|
|
|
|
struct output_config *output = calloc(1, sizeof(struct output_config));
|
|
|
|
output->x = output->y = output->width = output->height = -1;
|
|
|
|
output->name = strdup(name);
|
|
|
|
output->enabled = -1;
|
|
|
|
|
|
|
|
// TODO: atoi doesn't handle invalid numbers
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < argc; ++i) {
|
|
|
|
const char *command = argv[i];
|
|
|
|
|
|
|
|
if (strcasecmp(command, "disable") == 0) {
|
|
|
|
output->enabled = 0;
|
|
|
|
} else if (strcasecmp(command, "resolution") == 0 || strcasecmp(command, "res") == 0) {
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument.");
|
|
|
|
}
|
|
|
|
char *res = argv[i];
|
|
|
|
char *x = strchr(res, 'x');
|
|
|
|
int width = -1, height = -1;
|
|
|
|
if (x != NULL) {
|
|
|
|
// Format is 1234x4321
|
|
|
|
*x = '\0';
|
|
|
|
width = atoi(res);
|
|
|
|
height = atoi(x + 1);
|
|
|
|
*x = 'x';
|
|
|
|
} else {
|
|
|
|
// Format is 1234 4321
|
|
|
|
width = atoi(res);
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing resolution argument (height).");
|
|
|
|
}
|
|
|
|
res = argv[i];
|
|
|
|
height = atoi(res);
|
|
|
|
}
|
|
|
|
output->width = width;
|
|
|
|
output->height = height;
|
|
|
|
} else if (strcasecmp(command, "position") == 0 || strcasecmp(command, "pos") == 0) {
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing position argument.");
|
|
|
|
}
|
|
|
|
char *res = argv[i];
|
|
|
|
char *c = strchr(res, ',');
|
|
|
|
int x = -1, y = -1;
|
|
|
|
if (c != NULL) {
|
|
|
|
// Format is 1234,4321
|
|
|
|
*c = '\0';
|
|
|
|
x = atoi(res);
|
|
|
|
y = atoi(c + 1);
|
|
|
|
*c = ',';
|
|
|
|
} else {
|
|
|
|
// Format is 1234 4321
|
|
|
|
x = atoi(res);
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing position argument (y).");
|
|
|
|
}
|
|
|
|
res = argv[i];
|
|
|
|
y = atoi(res);
|
|
|
|
}
|
|
|
|
output->x = x;
|
|
|
|
output->y = y;
|
|
|
|
} else if (strcasecmp(command, "background") == 0 || strcasecmp(command, "bg") == 0) {
|
|
|
|
wordexp_t p;
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing background file.");
|
|
|
|
}
|
|
|
|
char *src = argv[i];
|
|
|
|
if (++i >= argc) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Missing background scaling mode.");
|
|
|
|
}
|
|
|
|
char *mode = argv[i];
|
|
|
|
if (wordexp(src, &p, 0) != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Invalid syntax (%s)", src);
|
|
|
|
}
|
|
|
|
src = p.we_wordv[0];
|
|
|
|
if (access(src, F_OK) == -1) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Background file unreadable (%s)", src);
|
|
|
|
}
|
|
|
|
for (char *m = mode; *m; ++m) *m = tolower(*m);
|
|
|
|
// Check mode
|
|
|
|
bool valid = false;
|
|
|
|
size_t j;
|
|
|
|
for (j = 0; j < sizeof(bg_options) / sizeof(char *); ++j) {
|
|
|
|
if (strcasecmp(mode, bg_options[j]) == 0) {
|
|
|
|
valid = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!valid) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "output", "Invalid background scaling mode.");
|
|
|
|
}
|
|
|
|
output->background = strdup(src);
|
|
|
|
output->background_option = strdup(mode);
|
|
|
|
wordfree(&p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
i = list_seq_find(config->output_configs, output_name_cmp, name);
|
|
|
|
if (i >= 0) {
|
|
|
|
// merge existing config
|
|
|
|
struct output_config *oc = config->output_configs->items[i];
|
|
|
|
merge_output_config(oc, output);
|
|
|
|
free_output_config(output);
|
|
|
|
output = oc;
|
|
|
|
} else {
|
|
|
|
list_add(config->output_configs, output);
|
|
|
|
}
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "Config stored for output %s (enabled:%d) (%d x %d @ %d, %d) (bg %s %s)",
|
|
|
|
output->name, output->enabled, output->width,
|
|
|
|
output->height, output->x, output->y, output->background,
|
|
|
|
output->background_option);
|
|
|
|
|
|
|
|
if (output->name) {
|
|
|
|
// Try to find the output container and apply configuration now. If
|
|
|
|
// this is during startup then there will be no container and config
|
|
|
|
// will be applied during normal "new output" event from wlc.
|
|
|
|
swayc_t *cont = NULL;
|
|
|
|
for (int i = 0; i < root_container.children->length; ++i) {
|
|
|
|
cont = root_container.children->items[i];
|
|
|
|
if (cont->name && strcmp(cont->name, output->name) == 0) {
|
|
|
|
apply_output_config(output, cont);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_gaps(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
const char* expected_syntax =
|
|
|
|
"Expected 'gaps edge_gaps <on|off|toggle>' or "
|
|
|
|
"'gaps <inner|outer> <current|all|workspace> <set|plus|minus n>'";
|
|
|
|
const char *amount_str = argv[0];
|
|
|
|
// gaps amount
|
|
|
|
if (argc >= 1 && isdigit(*amount_str)) {
|
|
|
|
int amount = (int)strtol(amount_str, NULL, 10);
|
|
|
|
if (errno == ERANGE) {
|
|
|
|
errno = 0;
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
|
|
|
|
}
|
|
|
|
config->gaps_inner = config->gaps_outer = amount;
|
|
|
|
arrange_windows(&root_container, -1, -1);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
// gaps inner|outer n
|
|
|
|
else if (argc >= 2 && isdigit((amount_str = argv[1])[0])) {
|
|
|
|
int amount = (int)strtol(amount_str, NULL, 10);
|
|
|
|
if (errno == ERANGE) {
|
|
|
|
errno = 0;
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
|
|
|
|
}
|
|
|
|
const char *target_str = argv[0];
|
|
|
|
if (strcasecmp(target_str, "inner") == 0) {
|
|
|
|
config->gaps_inner = amount;
|
|
|
|
} else if (strcasecmp(target_str, "outer") == 0) {
|
|
|
|
config->gaps_outer = amount;
|
|
|
|
}
|
|
|
|
arrange_windows(&root_container, -1, -1);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
} else if (argc == 2 && strcasecmp(argv[0], "edge_gaps") == 0) {
|
|
|
|
// gaps edge_gaps <on|off|toggle>
|
|
|
|
if (strcasecmp(argv[1], "toggle") == 0) {
|
|
|
|
if (config->reading) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "gaps edge_gaps toggle",
|
|
|
|
"Can't be used in config file.");
|
|
|
|
}
|
|
|
|
config->edge_gaps = !config->edge_gaps;
|
|
|
|
} else {
|
|
|
|
config->edge_gaps =
|
|
|
|
(strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "on") == 0);
|
|
|
|
}
|
|
|
|
arrange_windows(&root_container, -1, -1);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
// gaps inner|outer current|all set|plus|minus n
|
|
|
|
if (argc < 4 || config->reading) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
|
|
|
|
}
|
|
|
|
// gaps inner|outer ...
|
|
|
|
const char *inout_str = argv[0];
|
|
|
|
enum {INNER, OUTER} inout;
|
|
|
|
if (strcasecmp(inout_str, "inner") == 0) {
|
|
|
|
inout = INNER;
|
|
|
|
} else if (strcasecmp(inout_str, "outer") == 0) {
|
|
|
|
inout = OUTER;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
|
|
|
|
}
|
|
|
|
|
|
|
|
// gaps ... current|all ...
|
|
|
|
const char *target_str = argv[1];
|
|
|
|
enum {CURRENT, WORKSPACE, ALL} target;
|
|
|
|
if (strcasecmp(target_str, "current") == 0) {
|
|
|
|
target = CURRENT;
|
|
|
|
} else if (strcasecmp(target_str, "all") == 0) {
|
|
|
|
target = ALL;
|
|
|
|
} else if (strcasecmp(target_str, "workspace") == 0) {
|
|
|
|
if (inout == OUTER) {
|
|
|
|
target = CURRENT;
|
|
|
|
} else {
|
|
|
|
// Set gap for views in workspace
|
|
|
|
target = WORKSPACE;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
|
|
|
|
}
|
|
|
|
|
|
|
|
// gaps ... n
|
|
|
|
amount_str = argv[3];
|
|
|
|
int amount = (int)strtol(amount_str, NULL, 10);
|
|
|
|
if (errno == ERANGE) {
|
|
|
|
errno = 0;
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", "Number is out out of range.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// gaps ... set|plus|minus ...
|
|
|
|
const char *method_str = argv[2];
|
|
|
|
enum {SET, ADD} method;
|
|
|
|
if (strcasecmp(method_str, "set") == 0) {
|
|
|
|
method = SET;
|
|
|
|
} else if (strcasecmp(method_str, "plus") == 0) {
|
|
|
|
method = ADD;
|
|
|
|
} else if (strcasecmp(method_str, "minus") == 0) {
|
|
|
|
method = ADD;
|
|
|
|
amount *= -1;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "gaps", expected_syntax);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target == CURRENT) {
|
|
|
|
swayc_t *cont;
|
|
|
|
if (inout == OUTER) {
|
|
|
|
if ((cont = swayc_active_workspace()) == NULL) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "gaps", "There's no active workspace.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((cont = get_focused_view(&root_container))->type != C_VIEW) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "gaps", "Currently focused item is not a view.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cont->gaps = swayc_gap(cont);
|
|
|
|
if (method == SET) {
|
|
|
|
cont->gaps = amount;
|
|
|
|
} else if ((cont->gaps += amount) < 0) {
|
|
|
|
cont->gaps = 0;
|
|
|
|
}
|
|
|
|
arrange_windows(cont->parent, -1, -1);
|
|
|
|
} else if (inout == OUTER) {
|
|
|
|
//resize all workspace.
|
|
|
|
int i,j;
|
|
|
|
for (i = 0; i < root_container.children->length; ++i) {
|
|
|
|
swayc_t *op = root_container.children->items[i];
|
|
|
|
for (j = 0; j < op->children->length; ++j) {
|
|
|
|
swayc_t *ws = op->children->items[j];
|
|
|
|
if (method == SET) {
|
|
|
|
ws->gaps = amount;
|
|
|
|
} else if ((ws->gaps += amount) < 0) {
|
|
|
|
ws->gaps = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arrange_windows(&root_container, -1, -1);
|
|
|
|
} else {
|
|
|
|
// Resize gaps for all views in workspace
|
|
|
|
swayc_t *top;
|
|
|
|
if (target == WORKSPACE) {
|
|
|
|
if ((top = swayc_active_workspace()) == NULL) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "gaps", "There's currently no active workspace.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
top = &root_container;
|
|
|
|
}
|
|
|
|
int top_gap = top->gaps;
|
|
|
|
container_map(top, method == SET ? set_gaps : add_gaps, &amount);
|
|
|
|
top->gaps = top_gap;
|
|
|
|
arrange_windows(top, -1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_kill(int argc, char **argv) {
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running.");
|
|
|
|
|
|
|
|
swayc_t *view = get_focused_container(&root_container);
|
|
|
|
wlc_view_close(view->handle);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_layout(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "layout", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "layout", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "layout", EXPECTED_MORE_THAN, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
swayc_t *parent = get_focused_container(&root_container);
|
|
|
|
while (parent->type == C_VIEW) {
|
|
|
|
parent = parent->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(argv[0], "splith") == 0) {
|
|
|
|
parent->layout = L_HORIZ;
|
|
|
|
} else if (strcasecmp(argv[0], "splitv") == 0) {
|
|
|
|
parent->layout = L_VERT;
|
|
|
|
} else if (strcasecmp(argv[0], "toggle") == 0 && argc == 2 && strcasecmp(argv[1], "split") == 0) {
|
|
|
|
if (parent->layout == L_VERT) {
|
|
|
|
parent->layout = L_HORIZ;
|
|
|
|
} else {
|
|
|
|
parent->layout = L_VERT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arrange_windows(parent, parent->width, parent->height);
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_reload(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "reload", "Can't be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (!load_config(NULL)) return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config.");
|
|
|
|
|
|
|
|
int i;
|
|
|
|
swayc_t *cont = NULL;
|
|
|
|
for (i = 0; i < root_container.children->length; ++i) {
|
|
|
|
cont = root_container.children->items[i];
|
|
|
|
if (cont->type == C_OUTPUT) {
|
|
|
|
load_swaybars(cont, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
arrange_windows(&root_container, -1, -1);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_resize(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "resize", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "resize", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
char *end;
|
|
|
|
int amount = (int)strtol(argv[2], &end, 10);
|
|
|
|
if (errno == ERANGE || amount == 0) {
|
|
|
|
errno = 0;
|
|
|
|
return cmd_results_new(CMD_INVALID, "resize", "Number is out of range.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(argv[0], "shrink") != 0 && strcmp(argv[0], "grow") != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "resize",
|
|
|
|
"Expected 'resize <shrink|grow> <width|height> <amount>'");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(argv[0], "shrink") == 0) {
|
|
|
|
amount *= -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(argv[1], "width") == 0) {
|
|
|
|
resize_tiled(amount, true);
|
|
|
|
} else if (strcmp(argv[1], "height") == 0) {
|
|
|
|
resize_tiled(amount, false);
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "resize",
|
|
|
|
"Expected 'resize <shrink|grow> <width|height> <amount>'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_bar(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "bar", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->reading && strcmp("{", argv[0]) != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "bar",
|
|
|
|
"Expected '{' at start of bar config definition.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->reading) {
|
|
|
|
if (argc > 1) {
|
|
|
|
if (strcasecmp("mode", argv[0]) == 0) {
|
|
|
|
return bar_cmd_mode(argc-1, argv + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp("hidden_state", argv[0]) == 0) {
|
|
|
|
return bar_cmd_hidden_state(argc-1, argv + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_FAILURE, "bar", "Can only be used in config file.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create new bar with default values
|
|
|
|
struct bar_config *bar = default_bar_config();
|
|
|
|
|
|
|
|
// set bar id
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < config->bars->length; ++i) {
|
|
|
|
if (bar == config->bars->items[i]) {
|
|
|
|
const int len = 5 + numlen(i); // "bar-" + i + \0
|
|
|
|
bar->id = malloc(len * sizeof(char));
|
|
|
|
snprintf(bar->id, len, "bar-%d", i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set current bar
|
|
|
|
config->current_bar = bar;
|
|
|
|
sway_log(L_DEBUG, "Configuring bar %s", bar->id);
|
|
|
|
return cmd_results_new(CMD_BLOCK_BAR, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static swayc_t *fetch_view_from_scratchpad() {
|
|
|
|
if (sp_index >= scratchpad->length) {
|
|
|
|
sp_index = 0;
|
|
|
|
}
|
|
|
|
swayc_t *view = scratchpad->items[sp_index++];
|
|
|
|
|
|
|
|
if (wlc_view_get_output(view->handle) != swayc_active_output()->handle) {
|
|
|
|
wlc_view_set_output(view->handle, swayc_active_output()->handle);
|
|
|
|
}
|
|
|
|
if (!view->is_floating) {
|
|
|
|
view->width = swayc_active_workspace()->width/2;
|
|
|
|
view->height = swayc_active_workspace()->height/2;
|
|
|
|
view->x = (swayc_active_workspace()->width - view->width)/2;
|
|
|
|
view->y = (swayc_active_workspace()->height - view->height)/2;
|
|
|
|
}
|
|
|
|
if (swayc_active_workspace()->width < view->x + 20 || view->x + view->width < 20) {
|
|
|
|
view->x = (swayc_active_workspace()->width - view->width)/2;
|
|
|
|
}
|
|
|
|
if (swayc_active_workspace()->height < view->y + 20 || view->y + view->height < 20) {
|
|
|
|
view->y = (swayc_active_workspace()->height - view->height)/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_floating(swayc_active_workspace(), view);
|
|
|
|
wlc_view_set_mask(view->handle, VISIBLE);
|
|
|
|
view->visible = true;
|
|
|
|
arrange_windows(swayc_active_workspace(), -1, -1);
|
|
|
|
set_focused_container(view);
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
|
|
|
void remove_view_from_scratchpad(swayc_t *view) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < scratchpad->length; i++) {
|
|
|
|
if (scratchpad->items[i] == view) {
|
|
|
|
if (sp_index == 0) {
|
|
|
|
sp_index = scratchpad->length - 1;
|
|
|
|
} else {
|
|
|
|
sp_index--;
|
|
|
|
}
|
|
|
|
list_del(scratchpad, sp_index);
|
|
|
|
sp_view = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_scratchpad(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "scratchpad", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp(argv[0], "show") == 0 && scratchpad->length > 0) {
|
|
|
|
if (!sp_view) {
|
|
|
|
sp_view = fetch_view_from_scratchpad();
|
|
|
|
} else {
|
|
|
|
if (swayc_active_workspace() != sp_view->parent) {
|
|
|
|
hide_view_in_scratchpad(sp_view);
|
|
|
|
if (sp_index == 0) {
|
|
|
|
sp_index = scratchpad->length;
|
|
|
|
}
|
|
|
|
sp_index--;
|
|
|
|
sp_view = fetch_view_from_scratchpad();
|
|
|
|
} else {
|
|
|
|
hide_view_in_scratchpad(sp_view);
|
|
|
|
sp_view = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_FAILURE, "scratchpad", "Expected 'scratchpad show' when scratchpad is not empty.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort in order of longest->shortest
|
replace non-standard qsort_r with qsort
I've tried to make as few changes, as possible.
Usually the reason for using qsort_r is, that you can pass an extra userdata pointer to the
compare function. However, in sway list_sort wrapped qsort_r and always called a wrapper
function for comparing, the wrapper function then had the real compare function as argument.
The only thing, that the wrapper function does, is dereferencing the 'left' and 'right' function
arguments before passing them to the real compare function.
I have renamed list_sort to list_qsort to avoid confusion (so nobody tries to use list_qsort like
list_sort) and removed the wrapper functionality. Now the dereferencing must be done in the
compare function, that gets passed.
Some compare functions were used in both list_sort and list_seq_find. To make the difference
clear, I've added a '_qsort' suffix to the compare functions, that are intended to be used with
the new list_qsort. (In other words: list_qsort is not compatible anymore with list_seq_find).
- Changed and renamed function (it isn't used anywhere but in commands.c, and only for sorting):
compare_set -> compare_set_qsort
- New wrapper functions:
sway_binding_cmp_qsort (for sway_binding_cmp)
sway_mouse_binding_cmp_qsort (for sway_mouse_binding_cmp)
9 years ago
|
|
|
static int compare_set_qsort(const void *_l, const void *_r) {
|
|
|
|
struct sway_variable const *l = *(void **)_l;
|
|
|
|
struct sway_variable const *r = *(void **)_r;
|
|
|
|
return strlen(r->name) - strlen(l->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_set(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (!config->reading) return cmd_results_new(CMD_FAILURE, "set", "Can only be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "set", EXPECTED_AT_LEAST, 2))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sway_variable *var = NULL;
|
|
|
|
// Find old variable if it exists
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < config->symbols->length; ++i) {
|
|
|
|
var = config->symbols->items[i];
|
|
|
|
if (strcmp(var->name, argv[0]) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
var = NULL;
|
|
|
|
}
|
|
|
|
if (var) {
|
|
|
|
free(var->value);
|
|
|
|
} else {
|
|
|
|
var = malloc(sizeof(struct sway_variable));
|
|
|
|
var->name = strdup(argv[0]);
|
|
|
|
list_add(config->symbols, var);
|
replace non-standard qsort_r with qsort
I've tried to make as few changes, as possible.
Usually the reason for using qsort_r is, that you can pass an extra userdata pointer to the
compare function. However, in sway list_sort wrapped qsort_r and always called a wrapper
function for comparing, the wrapper function then had the real compare function as argument.
The only thing, that the wrapper function does, is dereferencing the 'left' and 'right' function
arguments before passing them to the real compare function.
I have renamed list_sort to list_qsort to avoid confusion (so nobody tries to use list_qsort like
list_sort) and removed the wrapper functionality. Now the dereferencing must be done in the
compare function, that gets passed.
Some compare functions were used in both list_sort and list_seq_find. To make the difference
clear, I've added a '_qsort' suffix to the compare functions, that are intended to be used with
the new list_qsort. (In other words: list_qsort is not compatible anymore with list_seq_find).
- Changed and renamed function (it isn't used anywhere but in commands.c, and only for sorting):
compare_set -> compare_set_qsort
- New wrapper functions:
sway_binding_cmp_qsort (for sway_binding_cmp)
sway_mouse_binding_cmp_qsort (for sway_mouse_binding_cmp)
9 years ago
|
|
|
list_qsort(config->symbols, compare_set_qsort);
|
|
|
|
}
|
|
|
|
var->value = join_args(argv + 1, argc - 1);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *_do_split(int argc, char **argv, int layout) {
|
|
|
|
char *name = layout == L_VERT ? "splitv" :
|
|
|
|
layout == L_HORIZ ? "splith" : "split";
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, name, "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, name, "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, name, EXPECTED_EQUAL_TO, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
swayc_t *focused = get_focused_container(&root_container);
|
|
|
|
|
|
|
|
// Case of floating window, dont split
|
|
|
|
if (focused->is_floating) {
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
/* Case that focus is on an workspace with 0/1 children.change its layout */
|
|
|
|
if (focused->type == C_WORKSPACE && focused->children->length <= 1) {
|
|
|
|
sway_log(L_DEBUG, "changing workspace layout");
|
|
|
|
focused->layout = layout;
|
|
|
|
} else if (focused->type != C_WORKSPACE && focused->parent->children->length == 1) {
|
|
|
|
/* Case of no siblings. change parent layout */
|
|
|
|
sway_log(L_DEBUG, "changing container layout");
|
|
|
|
focused->parent->layout = layout;
|
|
|
|
} else {
|
|
|
|
/* regular case where new split container is build around focused container
|
|
|
|
* or in case of workspace, container inherits its children */
|
|
|
|
sway_log(L_DEBUG, "Adding new container around current focused container");
|
|
|
|
swayc_t *parent = new_container(focused, layout);
|
|
|
|
set_focused_container(focused);
|
|
|
|
arrange_windows(parent, -1, -1);
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_split(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "split", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "split", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "split", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (strcasecmp(argv[0], "v") == 0 || strcasecmp(argv[0], "vertical") == 0) {
|
|
|
|
_do_split(argc - 1, argv + 1, L_VERT);
|
|
|
|
} else if (strcasecmp(argv[0], "h") == 0 || strcasecmp(argv[0], "horizontal") == 0) {
|
|
|
|
_do_split(argc - 1, argv + 1, L_HORIZ);
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_FAILURE, "split",
|
|
|
|
"Invalid split command (expected either horiziontal or vertical).");
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_splitv(int argc, char **argv) {
|
|
|
|
return _do_split(argc, argv, L_VERT);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_splith(int argc, char **argv) {
|
|
|
|
return _do_split(argc, argv, L_HORIZ);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_sticky(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "sticky", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "sticky", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "sticky", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
char *action = argv[0];
|
|
|
|
swayc_t *cont = get_focused_view(&root_container);
|
|
|
|
if (strcmp(action, "toggle") == 0) {
|
|
|
|
cont->sticky = !cont->sticky;
|
|
|
|
} else if (strcmp(action, "enable") == 0) {
|
|
|
|
cont->sticky = true;
|
|
|
|
} else if (strcmp(action, "disable") == 0) {
|
|
|
|
cont->sticky = false;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "sticky",
|
|
|
|
"Expected 'sticky enable|disable|toggle'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_log_colors(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (!config->reading) return cmd_results_new(CMD_FAILURE, "log_colors", "Can only be used in config file.");
|
|
|
|
if ((error = checkarg(argc, "log_colors", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (strcasecmp(argv[0], "no") == 0) {
|
|
|
|
sway_log_colors(0);
|
|
|
|
} else if (strcasecmp(argv[0], "yes") == 0) {
|
|
|
|
sway_log_colors(1);
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_FAILURE, "log_colors",
|
|
|
|
"Invalid log_colors command (expected `yes` or `no`, got '%s')", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_for_window(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "for_window", EXPECTED_AT_LEAST, 2))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
// add command to a criteria/command pair that is run against views when they appear.
|
|
|
|
char *criteria = argv[0], *cmdlist = join_args(argv + 1, argc - 1);
|
|
|
|
|
|
|
|
struct criteria *crit = malloc(sizeof(struct criteria));
|
|
|
|
crit->crit_raw = strdup(criteria);
|
|
|
|
crit->cmdlist = cmdlist;
|
|
|
|
crit->tokens = create_list();
|
|
|
|
char *err_str = extract_crit_tokens(crit->tokens, crit->crit_raw);
|
|
|
|
|
|
|
|
if (err_str) {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "for_window", err_str);
|
|
|
|
free(err_str);
|
|
|
|
free_criteria(crit);
|
|
|
|
} else if (crit->tokens->length == 0) {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "for_window", "Found no name/value pairs in criteria");
|
|
|
|
free_criteria(crit);
|
|
|
|
} else if (list_seq_find(config->criteria, criteria_cmp, crit) != -1) {
|
|
|
|
sway_log(L_DEBUG, "for_window: Duplicate, skipping.");
|
|
|
|
free_criteria(crit);
|
|
|
|
} else {
|
|
|
|
sway_log(L_DEBUG, "for_window: '%s' -> '%s' added", crit->crit_raw, crit->cmdlist);
|
|
|
|
list_add(config->criteria, crit);
|
|
|
|
}
|
|
|
|
return error ? error : cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_fullscreen(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if (config->reading) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can't be used in config file.");
|
|
|
|
if (!config->active) return cmd_results_new(CMD_FAILURE, "fullscreen", "Can only be used when sway is running.");
|
|
|
|
if ((error = checkarg(argc, "fullscreen", EXPECTED_AT_LEAST, 0))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
swayc_t *container = get_focused_view(&root_container);
|
|
|
|
swayc_t *workspace = swayc_parent_by_type(container, C_WORKSPACE);
|
|
|
|
bool current = swayc_is_fullscreen(container);
|
|
|
|
wlc_view_set_state(container->handle, WLC_BIT_FULLSCREEN, !current);
|
|
|
|
// Resize workspace if going from fullscreen -> notfullscreen
|
|
|
|
// otherwise just resize container
|
|
|
|
if (current) {
|
|
|
|
arrange_windows(workspace, -1, -1);
|
|
|
|
workspace->fullscreen = container;
|
|
|
|
} else {
|
|
|
|
arrange_windows(container, -1, -1);
|
|
|
|
workspace->fullscreen = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_workspace(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (argc == 1 || (argc == 2 && strcasecmp(argv[0], "number") == 0) ) {
|
|
|
|
if (config->reading || !config->active) {
|
|
|
|
return cmd_results_new(CMD_DEFER, "workspace", NULL);
|
|
|
|
}
|
|
|
|
// Handle workspace next/prev
|
|
|
|
swayc_t *ws = NULL;
|
|
|
|
if (argc == 2) {
|
|
|
|
if (!(ws=workspace_by_number(argv[1]))) {
|
|
|
|
ws = workspace_create(argv[1]);
|
|
|
|
}
|
|
|
|
}else if (strcasecmp(argv[0], "next") == 0) {
|
|
|
|
ws = workspace_next();
|
|
|
|
} else if (strcasecmp(argv[0], "prev") == 0) {
|
|
|
|
ws = workspace_prev();
|
|
|
|
} else if (strcasecmp(argv[0], "next_on_output") == 0) {
|
|
|
|
ws = workspace_output_next();
|
|
|
|
} else if (strcasecmp(argv[0], "prev_on_output") == 0) {
|
|
|
|
ws = workspace_output_prev();
|
|
|
|
} else if (strcasecmp(argv[0], "back_and_forth") == 0) {
|
|
|
|
if (prev_workspace_name) {
|
|
|
|
if (!(ws = workspace_by_name(prev_workspace_name))) {
|
|
|
|
ws = workspace_create(prev_workspace_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(ws= workspace_by_name(argv[0]))) {
|
|
|
|
ws = workspace_create(argv[0]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
swayc_t *old_output = swayc_active_output();
|
|
|
|
workspace_switch(ws);
|
|
|
|
swayc_t *new_output = swayc_active_output();
|
|
|
|
|
|
|
|
if (config->mouse_warping && old_output != new_output) {
|
|
|
|
swayc_t *focused = get_focused_view(ws);
|
|
|
|
if (focused && focused->type == C_VIEW) {
|
|
|
|
center_pointer_on(focused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (strcasecmp(argv[1], "output") == 0) {
|
|
|
|
if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
struct workspace_output *wso = calloc(1, sizeof(struct workspace_output));
|
|
|
|
wso->workspace = strdup(argv[0]);
|
|
|
|
wso->output = strdup(argv[2]);
|
|
|
|
int i = -1;
|
|
|
|
if ((i = list_seq_find(config->workspace_outputs, workspace_output_cmp_workspace, wso)) != -1) {
|
|
|
|
struct workspace_output *old = config->workspace_outputs->items[i];
|
|
|
|
free(old); // workspaces can only be assigned to a single output
|
|
|
|
list_del(config->workspace_outputs, i);
|
|
|
|
}
|
|
|
|
sway_log(L_DEBUG, "Assigning workspace %s to output %s", argv[0], argv[2]);
|
|
|
|
list_add(config->workspace_outputs, wso);
|
|
|
|
if (!config->reading) {
|
|
|
|
// TODO: Move workspace to output. (dont do so when reloading)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if (strcasecmp(argv[0], "yes") == 0) {
|
|
|
|
config->auto_back_and_forth = true;
|
|
|
|
} else if (strcasecmp(argv[0], "no") == 0) {
|
|
|
|
config->auto_back_and_forth = false;
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "workspace_auto_back_and_forth", "Expected 'workspace_auto_back_and_forth <yes|no>'");
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep alphabetized */
|
|
|
|
static struct cmd_handler handlers[] = {
|
|
|
|
{ "bar", cmd_bar },
|
|
|
|
{ "bindsym", cmd_bindsym },
|
|
|
|
{ "debuglog", cmd_debuglog },
|
|
|
|
{ "default_orientation", cmd_orientation },
|
|
|
|
{ "exec", cmd_exec },
|
|
|
|
{ "exec_always", cmd_exec_always },
|
|
|
|
{ "exit", cmd_exit },
|
|
|
|
{ "floating", cmd_floating },
|
|
|
|
{ "floating_modifier", cmd_floating_mod },
|
|
|
|
{ "focus", cmd_focus },
|
|
|
|
{ "focus_follows_mouse", cmd_focus_follows_mouse },
|
|
|
|
{ "for_window", cmd_for_window },
|
|
|
|
{ "fullscreen", cmd_fullscreen },
|
|
|
|
{ "gaps", cmd_gaps },
|
|
|
|
{ "kill", cmd_kill },
|
|
|
|
{ "layout", cmd_layout },
|
|
|
|
{ "log_colors", cmd_log_colors },
|
|
|
|
{ "mode", cmd_mode },
|
|
|
|
{ "mouse_warping", cmd_mouse_warping },
|
|
|
|
{ "move", cmd_move },
|
|
|
|
{ "output", cmd_output },
|
|
|
|
{ "reload", cmd_reload },
|
|
|
|
{ "resize", cmd_resize },
|
|
|
|
{ "scratchpad", cmd_scratchpad },
|
|
|
|
{ "seamless_mouse", cmd_seamless_mouse },
|
|
|
|
{ "set", cmd_set },
|
|
|
|
{ "split", cmd_split },
|
|
|
|
{ "splith", cmd_splith },
|
|
|
|
{ "splitv", cmd_splitv },
|
|
|
|
{ "sticky", cmd_sticky },
|
|
|
|
{ "workspace", cmd_workspace },
|
|
|
|
{ "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "binding_mode_indicator", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "binding_mode_indicator", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp("yes", argv[0]) == 0) {
|
|
|
|
config->current_bar->binding_mode_indicator = true;
|
|
|
|
sway_log(L_DEBUG, "Enabling binding mode indicator on bar: %s", config->current_bar->id);
|
|
|
|
} else if (strcasecmp("no", argv[0]) == 0) {
|
|
|
|
config->current_bar->binding_mode_indicator = false;
|
|
|
|
sway_log(L_DEBUG, "Disabling binding mode indicator on bar: %s", config->current_bar->id);
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "binding_mode_indicator", "Invalid value %s", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_bindsym(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "bindsym", EXPECTED_MORE_THAN, 1))) {
|
|
|
|
return error;
|
|
|
|
} else if (!config->reading) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "bindsym", "Can only be used in config file.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "bindsym", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(argv[1]) != 7) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
|
|
|
|
}
|
|
|
|
uint32_t numbutton = (uint32_t)atoi(argv[1] + 6);
|
|
|
|
if (numbutton < 1 || numbutton > 5 || strncmp(argv[1], "button", 6) != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "bindsym", "Invalid mouse binding %s", argv[1]);
|
|
|
|
}
|
|
|
|
struct sway_mouse_binding *binding = malloc(sizeof(struct sway_mouse_binding));
|
|
|
|
binding->button = numbutton;
|
|
|
|
binding->command = join_args(argv + 1, argc - 1);
|
|
|
|
|
|
|
|
struct bar_config *bar = config->current_bar;
|
|
|
|
int i = list_seq_find(bar->bindings, sway_mouse_binding_cmp_buttons, binding);
|
|
|
|
if (i > -1) {
|
|
|
|
sway_log(L_DEBUG, "bindsym - '%s' for swaybar already exists, overwriting", argv[0]);
|
|
|
|
struct sway_mouse_binding *dup = bar->bindings->items[i];
|
|
|
|
free_sway_mouse_binding(dup);
|
|
|
|
list_del(bar->bindings, i);
|
|
|
|
}
|
|
|
|
list_add(bar->bindings, binding);
|
replace non-standard qsort_r with qsort
I've tried to make as few changes, as possible.
Usually the reason for using qsort_r is, that you can pass an extra userdata pointer to the
compare function. However, in sway list_sort wrapped qsort_r and always called a wrapper
function for comparing, the wrapper function then had the real compare function as argument.
The only thing, that the wrapper function does, is dereferencing the 'left' and 'right' function
arguments before passing them to the real compare function.
I have renamed list_sort to list_qsort to avoid confusion (so nobody tries to use list_qsort like
list_sort) and removed the wrapper functionality. Now the dereferencing must be done in the
compare function, that gets passed.
Some compare functions were used in both list_sort and list_seq_find. To make the difference
clear, I've added a '_qsort' suffix to the compare functions, that are intended to be used with
the new list_qsort. (In other words: list_qsort is not compatible anymore with list_seq_find).
- Changed and renamed function (it isn't used anywhere but in commands.c, and only for sorting):
compare_set -> compare_set_qsort
- New wrapper functions:
sway_binding_cmp_qsort (for sway_binding_cmp)
sway_mouse_binding_cmp_qsort (for sway_mouse_binding_cmp)
9 years ago
|
|
|
list_qsort(bar->bindings, sway_mouse_binding_cmp_qsort);
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "bindsym - Bound %s to command %s when clicking swaybar", argv[0], binding->command);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_colors(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "colors", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp("{", argv[0]) != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "colors",
|
|
|
|
"Expected '{' at the start of colors config definition.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_BLOCK_BAR_COLORS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_font(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "font", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "font", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
char *font = join_args(argv, argc);
|
|
|
|
if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) {
|
|
|
|
free(config->current_bar->font);
|
|
|
|
config->current_bar->font = font;
|
|
|
|
sway_log(L_DEBUG, "Settings font '%s' for bar: %s", config->current_bar->font, config->current_bar->id);
|
|
|
|
} else {
|
|
|
|
sway_log(L_ERROR, "warning: non-pango font '%s' not supported.", font);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_height(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "height", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int height = atoi(argv[0]);
|
|
|
|
if (height < 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "height",
|
|
|
|
"Invalid height value: %s", argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
config->current_bar->height = height;
|
|
|
|
sway_log(L_DEBUG, "Setting bar height to %d on bar: %s", height, config->current_bar->id);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_set_hidden_state(struct bar_config *bar, const char *hidden_state) {
|
|
|
|
char *old_state = bar->hidden_state;
|
|
|
|
if (strcasecmp("toggle", hidden_state) == 0 && !config->reading) {
|
|
|
|
if (strcasecmp("hide", bar->hidden_state) == 0) {
|
|
|
|
bar->hidden_state = strdup("show");
|
|
|
|
} else if (strcasecmp("show", bar->hidden_state) == 0) {
|
|
|
|
bar->hidden_state = strdup("hide");
|
|
|
|
}
|
|
|
|
} else if (strcasecmp("hide", hidden_state) == 0) {
|
|
|
|
bar->hidden_state = strdup("hide");
|
|
|
|
} else if (strcasecmp("show", hidden_state) == 0) {
|
|
|
|
bar->hidden_state = strdup("show");
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "hidden_state", "Invalid value %s", hidden_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(old_state, bar->hidden_state) != 0) {
|
|
|
|
if (!config->reading) {
|
|
|
|
ipc_event_barconfig_update(bar);
|
|
|
|
}
|
|
|
|
sway_log(L_DEBUG, "Setting hidden_state: '%s' for bar: %s", bar->hidden_state, bar->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// free old mode
|
|
|
|
free(old_state);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "hidden_state", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = checkarg(argc, "hidden_state", EXPECTED_LESS_THAN, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->reading && argc > 1) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "hidden_state", "Unexpected value %s in config mode", argv[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *state = argv[0];
|
|
|
|
|
|
|
|
if (config->reading) {
|
|
|
|
return bar_set_hidden_state(config->current_bar, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *id = NULL;
|
|
|
|
if (argc == 2) {
|
|
|
|
id = argv[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
struct bar_config *bar;
|
|
|
|
for (i = 0; i < config->bars->length; ++i) {
|
|
|
|
bar = config->bars->items[i];
|
|
|
|
if (id && strcmp(id, bar->id) == 0) {
|
|
|
|
return bar_set_hidden_state(bar, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bar_set_hidden_state(bar, state);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode) {
|
|
|
|
char *old_mode = bar->mode;
|
|
|
|
if (strcasecmp("toggle", mode) == 0 && !config->reading) {
|
|
|
|
if (strcasecmp("dock", bar->mode) == 0) {
|
|
|
|
bar->mode = strdup("hide");
|
|
|
|
} else if (strcasecmp("hide", bar->mode) == 0) {
|
|
|
|
bar->mode = strdup("dock");
|
|
|
|
}
|
|
|
|
} else if (strcasecmp("dock", mode) == 0) {
|
|
|
|
bar->mode = strdup("dock");
|
|
|
|
} else if (strcasecmp("hide", mode) == 0) {
|
|
|
|
bar->mode = strdup("hide");
|
|
|
|
} else if (strcasecmp("invisible", mode) == 0) {
|
|
|
|
bar->mode = strdup("invisible");
|
|
|
|
} else {
|
|
|
|
return cmd_results_new(CMD_INVALID, "mode", "Invalid value %s", mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(old_mode, bar->mode) != 0) {
|
|
|
|
if (!config->reading) {
|
|
|
|
ipc_event_barconfig_update(bar);
|
|
|
|
}
|
|
|
|
sway_log(L_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// free old mode
|
|
|
|
free(old_mode);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_mode(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "mode", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
if ((error = checkarg(argc, "mode", EXPECTED_LESS_THAN, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config->reading && argc > 1) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "mode", "Unexpected value %s in config mode", argv[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *mode = argv[0];
|
|
|
|
|
|
|
|
if (config->reading) {
|
|
|
|
return bar_set_mode(config->current_bar, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *id = NULL;
|
|
|
|
if (argc == 2) {
|
|
|
|
id = argv[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
struct bar_config *bar;
|
|
|
|
for (i = 0; i < config->bars->length; ++i) {
|
|
|
|
bar = config->bars->items[i];
|
|
|
|
if (id && strcmp(id, bar->id) == 0) {
|
|
|
|
return bar_set_mode(bar, mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bar_set_mode(bar, mode);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_id(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "id", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *name = argv[0];
|
|
|
|
const char *oldname = config->current_bar->id;
|
|
|
|
|
|
|
|
// check if id is used by a previously defined bar
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < config->bars->length; ++i) {
|
|
|
|
struct bar_config *find = config->bars->items[i];
|
|
|
|
if (strcmp(name, find->id) == 0 && config->current_bar != find) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "id",
|
|
|
|
"Id '%s' already defined for another bar. Id unchanged (%s).",
|
|
|
|
name, oldname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "Renaming bar: '%s' to '%s'", oldname, name);
|
|
|
|
|
|
|
|
// free old bar id
|
|
|
|
free(config->current_bar->id);
|
|
|
|
|
|
|
|
config->current_bar->id = strdup(name);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_modifier(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "modifier", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "modifier", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t mod = 0;
|
|
|
|
|
|
|
|
list_t *split = split_string(argv[0], "+");
|
|
|
|
for (int i = 0; i < split->length; ++i) {
|
|
|
|
int j;
|
|
|
|
bool is_mod = false;
|
|
|
|
for (j = 0; j < (int)(sizeof(modifiers) / sizeof(struct modifier_key)); ++j) {
|
|
|
|
if (strcasecmp(modifiers[j].name, split->items[i]) == 0) {
|
|
|
|
mod |= modifiers[j].mod;
|
|
|
|
is_mod = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!is_mod) {
|
|
|
|
free_flat_list(split);
|
|
|
|
return cmd_results_new(CMD_INVALID, "modifier", "Unknown modifier '%s'", split->items[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_flat_list(split);
|
|
|
|
|
|
|
|
config->current_bar->modifier = mod;
|
|
|
|
sway_log(L_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_output(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "output", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "output", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *output = argv[0];
|
|
|
|
list_t *outputs = config->current_bar->outputs;
|
|
|
|
if (!outputs) {
|
|
|
|
outputs = create_list();
|
|
|
|
config->current_bar->outputs = outputs;
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
int add_output = 1;
|
|
|
|
if (strcmp("*", output) == 0) {
|
|
|
|
// remove all previous defined outputs and replace with '*'
|
|
|
|
for (i = 0; i < outputs->length; ++i) {
|
|
|
|
free(outputs->items[i]);
|
|
|
|
list_del(outputs, i);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// only add output if not already defined with either the same
|
|
|
|
// name or as '*'
|
|
|
|
for (i = 0; i < outputs->length; ++i) {
|
|
|
|
const char *find = outputs->items[i];
|
|
|
|
if (strcmp("*", find) == 0 || strcmp(output, find) == 0) {
|
|
|
|
add_output = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (add_output) {
|
|
|
|
list_add(outputs, strdup(output));
|
|
|
|
sway_log(L_DEBUG, "Adding bar: '%s' to output '%s'", config->current_bar->id, output);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_position(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "position", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "position", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp("top", argv[0]) == 0) {
|
|
|
|
config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_TOP;
|
|
|
|
} else if (strcasecmp("bottom", argv[0]) == 0) {
|
|
|
|
config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_BOTTOM;
|
|
|
|
} else if (strcasecmp("left", argv[0]) == 0) {
|
|
|
|
config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_LEFT;
|
|
|
|
} else if (strcasecmp("right", argv[0]) == 0) {
|
|
|
|
config->current_bar->position = DESKTOP_SHELL_PANEL_POSITION_RIGHT;
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "position", "Invalid value %s", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
sway_log(L_DEBUG, "Setting bar position '%s' for bar: %s", argv[0], config->current_bar->id);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_separator_symbol(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "separator_symbol", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "separator_symbol", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
free(config->current_bar->separator_symbol);
|
|
|
|
config->current_bar->separator_symbol = strdup(argv[0]);
|
|
|
|
sway_log(L_DEBUG, "Settings separator_symbol '%s' for bar: %s", config->current_bar->separator_symbol, config->current_bar->id);
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "status_command", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "status_command", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
free(config->current_bar->status_command);
|
|
|
|
config->current_bar->status_command = join_args(argv, argc);
|
|
|
|
sway_log(L_DEBUG, "Feeding bar with status command: %s", config->current_bar->status_command);
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_strip_workspace_numbers(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "strip_workspace_numbers", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "strip_workspace_numbers", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp("yes", argv[0]) == 0) {
|
|
|
|
config->current_bar->strip_workspace_numbers = true;
|
|
|
|
sway_log(L_DEBUG, "Stripping workspace numbers on bar: %s", config->current_bar->id);
|
|
|
|
} else if (strcasecmp("no", argv[0]) == 0) {
|
|
|
|
config->current_bar->strip_workspace_numbers = false;
|
|
|
|
sway_log(L_DEBUG, "Enabling workspace numbers on bar: %s", config->current_bar->id);
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "strip_workspace_numbers", "Invalid value %s", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_swaybar_command(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "swaybar_command", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "swaybar_command", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
free(config->current_bar->swaybar_command);
|
|
|
|
config->current_bar->swaybar_command = join_args(argv, argc);
|
|
|
|
sway_log(L_DEBUG, "Using custom swaybar command: %s", config->current_bar->swaybar_command);
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
|
|
|
|
sway_log(L_ERROR, "warning: tray_output is not supported on wayland");
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_tray_padding(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "tray_padding", EXPECTED_AT_LEAST, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "tray_padding", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
int padding = atoi(argv[0]);
|
|
|
|
if (padding < 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "tray_padding",
|
|
|
|
"Invalid padding value %s, minimum is 0", argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc > 1 && strcasecmp("px", argv[1]) != 0) {
|
|
|
|
return cmd_results_new(CMD_INVALID, "tray_padding",
|
|
|
|
"Unknown unit %s", argv[1]);
|
|
|
|
}
|
|
|
|
config->current_bar->tray_padding = padding;
|
|
|
|
sway_log(L_DEBUG, "Enabling tray padding of %d px on bar: %s", padding, config->current_bar->id);
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "workspace_buttons", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!config->current_bar) {
|
|
|
|
return cmd_results_new(CMD_FAILURE, "workspace_buttons", "No bar defined.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcasecmp("yes", argv[0]) == 0) {
|
|
|
|
config->current_bar->workspace_buttons = true;
|
|
|
|
sway_log(L_DEBUG, "Enabling workspace buttons on bar: %s", config->current_bar->id);
|
|
|
|
} else if (strcasecmp("no", argv[0]) == 0) {
|
|
|
|
config->current_bar->workspace_buttons = false;
|
|
|
|
sway_log(L_DEBUG, "Disabling workspace buttons on bar: %s", config->current_bar->id);
|
|
|
|
} else {
|
|
|
|
error = cmd_results_new(CMD_INVALID, "workspace_buttons", "Invalid value %s", argv[0]);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_handler bar_handlers[] = {
|
|
|
|
{ "binding_mode_indicator", bar_cmd_binding_mode_indicator },
|
|
|
|
{ "bindsym", bar_cmd_bindsym },
|
|
|
|
{ "colors", bar_cmd_colors },
|
|
|
|
{ "font", bar_cmd_font },
|
|
|
|
{ "height", bar_cmd_height },
|
|
|
|
{ "hidden_state", bar_cmd_hidden_state },
|
|
|
|
{ "id", bar_cmd_id },
|
|
|
|
{ "mode", bar_cmd_mode },
|
|
|
|
{ "modifier", bar_cmd_modifier },
|
|
|
|
{ "output", bar_cmd_output },
|
|
|
|
{ "position", bar_cmd_position },
|
|
|
|
{ "separator_symbol", bar_cmd_separator_symbol },
|
|
|
|
{ "status_command", bar_cmd_status_command },
|
|
|
|
{ "strip_workspace_numbers", bar_cmd_strip_workspace_numbers },
|
|
|
|
{ "swaybar_command", bar_cmd_swaybar_command },
|
|
|
|
{ "tray_output", bar_cmd_tray_output },
|
|
|
|
{ "tray_padding", bar_cmd_tray_padding },
|
|
|
|
{ "workspace_buttons", bar_cmd_workspace_buttons },
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check and add color to buffer.
|
|
|
|
*
|
|
|
|
* return error object, or NULL if color is valid.
|
|
|
|
*/
|
|
|
|
static struct cmd_results *add_color(const char *name, char *buffer, const char *color) {
|
|
|
|
int len = strlen(color);
|
|
|
|
if (len != 7 && len != 9 ) {
|
|
|
|
return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (color[0] != '#') {
|
|
|
|
return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
|
|
|
|
}
|
|
|
|
|
|
|
|
int i;
|
|
|
|
for (i = 1; i < len; ++i) {
|
|
|
|
if (!isxdigit(color[i])) {
|
|
|
|
return cmd_results_new(CMD_INVALID, name, "Invalid color definition %s", color);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// copy color to buffer
|
|
|
|
strncpy(buffer, color, len);
|
|
|
|
// add default alpha channel if color was defined without it
|
|
|
|
if (len == 7) {
|
|
|
|
buffer[7] = 'f';
|
|
|
|
buffer[8] = 'f';
|
|
|
|
}
|
|
|
|
sway_log(L_DEBUG, "Setting %s color %s for bar: %s", name, buffer, config->current_bar->id);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_active_workspace(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "active_workspace", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("active_workspace_border", config->current_bar->colors.active_workspace_border, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("active_workspace_bg", config->current_bar->colors.active_workspace_bg, argv[1]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("active_workspace_text", config->current_bar->colors.active_workspace_text, argv[2]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_background(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "background", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("background", config->current_bar->colors.background, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_binding_mode(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "binding_mode", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("binding_mode_border", config->current_bar->colors.binding_mode_border, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("binding_mode_bg", config->current_bar->colors.binding_mode_bg, argv[1]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("binding_mode_text", config->current_bar->colors.binding_mode_text, argv[2]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_focused_workspace(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "focused_workspace", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("focused_workspace_border", config->current_bar->colors.focused_workspace_border, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("focused_workspace_bg", config->current_bar->colors.focused_workspace_bg, argv[1]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("focused_workspace_text", config->current_bar->colors.focused_workspace_text, argv[2]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_inactive_workspace(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "inactive_workspace", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("inactive_workspace_border", config->current_bar->colors.inactive_workspace_border, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("inactive_workspace_bg", config->current_bar->colors.inactive_workspace_bg, argv[1]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("inactive_workspace_text", config->current_bar->colors.inactive_workspace_text, argv[2]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_separator(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "separator", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("separator", config->current_bar->colors.separator, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_statusline(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "statusline", EXPECTED_EQUAL_TO, 1))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("statusline", config->current_bar->colors.statusline, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_results *bar_colors_cmd_urgent_workspace(int argc, char **argv) {
|
|
|
|
struct cmd_results *error = NULL;
|
|
|
|
if ((error = checkarg(argc, "urgent_workspace", EXPECTED_EQUAL_TO, 3))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("urgent_workspace_border", config->current_bar->colors.urgent_workspace_border, argv[0]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("urgent_workspace_bg", config->current_bar->colors.urgent_workspace_bg, argv[1]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = add_color("urgent_workspace_text", config->current_bar->colors.urgent_workspace_text, argv[2]))) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_handler bar_colors_handlers[] = {
|
|
|
|
{ "active_workspace", bar_colors_cmd_active_workspace },
|
|
|
|
{ "background", bar_colors_cmd_background },
|
|
|
|
{ "binding_mode", bar_colors_cmd_binding_mode },
|
|
|
|
{ "focused_workspace", bar_colors_cmd_focused_workspace },
|
|
|
|
{ "inactive_workspace", bar_colors_cmd_inactive_workspace },
|
|
|
|
{ "separator", bar_colors_cmd_separator },
|
|
|
|
{ "statusline", bar_colors_cmd_statusline },
|
|
|
|
{ "urgent_workspace", bar_colors_cmd_urgent_workspace },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int handler_compare(const void *_a, const void *_b) {
|
|
|
|
const struct cmd_handler *a = _a;
|
|
|
|
const struct cmd_handler *b = _b;
|
|
|
|
return strcasecmp(a->command, b->command);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct cmd_handler *find_handler(char *line, enum cmd_status block) {
|
|
|
|
struct cmd_handler d = { .command=line };
|
|
|
|
struct cmd_handler *res = NULL;
|
|
|
|
if (block == CMD_BLOCK_BAR) {
|
|
|
|
res = bsearch(&d, bar_handlers,
|
|
|
|
sizeof(bar_handlers) / sizeof(struct cmd_handler),
|
|
|
|
sizeof(struct cmd_handler), handler_compare);
|
|
|
|
} else if (block == CMD_BLOCK_BAR_COLORS){
|
|
|
|
res = bsearch(&d, bar_colors_handlers,
|
|
|
|
sizeof(bar_colors_handlers) / sizeof(struct cmd_handler),
|
|
|
|
sizeof(struct cmd_handler), handler_compare);
|
|
|
|
} else {
|
|
|
|
res = bsearch(&d, handlers,
|
|
|
|
sizeof(handlers) / sizeof(struct cmd_handler),
|
|
|
|
sizeof(struct cmd_handler), handler_compare);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cmd_results *handle_command(char *_exec) {
|
|
|
|
// Even though this function will process multiple commands we will only
|
|
|
|
// return the last error, if any (for now). (Since we have access to an
|
|
|
|
// error string we could e.g. concatonate all errors there.)
|
|
|
|
struct cmd_results *results = NULL;
|
|
|
|
char *exec = strdup(_exec);
|
|
|
|
char *head = exec;
|
|
|
|
char *cmdlist;
|
|
|
|
char *cmd;
|
|
|
|
char *criteria __attribute__((unused));
|
|
|
|
|
|
|
|
head = exec;
|
|
|
|
do {
|
|
|
|
// Extract criteria (valid for this command list only).
|
|
|
|
criteria = NULL;
|
|
|
|
if (*head == '[') {
|
|
|
|
++head;
|
|
|
|
criteria = argsep(&head, "]");
|
|
|
|
if (head) {
|
|
|
|
++head;
|
|
|
|
// TODO handle criteria
|
|
|
|
} else {
|
|
|
|
if (!results) {
|
|
|
|
results = cmd_results_new(CMD_INVALID, criteria, "Unmatched [");
|
|
|
|
}
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
// Skip leading whitespace
|
|
|
|
head += strspn(head, whitespace);
|
|
|
|
|
|
|
|
// TODO: it will yield unexpected results to execute commands
|
|
|
|
// (on any view) that where meant for certain views only.
|
|
|
|
if (!results) {
|
|
|
|
int len = strlen(criteria) + strlen(head) + 4;
|
|
|
|
char *tmp = malloc(len);
|
|
|
|
snprintf(tmp, len, "[%s] %s", criteria, head);
|
|
|
|
results = cmd_results_new(CMD_INVALID, tmp,
|
|
|
|
"Can't handle criteria string: Refusing to execute command");
|
|
|
|
free(tmp);
|
|
|
|
}
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
// Split command list
|
|
|
|
cmdlist = argsep(&head, ";");
|
|
|
|
cmdlist += strspn(cmdlist, whitespace);
|
|
|
|
do {
|
|
|
|
// Split commands
|
|
|
|
cmd = argsep(&cmdlist, ",");
|
|
|
|
cmd += strspn(cmd, whitespace);
|
|
|
|
if (strcmp(cmd, "") == 0) {
|
|
|
|
sway_log(L_INFO, "Ignoring empty command.");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sway_log(L_INFO, "Handling command '%s'", cmd);
|
|
|
|
//TODO better handling of argv
|
|
|
|
int argc;
|
|
|
|
char **argv = split_args(cmd, &argc);
|
|
|
|
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
|
|
|
|
strip_quotes(argv[1]);
|
|
|
|
}
|
|
|
|
struct cmd_handler *handler = find_handler(argv[0], CMD_BLOCK_END);
|
|
|
|
if (!handler) {
|
|
|
|
if (results) {
|
|
|
|
free_cmd_results(results);
|
|
|
|
}
|
|
|
|
results = cmd_results_new(CMD_INVALID, cmd, "Unknown/invalid command");
|
|
|
|
free_argv(argc, argv);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
struct cmd_results *res = handler->handle(argc-1, argv+1);
|
|
|
|
if (res->status != CMD_SUCCESS) {
|
|
|
|
free_argv(argc, argv);
|
|
|
|
if (results) {
|
|
|
|
free_cmd_results(results);
|
|
|
|
}
|
|
|
|
results = res;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
free_argv(argc, argv);
|
|
|
|
free_cmd_results(res);
|
|
|
|
} while(cmdlist);
|
|
|
|
} while(head);
|
|
|
|
cleanup:
|
|
|
|
free(exec);
|
|
|
|
if (!results) {
|
|
|
|
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is like handle_command above, except:
|
|
|
|
// 1) it ignores empty commands (empty lines)
|
|
|
|
// 2) it does variable substitution
|
|
|
|
// 3) it doesn't split commands (because the multiple commands are supposed to
|
|
|
|
// be chained together)
|
|
|
|
// 4) handle_command handles all state internally while config_command has some
|
|
|
|
// state handled outside (notably the block mode, in read_config)
|
|
|
|
struct cmd_results *config_command(char *exec, enum cmd_status block) {
|
|
|
|
struct cmd_results *results = NULL;
|
|
|
|
int argc;
|
|
|
|
char **argv = split_args(exec, &argc);
|
|
|
|
if (!argc) {
|
|
|
|
results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
sway_log(L_INFO, "handling config command '%s'", exec);
|
|
|
|
// Endblock
|
|
|
|
if (**argv == '}') {
|
|
|
|
results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
struct cmd_handler *handler = find_handler(argv[0], block);
|
|
|
|
if (!handler) {
|
|
|
|
char *input = argv[0] ? argv[0] : "(empty)";
|
|
|
|
results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command");
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
int i;
|
|
|
|
// Var replacement, for all but first argument of set
|
|
|
|
for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
|
|
|
|
argv[i] = do_var_replacement(argv[i]);
|
|
|
|
}
|
|
|
|
/* Strip quotes for first argument.
|
|
|
|
* TODO This part needs to be handled much better */
|
|
|
|
if (argc>1 && (*argv[1] == '\"' || *argv[1] == '\'')) {
|
|
|
|
strip_quotes(argv[1]);
|
|
|
|
}
|
|
|
|
if (handler->handle) {
|
|
|
|
results = handler->handle(argc-1, argv+1);
|
|
|
|
} else {
|
|
|
|
results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
|
|
|
|
}
|
|
|
|
cleanup:
|
|
|
|
free_argv(argc, argv);
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cmd_results *cmd_results_new(enum cmd_status status, const char* input, const char *format, ...) {
|
|
|
|
struct cmd_results *results = malloc(sizeof(struct cmd_results));
|
|
|
|
results->status = status;
|
|
|
|
if (input) {
|
|
|
|
results->input = strdup(input); // input is the command name
|
|
|
|
} else {
|
|
|
|
results->input = NULL;
|
|
|
|
}
|
|
|
|
if (format) {
|
|
|
|
char *error = malloc(256);
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
vsnprintf(error, 256, format, args);
|
|
|
|
va_end(args);
|
|
|
|
results->error = error;
|
|
|
|
} else {
|
|
|
|
results->error = NULL;
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
void free_cmd_results(struct cmd_results *results) {
|
|
|
|
if (results->input) {
|
|
|
|
free(results->input);
|
|
|
|
}
|
|
|
|
if (results->error) {
|
|
|
|
free(results->error);
|
|
|
|
}
|
|
|
|
free(results);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *cmd_results_to_json(struct cmd_results *results) {
|
|
|
|
json_object *root = json_object_new_object();
|
|
|
|
json_object_object_add(root, "success", json_object_new_boolean(results->status == CMD_SUCCESS));
|
|
|
|
if (results->input) {
|
|
|
|
json_object_object_add(root, "input", json_object_new_string(results->input));
|
|
|
|
}
|
|
|
|
if (results->error) {
|
|
|
|
json_object_object_add(root, "error", json_object_new_string(results->error));
|
|
|
|
}
|
|
|
|
const char *json = json_object_to_json_string(root);
|
|
|
|
free(root);
|
|
|
|
return json;
|
|
|
|
}
|