wm/src/config.c

452 lines
13 KiB
C

#include "config.h"
#include "wm.h"
#include <assert.h>
#include <stdint.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>
void wm_cfg_init_def(Config *config)
{
config->ms_p = 0.5;
config->border_col = "#222222";
config->focused_border_col = "#444444";
config->border_width = 2;
config->focus_on_motion = true;
wm_keybinds_init_def(config);
}
void wm_keybinds_init_def(Config *config)
{
char *st[] = {"st", NULL};
char **sth = malloc(sizeof(st));
memcpy(sth, st, sizeof(st));
char *dmenu[] = {"i3-dmenu-desktop", NULL};
char **dmenuh = malloc(sizeof(dmenu));
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);
memcpy(config->keybinds, k, size);
}
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},
(ConfigCommand) {.string = "focused_border_color", .arg_count = 1,
.cfg_var_offset[0] = offsetof(Config, focused_border_col),
.argc = 0},
(ConfigCommand) {.string = "border_width", .arg_count = 1,
.cfg_var_offset[0] = offsetof(Config, border_width),
.argc = 0},
(ConfigCommand) {.string = "keybind", .arg_count = 0,
.cfg_var_offset[0] = -1,
.argc = 0},
(ConfigCommand) {.string = "focus_on_motion", .arg_count = 1,
.cfg_var_offset[0] = offsetof(Config, focus_on_motion),
.argc = 0},
};
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));
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]);
}
}
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:
// TODO
wm_config_parse_bind_command(command);
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 = ""};
// 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) {
PARSER_RETURN(result, false, "Too many arguments to command %s\n",
command->string);
}
strncpy(command->argv[command->argc-1], token, CONFIG_ARGLEN_MAX);
}
// 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));
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*));
for (size_t j = 0; j < CONFIG_ARGC_MAX; j++) {
command->args[i].sl[j] = calloc(CONFIG_ARGLEN_MAX, 1);
}
}
}
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:
// todo
wm_config_parse_bind_command(command);
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;
}
// TODO
Keybind wm_config_parse_bind_command(ConfigCommand *command)
{
}