752 lines
22 KiB
C
752 lines
22 KiB
C
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include "config.h"
|
|
#include "wm.h"
|
|
|
|
static const KeybindFunctionCommand __wm_keybind_commands[] = {
|
|
(KeybindFunctionCommand) {
|
|
.command = "spawn",
|
|
.function = &wm_kb_spawn
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "focus",
|
|
.function = &wm_kb_focus_dir
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "move",
|
|
.function = &wm_kb_move_dir
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "switch_ws",
|
|
.function = &wm_kb_switch_ws
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "move_to_ws",
|
|
.function = &wm_kb_move_client_ws
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "quit",
|
|
.function = &wm_kb_exit
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "quit_client",
|
|
.function = &wm_kb_kill
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "split_vertical",
|
|
.function = &wm_kb_switch_split_mode
|
|
},
|
|
(KeybindFunctionCommand) {
|
|
.command = "split_horizontal",
|
|
.function = &wm_kb_switch_split_mode
|
|
},
|
|
};
|
|
|
|
void wm_cfg_init_def(Config *config)
|
|
{
|
|
config->ms_p = 0.5;
|
|
strcpy(config->border_col, "#222222");
|
|
strcpy(config->focused_border_col, "#444444");
|
|
config->border_width = 2;
|
|
config->focus_on_motion = true;
|
|
config->show_titlebar = true;
|
|
strcpy(config->titlebar_color, "#000000");
|
|
strcpy(config->titlebar_focused_color, "#4169e1");
|
|
strcpy(config->titlebar_font, "monospace:size=18:antialias=true");
|
|
strcpy(config->titlebar_text_color, "#ffffff");
|
|
|
|
wm_keybinds_init_def(config);
|
|
|
|
char log_file_dir[PATH_MAX] = {0};
|
|
const char* log_file_name = "wm.json";
|
|
char *xdg_data_home = getenv("XDG_DATA_HOME");
|
|
|
|
if (!xdg_data_home) {
|
|
char *home = getenv("HOME");
|
|
assert(home);
|
|
snprintf(log_file_dir, sizeof(log_file_dir), "%s/.local/share", home);
|
|
} else {
|
|
strncpy(log_file_dir, xdg_data_home, PATH_MAX);
|
|
}
|
|
|
|
snprintf(config->log_path, PATH_MAX, "%s/%s", log_file_dir, log_file_name);
|
|
}
|
|
|
|
void wm_keybinds_init_def(Config *config)
|
|
{
|
|
char *st[] = {"st", NULL};
|
|
char **sth = malloc(sizeof(st));
|
|
assert(sth);
|
|
memcpy(sth, st, sizeof(st));
|
|
|
|
char *dmenu[] = {"i3-dmenu-desktop", NULL};
|
|
char **dmenuh = malloc(sizeof(dmenu));
|
|
assert(dmenuh);
|
|
memcpy(dmenuh, dmenu, sizeof(dmenu));
|
|
int size;
|
|
|
|
Keybind k[] = {
|
|
(Keybind) {Mod4Mask, XK_Return, *wm_kb_spawn,
|
|
(Arg) {.sl = sth, .count = 2}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_q, *wm_kb_exit,
|
|
(Arg) {0}},
|
|
|
|
(Keybind) {Mod4Mask, XK_d, *wm_kb_spawn,
|
|
(Arg) {.sl = dmenuh, .count = 2}},
|
|
|
|
(Keybind) {Mod4Mask, XK_c, *wm_kb_kill,
|
|
(Arg) {.c = NULL}},
|
|
|
|
(Keybind) {Mod4Mask, XK_1, *wm_kb_switch_ws,
|
|
(Arg) {.i = 0}},
|
|
|
|
(Keybind) {Mod4Mask, XK_2, *wm_kb_switch_ws,
|
|
(Arg) {.i = 1}},
|
|
|
|
(Keybind) {Mod4Mask, XK_3, *wm_kb_switch_ws,
|
|
(Arg) {.i = 2}},
|
|
|
|
(Keybind) {Mod4Mask, XK_4, *wm_kb_switch_ws,
|
|
(Arg) {.i = 3}},
|
|
|
|
(Keybind) {Mod4Mask, XK_5, *wm_kb_switch_ws,
|
|
(Arg) {.i = 4}},
|
|
|
|
(Keybind) {Mod4Mask, XK_6, *wm_kb_switch_ws,
|
|
(Arg) {.i = 5}},
|
|
|
|
(Keybind) {Mod4Mask, XK_7, *wm_kb_switch_ws,
|
|
(Arg) {.i = 6}},
|
|
|
|
(Keybind) {Mod4Mask, XK_8, *wm_kb_switch_ws,
|
|
(Arg) {.i = 7}},
|
|
|
|
(Keybind) {Mod4Mask, XK_9, *wm_kb_switch_ws,
|
|
(Arg) {.i = 8}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_1, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 0}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_2, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 1}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_3, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 2}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_4, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 3}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_5, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 4}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_6, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 5}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_7, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 6}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_8, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 7}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_9, *wm_kb_move_client_ws,
|
|
(Arg) {.i = 8}},
|
|
|
|
(Keybind) {Mod4Mask, XK_h, *wm_kb_focus_dir,
|
|
(Arg) {.i = LEFT}},
|
|
|
|
(Keybind) {Mod4Mask, XK_j, *wm_kb_focus_dir,
|
|
(Arg) {.i = DOWN}},
|
|
|
|
(Keybind) {Mod4Mask, XK_k, *wm_kb_focus_dir,
|
|
(Arg) {.i = UP}},
|
|
|
|
(Keybind) {Mod4Mask, XK_l, *wm_kb_focus_dir,
|
|
(Arg) {.i = RIGHT}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_h, *wm_kb_move_dir,
|
|
(Arg) {.i = LEFT}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_j, *wm_kb_move_dir,
|
|
(Arg) {.i = DOWN}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_k, *wm_kb_move_dir,
|
|
(Arg) {.i = UP}},
|
|
|
|
(Keybind) {Mod4Mask | ShiftMask, XK_l, *wm_kb_move_dir,
|
|
(Arg) {.i = RIGHT}},
|
|
|
|
(Keybind) {Mod4Mask, XK_b, *wm_kb_switch_split_mode,
|
|
(Arg) {.i = SPLIT_VERTICAL}},
|
|
|
|
(Keybind) {Mod4Mask, XK_v, *wm_kb_switch_split_mode,
|
|
(Arg) {.i = SPLIT_HORIZ}},
|
|
|
|
(Keybind) {Mod4Mask, XK_t, *wm_kb_switch_split_mode,
|
|
(Arg) {.i = SPLIT_TAB}},
|
|
};
|
|
|
|
|
|
size = sizeof(k);
|
|
config->kb_count = size / sizeof(Keybind);
|
|
DEBUG_PRINT("sizeof k: %d\n", size);
|
|
config->keybinds = malloc(size);
|
|
assert(config->keybinds);
|
|
memcpy(config->keybinds, k, size);
|
|
}
|
|
|
|
void wm_keybinds_free(Config *config)
|
|
{
|
|
for (size_t i = 0; i < config->kb_count; i++) {
|
|
if (config->keybinds[i].function == &wm_kb_spawn)
|
|
free(config->keybinds[i].args.sl);
|
|
}
|
|
}
|
|
|
|
void wm_configfile_init(ConfigFile *config, const char *filename)
|
|
{
|
|
assert(config);
|
|
assert(filename);
|
|
|
|
strncpy(config->file_path, filename, PATH_MAX);
|
|
|
|
ConfigCommand commands[] = {
|
|
(ConfigCommand) {.string = "border_color", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, border_col),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
|
|
(ConfigCommand) {.string = "focused_border_color", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, focused_border_col),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
|
|
(ConfigCommand) {.string = "border_width", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, border_width),
|
|
.argc = 0, .arg_type = TYPE_UNSIGNED_INTEGER},
|
|
|
|
(ConfigCommand) {.string = "keybind", .arg_count = 0,
|
|
.cfg_var_offset[0] = -1,
|
|
.argc = 0, .arg_type = TYPE_KEYBIND},
|
|
|
|
(ConfigCommand) {.string = "focus_on_motion", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, focus_on_motion),
|
|
.argc = 0, .arg_type = TYPE_BOOL},
|
|
|
|
(ConfigCommand) {.string = "show_titlebar", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, show_titlebar),
|
|
.argc = 0, .arg_type = TYPE_BOOL},
|
|
|
|
(ConfigCommand) {.string = "titlebar_color", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, titlebar_color),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
|
|
(ConfigCommand) {.string = "titlebar_focused_color", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, titlebar_focused_color),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
|
|
(ConfigCommand) {.string = "titlebar_font", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, titlebar_font),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
|
|
(ConfigCommand) {.string = "titlebar_text_color", .arg_count = 1,
|
|
.cfg_var_offset[0] = offsetof(Config, titlebar_text_color),
|
|
.argc = 0, .arg_type = TYPE_STRING},
|
|
};
|
|
|
|
config->command_count = sizeof(commands) / sizeof (commands[0]);
|
|
DEBUG_PRINT("command_count: %ld\n", config->command_count);
|
|
for (size_t i = 0; i < config->command_count; i++) {
|
|
wm_configcommand_init(&commands[i]);
|
|
}
|
|
|
|
config->available_commands = calloc(config->command_count, sizeof(ConfigCommand));
|
|
assert(config->available_commands);
|
|
memcpy(config->available_commands, commands, sizeof(commands));
|
|
}
|
|
|
|
void wm_configfile_free(ConfigFile *config)
|
|
{
|
|
for (size_t i = 0; i < config->command_count; i++) {
|
|
wm_configcommand_free(&config->available_commands[i]);
|
|
}
|
|
|
|
free(config->available_commands);
|
|
}
|
|
|
|
void wm_configfile_read(ConfigFile *cfile, Config *config)
|
|
{
|
|
assert(config);
|
|
assert(cfile);
|
|
|
|
FILE *fp = fopen(cfile->file_path, "r");
|
|
|
|
if (!fp) {
|
|
fprintf(stderr, "wm: could not open config file: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
|
|
const int max_line_len = 8192;
|
|
char line[max_line_len];
|
|
size_t line_index = 0;
|
|
ConfigCommand command = {0};
|
|
wm_configcommand_init(&command);
|
|
|
|
while(fgets(line, max_line_len, fp) != NULL) {
|
|
line_index++;
|
|
|
|
ParserResult result = wm_configfile_parse_line(cfile, config, line, &command);
|
|
if (!result.success) {
|
|
fprintf(stderr, "wm: could not parse line %ld of config file %s: %s\n",
|
|
line_index, cfile->file_path, result.message);
|
|
continue;
|
|
}
|
|
|
|
wm_configcommand_argv_parse(&command);
|
|
wm_config_apply_command(config, &command);
|
|
}
|
|
|
|
fclose(fp);
|
|
wm_configcommand_free(&command);
|
|
}
|
|
|
|
void wm_config_apply_command(Config *config, ConfigCommand *command)
|
|
{
|
|
switch (command->arg_type) {
|
|
case TYPE_INTEGER:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
*(int*)(((char*)config) + (command->cfg_var_offset[i])) = command->args[i].i;
|
|
}
|
|
return;
|
|
case TYPE_UNSIGNED_INTEGER:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
*(unsigned int*)(((char*)config) + (command->cfg_var_offset[i])) = command->args[i].ui;
|
|
}
|
|
return;
|
|
case TYPE_BOOL:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
*(bool*)(((char*)config) + (command->cfg_var_offset[i])) = command->args[i].i;
|
|
}
|
|
return;
|
|
case TYPE_KEYBIND:
|
|
wm_config_replace_keybind(config, &command->keybind);
|
|
return;
|
|
case TYPE_STRING:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
size_t len = strnlen(command->argv[i], CONFIG_ARGLEN_MAX);
|
|
memcpy(((char*)(config) + (command->cfg_var_offset)[i]), command->argv[i], len);
|
|
}
|
|
return;
|
|
case TYPE_FLOAT:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
*(float*)(((char*)config) + (command->cfg_var_offset[i])) = atof(command->argv[i]);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
ParserResult wm_configfile_parse_line(const ConfigFile *cfile, Config *config,
|
|
char* line, ConfigCommand *command)
|
|
{
|
|
char *token = strtok(line, " ");
|
|
bool matched_command = false;
|
|
ParserResult result;
|
|
|
|
// comment
|
|
if (line[0] == '#')
|
|
return (ParserResult) {.success = true, .message = ""};
|
|
|
|
// empty line
|
|
if (line[0] == '\n')
|
|
return (ParserResult) {.success = true, .message = ""};
|
|
|
|
if (!token)
|
|
return (ParserResult) {.success = true, .message = ""};
|
|
|
|
// try to match command
|
|
for (size_t i = 0; i < cfile->command_count; i++) {
|
|
if (strncmp(cfile->available_commands[i].string, token,
|
|
CONFIG_COMMAND_MAX_LEN) == 0) {
|
|
wm_configcommand_copy(command, &cfile->available_commands[i]);
|
|
matched_command = true;
|
|
}
|
|
}
|
|
|
|
if (!matched_command) {
|
|
PARSER_RETURN(result, false, "Unknown config command: %s\n", token);
|
|
}
|
|
|
|
DEBUG_PRINT("matched command: %s\n", command->string);
|
|
|
|
// read arguments
|
|
while (true) {
|
|
token = strtok(NULL, " ");
|
|
DEBUG_PRINT("token: %s\n", token);
|
|
|
|
if (token != NULL) {
|
|
command->argc++;
|
|
|
|
if (strnlen(token, CONFIG_ARGLEN_MAX) == CONFIG_ARGLEN_MAX) {
|
|
PARSER_RETURN(result, false,
|
|
"Argument length exceeded maximum of %d characters\n",
|
|
CONFIG_ARGLEN_MAX);
|
|
}
|
|
|
|
if (command->argc >= CONFIG_ARGC_MAX) {
|
|
PARSER_RETURN(result, false,
|
|
"Maximum number of arguments (%d) exceeded\n",
|
|
CONFIG_ARGC_MAX);
|
|
}
|
|
|
|
if (command->argc > command->arg_count && command->arg_type != TYPE_KEYBIND) {
|
|
PARSER_RETURN(result, false, "Too many arguments to command %s\n",
|
|
command->string);
|
|
}
|
|
|
|
strncpy(command->argv[command->argc-1], token, CONFIG_ARGLEN_MAX);
|
|
char *arg = command->argv[command->argc-1];
|
|
for (size_t i = 0; i < strnlen(arg, CONFIG_ARGLEN_MAX); i++)
|
|
if (arg[i] == '\n') arg[i] = '\0';
|
|
}
|
|
|
|
// end of args
|
|
if (token == NULL) {
|
|
if (command->argc < command->arg_count) {
|
|
PARSER_RETURN(result, false, "Missing arguments from command %s\n",
|
|
command->string);
|
|
}
|
|
|
|
break;
|
|
}
|
|
};
|
|
|
|
return (ParserResult) {.success = true, .message = ""};
|
|
}
|
|
|
|
void wm_configcommand_init(ConfigCommand *command)
|
|
{
|
|
assert(command);
|
|
|
|
command->argv = calloc(CONFIG_ARGC_MAX, sizeof(void*));
|
|
command->args = calloc(CONFIG_ARGC_MAX, sizeof(Arg));
|
|
assert(command->argv);
|
|
assert(command->args);
|
|
|
|
for (size_t i = 0; i < CONFIG_ARGC_MAX; i++) {
|
|
command->argv[i] = calloc(CONFIG_ARGLEN_MAX, 1);
|
|
command->args[i].s = calloc(CONFIG_ARGLEN_MAX, 1);
|
|
command->args[i].sl = calloc(CONFIG_ARGC_MAX, sizeof(char*));
|
|
assert(command->argv[i]);
|
|
assert(command->args[i].s);
|
|
assert(command->args[i].sl);
|
|
|
|
for (size_t j = 0; j < CONFIG_ARGC_MAX; j++) {
|
|
command->args[i].sl[j] = calloc(CONFIG_ARGLEN_MAX, 1);
|
|
assert(command->args[i].sl[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wm_configcommand_copy(ConfigCommand *dest, const ConfigCommand *src)
|
|
{
|
|
assert(dest);
|
|
assert(src);
|
|
|
|
memcpy(dest->string, src->string, CONFIG_COMMAND_MAX_LEN);
|
|
dest->arg_count = src->arg_count;
|
|
memcpy(dest->cfg_var_offset, src->cfg_var_offset, CONFIG_ARGC_MAX*sizeof(off_t));
|
|
dest->arg_type = src->arg_type;
|
|
dest->argc = src->argc;
|
|
|
|
for (size_t i = 0; i < CONFIG_ARGC_MAX; i++) {
|
|
memcpy(dest->argv[i], src->argv[i], CONFIG_ARGLEN_MAX);
|
|
memcpy(dest->args[i].s, src->args[i].s, CONFIG_ARGLEN_MAX);
|
|
|
|
for (size_t j = 0; j < CONFIG_ARGC_MAX; j++) {
|
|
memcpy(dest->args[i].sl[j], src->args[i].sl[j], CONFIG_ARGLEN_MAX);
|
|
}
|
|
}
|
|
}
|
|
|
|
void wm_configcommand_argv_parse(ConfigCommand *command)
|
|
{
|
|
switch (command->arg_type) {
|
|
case TYPE_INTEGER:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
command->args[i].i = atoi(command->argv[i]);
|
|
}
|
|
return;
|
|
case TYPE_UNSIGNED_INTEGER:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
command->args[i].ui = atoi(command->argv[i]);
|
|
}
|
|
return;
|
|
case TYPE_BOOL:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
command->args[i].i = atobool(command->argv[i]);
|
|
}
|
|
return;
|
|
case TYPE_KEYBIND:
|
|
command->keybind = wm_config_parse_bind_command(command);
|
|
return;
|
|
case TYPE_STRING:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
size_t len = strnlen(command->argv[i], CONFIG_ARGLEN_MAX);
|
|
command->args[i].count = len;
|
|
memcpy(command->args[i].s, command->argv[i], len);
|
|
}
|
|
return;
|
|
case TYPE_FLOAT:
|
|
for (size_t i = 0; i < command->argc; i++) {
|
|
command->args[i].f = atof(command->argv[i]);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
void wm_configcommand_free(ConfigCommand *command)
|
|
{
|
|
assert(command);
|
|
|
|
command->argc = 0;
|
|
|
|
for (size_t i = 0; i < CONFIG_ARGC_MAX; i++) {
|
|
free(command->argv[i]);
|
|
free(command->args[i].s);
|
|
|
|
for (size_t j = 0; j < CONFIG_ARGC_MAX; j++) {
|
|
free(command->args[i].sl[j]);
|
|
}
|
|
|
|
free(command->args[i].sl);
|
|
}
|
|
|
|
free(command->argv);
|
|
free(command->args);
|
|
}
|
|
|
|
bool atobool(char *arr)
|
|
{
|
|
for (size_t i = 0; i <strnlen(arr, CONFIG_ARGLEN_MAX); i++) {
|
|
arr[i] = tolower(arr[i]);
|
|
}
|
|
|
|
if ((strncmp(arr, "true", CONFIG_ARGLEN_MAX) == 0) ||
|
|
(strncmp(arr, "yes", CONFIG_ARGLEN_MAX) == 0))
|
|
return true;
|
|
|
|
if ((strncmp(arr, "false", CONFIG_ARGLEN_MAX) == 0) ||
|
|
(strncmp(arr, "no", CONFIG_ARGLEN_MAX) == 0))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
Arg wm_keybind_argv_to_args(Keybind kb, ConfigCommand command)
|
|
{
|
|
DEBUG_PRINT("%s\n", __func__);
|
|
Arg ret;
|
|
|
|
if (kb.function == &wm_kb_spawn) {
|
|
ret.sl = calloc(command.argc - 2, sizeof(char*));
|
|
for (size_t i = 2; i < command.argc; i++) {
|
|
ret.sl[i] = calloc(CONFIG_ARGLEN_MAX, 1);
|
|
strncpy(ret.sl[i], command.argv[i], CONFIG_ARGLEN_MAX);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (kb.function == &wm_kb_focus_dir) {
|
|
assert(command.argc == 3);
|
|
ret.i = wm_string_to_direction(command.argv[command.argc - 1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (kb.function == &wm_kb_move_dir) {
|
|
assert(command.argc == 3);
|
|
ret.i = wm_string_to_direction(command.argv[command.argc - 1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (kb.function == &wm_kb_switch_ws) {
|
|
assert(command.argc == 3);
|
|
ret.i = atoi(command.argv[command.argc - 1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (kb.function == &wm_kb_move_client_ws) {
|
|
assert(command.argc == 3);
|
|
ret.i = atoi(command.argv[command.argc - 1]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
if (kb.function == &wm_kb_switch_split_mode) {
|
|
// TODO
|
|
}
|
|
|
|
assert(false);
|
|
}
|
|
|
|
int wm_string_to_direction(const char *str)
|
|
{
|
|
int ret;
|
|
char lower[CONFIG_ARGLEN_MAX];
|
|
strncpy(lower, str, CONFIG_ARGLEN_MAX);
|
|
|
|
for (char *c = lower; *c; c++) *c = tolower(*c);
|
|
|
|
ret = strncmp(lower, "right", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return RIGHT;
|
|
|
|
ret = strncmp(lower, "left", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return LEFT;
|
|
|
|
ret = strncmp(lower, "up", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return UP;
|
|
|
|
ret = strncmp(lower, "down", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return DOWN;
|
|
|
|
fprintf(stderr, "wm: encountered invalid direction \"%s\"\n. exiting.",
|
|
str);
|
|
abort();
|
|
}
|
|
|
|
KeybindFunction wm_str_to_keybindfunction(const char* str)
|
|
{
|
|
DEBUG_PRINT("%s %s\n", __func__, str);
|
|
|
|
char lower[CONFIG_ARGLEN_MAX];
|
|
strncpy(lower, str, CONFIG_ARGLEN_MAX);
|
|
|
|
for (char *c = lower; *c; c++) *c = tolower(*c);
|
|
|
|
for (size_t i = 0; i < sizeof(__wm_keybind_commands) / sizeof(__wm_keybind_commands[0]); i++) {
|
|
if (strncmp(lower, __wm_keybind_commands[i].command, CONFIG_ARGLEN_MAX) == 0)
|
|
return __wm_keybind_commands[i].function;
|
|
}
|
|
|
|
fprintf(stderr, "wm: encountered invalid keybind command \"%s\"\n. exiting.",
|
|
str);
|
|
abort();
|
|
}
|
|
|
|
unsigned int wm_string_to_mask(const char* str)
|
|
{
|
|
int ret;
|
|
ret = strncmp(str, "Shift", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return ShiftMask;
|
|
|
|
ret = strncmp(str, "Control", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return ControlMask;
|
|
|
|
ret = strncmp(str, "Ctrl", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return ControlMask;
|
|
|
|
ret = strncmp(str, "Caps", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return LockMask;
|
|
|
|
ret = strncmp(str, "CapsLock", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return LockMask;
|
|
|
|
ret = strncmp(str, "Alt", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return Mod1Mask;
|
|
|
|
ret = strncmp(str, "Win", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return Mod4Mask;
|
|
|
|
ret = strncmp(str, "Windows", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return Mod4Mask;
|
|
|
|
ret = strncmp(str, "Super", CONFIG_ARGLEN_MAX);
|
|
if (ret == 0) return Mod4Mask;
|
|
|
|
fprintf(stderr, "wm: encountered invalid modifier token \"%s\"\n. exiting.",
|
|
str);
|
|
abort();
|
|
}
|
|
|
|
KeySym* wm_keybind_string_to_mask_keysym(const char* kb_argv)
|
|
{
|
|
KeySym *ret = calloc(2, sizeof(KeySym));
|
|
char* str = strdup(kb_argv);
|
|
char tokens[CONFIG_ARGC_MAX][CONFIG_ARGLEN_MAX];
|
|
|
|
char *token = strtok(str, "+");
|
|
|
|
long token_count = 0;
|
|
while(token != NULL) {
|
|
token_count++;
|
|
if (token_count >= CONFIG_ARGC_MAX) {
|
|
fprintf(stderr, "wm: maximum number of arguments (%d) exceeded."
|
|
"exiting.\n", CONFIG_ARGC_MAX);
|
|
abort();
|
|
}
|
|
|
|
strncpy(tokens[token_count - 1], token, CONFIG_ARGLEN_MAX);
|
|
|
|
token = strtok(NULL, "+");
|
|
}
|
|
|
|
DEBUG_PRINT("str: %s, str[0]: %d, token count: %d\n", kb_argv, kb_argv[0], token_count);
|
|
|
|
ret[0] = wm_string_to_mask(tokens[0]);
|
|
|
|
for (size_t i = 1; i < token_count - 1; i++) {
|
|
ret[0] |= wm_string_to_mask(tokens[i]);
|
|
}
|
|
|
|
KeySym keysym = XStringToKeysym(tokens[token_count - 1]);
|
|
if (keysym == NoSymbol) {
|
|
fprintf(stderr, "wm: encountered invalid keybind token \"%s\"\n. exiting.",
|
|
tokens[token_count-1]);
|
|
abort();
|
|
}
|
|
|
|
ret[1] = keysym;
|
|
|
|
free(str);
|
|
|
|
return ret;
|
|
}
|
|
|
|
Keybind wm_config_parse_bind_command(ConfigCommand *command)
|
|
{
|
|
DEBUG_PRINT("%s\n", __func__);
|
|
Keybind ret;
|
|
KeySym *keysyms = wm_keybind_string_to_mask_keysym(command->argv[0]);
|
|
|
|
ret.mask = keysyms[0];
|
|
ret.keysym = keysyms[1];
|
|
free(keysyms);
|
|
|
|
ret.function = wm_str_to_keybindfunction(command->argv[1]);
|
|
ret.args = wm_keybind_argv_to_args(ret, *command);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wm_config_replace_keybind(Config *config, const Keybind *keybind)
|
|
{
|
|
for (size_t i = 0; i < config->kb_count; i++)
|
|
if (config->keybinds[i].function == keybind->function) {
|
|
config->keybinds[i] = *keybind;
|
|
return;
|
|
}
|
|
}
|