maomaowm/parse_config.h

1220 lines
No EOL
41 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifndef SYSCONFDIR
#define SYSCONFDIR "/etc"
#endif
typedef struct {
const char *id;
const char *title;
unsigned int tags;
int isfloating;
int isfullscreen;
float scroller_proportion;
const char *animation_type;
int isnoborder;
int monitor;
int width;
int height;
} ConfigWinRule;
typedef struct {
const char *name; // 显示器名称
float mfact; // 主区域比例
int nmaster; // 主区域窗口数量
const char *layout; // 布局名称(字符串)
int rr; // 旋转和翻转(假设为整数)
float scale; // 显示器缩放比例
int x, y; // 显示器位置
} ConfigMonitorRule;
typedef struct {
uint32_t mod;
xkb_keysym_t keysym;
void (*func)(const Arg *);
Arg arg;
} KeyBinding;
// 定义一个宏来简化默认按键绑定的添加
#define CHVT(n) \
{ \
WLR_MODIFIER_CTRL | WLR_MODIFIER_ALT, XKB_KEY_XF86Switch_VT_##n, chvt, { \
.ui = (n) \
} \
}
// 默认的按键绑定数组
KeyBinding default_key_bindings[] = {CHVT(1), CHVT(2), CHVT(3), CHVT(4),
CHVT(5), CHVT(6), CHVT(7), CHVT(8),
CHVT(9), CHVT(10), CHVT(11), CHVT(12)};
typedef struct {
unsigned int mod;
unsigned int button;
void (*func)(const Arg *);
Arg arg;
} MouseBinding;
typedef struct {
unsigned int mod;
unsigned int dir;
void (*func)(const Arg *);
Arg arg;
} AxisBinding;
typedef struct {
int animations;
char animation_type[10];
char animation_fade_in;
float zoom_initial_ratio;
float fadein_begin_opacity;
float fadeout_begin_opacity;
uint32_t animation_duration_move;
uint32_t animation_duration_open;
uint32_t animation_duration_tag;
uint32_t animation_duration_close;
double animation_curve_move[4];
double animation_curve_open[4];
double animation_curve_tag[4];
int scroller_structs;
float scroller_default_proportion;
int scoller_focus_center;
float *scroller_proportion_preset;
int scroller_proportion_preset_count;
char **circle_layout;
int circle_layout_count;
unsigned int new_is_master;
float default_mfact;
unsigned int default_nmaster;
unsigned int hotarea_size;
unsigned int enable_hotarea;
unsigned int ov_tab_mode;
int overviewgappi;
int overviewgappo;
unsigned int axis_bind_apply_timeout;
unsigned int focus_on_activate;
int bypass_surface_visibility;
int sloppyfocus;
int warpcursor;
/* keyboard */
int repeat_rate;
int repeat_delay;
unsigned int numlockon;
/* Trackpad */
int tap_to_click;
int tap_and_drag;
int drag_lock;
int natural_scrolling;
int disable_while_typing;
int left_handed;
int middle_button_emulation;
int smartgaps;
unsigned int gappih;
unsigned int gappiv;
unsigned int gappoh;
unsigned int gappov;
unsigned int borderpx;
float rootcolor[4];
float bordercolor[4];
float focuscolor[4];
float maxmizescreencolor[4];
float urgentcolor[4];
float scratchpadcolor[4];
float globalcolor[4];
char autostart[3][256];
struct {
int id;
char layout_name[256];
} tags[9];
ConfigWinRule *window_rules;
int window_rules_count;
ConfigMonitorRule *monitor_rules; // 动态数组
int monitor_rules_count; // 条数
KeyBinding *key_bindings;
int key_bindings_count;
MouseBinding *mouse_bindings;
int mouse_bindings_count;
AxisBinding *axis_bindings;
int axis_bindings_count;
} Config;
typedef void (*FuncType)(const Arg *);
Config config;
// 清理字符串中的不可见字符(包括 \r, \n, 空格等)
char* sanitize_string(char *str) {
// 去除首部不可见字符
while (*str != '\0' && !isprint((unsigned char)*str)) str++;
// 去除尾部不可见字符
char *end = str + strlen(str) - 1;
while (end > str && !isprint((unsigned char)*end)) end--;
*(end + 1) = '\0';
return str;
}
int parse_circle_direction(const char *str) {
// 将输入字符串转换为小写
char lowerStr[10];
int i = 0;
while (str[i] && i < 9) {
lowerStr[i] = tolower(str[i]);
i++;
}
lowerStr[i] = '\0';
// 根据转换后的小写字符串返回对应的枚举值
if (strcmp(lowerStr, "next") == 0) {
return 1;
} else {
return -1;
}
}
int parse_direction(const char *str) {
// 将输入字符串转换为小写
char lowerStr[10];
int i = 0;
while (str[i] && i < 9) {
lowerStr[i] = tolower(str[i]);
i++;
}
lowerStr[i] = '\0';
// 根据转换后的小写字符串返回对应的枚举值
if (strcmp(lowerStr, "up") == 0) {
return UP;
} else if (strcmp(lowerStr, "down") == 0) {
return DOWN;
} else if (strcmp(lowerStr, "left") == 0) {
return LEFT;
} else if (strcmp(lowerStr, "right") == 0) {
return RIGHT;
} else {
return UNDIR;
}
}
long int parse_color(const char *hex_str) {
char *endptr;
long int hex_num = strtol(hex_str, &endptr, 16);
if (*endptr != '\0') {
return -1;
}
return hex_num;
}
// 辅助函数:检查字符串是否以指定的前缀开头(忽略大小写)
static bool starts_with_ignore_case(const char *str, const char *prefix) {
while (*prefix) {
if (tolower(*str) != tolower(*prefix)) {
return false;
}
str++;
prefix++;
}
return true;
}
uint32_t parse_mod(const char *mod_str) {
uint32_t mod = 0;
char lower_str[64]; // 假设输入的字符串长度不超过 64
int i = 0;
// 将 mod_str 转换为全小写
for (i = 0; mod_str[i] && i < sizeof(lower_str) - 1; i++) {
lower_str[i] = tolower(mod_str[i]);
}
lower_str[i] = '\0'; // 确保字符串以 NULL 结尾
// 检查修饰键,忽略左右键标识(如 "_l" 和 "_r"
if (strstr(lower_str, "super") || strstr(lower_str, "super_l") ||
strstr(lower_str, "super_r")) {
mod |= WLR_MODIFIER_LOGO;
}
if (strstr(lower_str, "ctrl") || strstr(lower_str, "ctrl_l") ||
strstr(lower_str, "ctrl_r")) {
mod |= WLR_MODIFIER_CTRL;
}
if (strstr(lower_str, "shift") || strstr(lower_str, "shift_l") ||
strstr(lower_str, "shift_r")) {
mod |= WLR_MODIFIER_SHIFT;
}
if (strstr(lower_str, "alt") || strstr(lower_str, "alt_l") ||
strstr(lower_str, "alt_r")) {
mod |= WLR_MODIFIER_ALT;
}
return mod;
}
xkb_keysym_t parse_keysym(const char *keysym_str) {
if (strcmp(keysym_str, "F1") == 0 || strcmp(keysym_str, "f1") == 0)
return XKB_KEY_XF86Switch_VT_1;
else if (strcmp(keysym_str, "F2") == 0 || strcmp(keysym_str, "f2") == 0)
return XKB_KEY_XF86Switch_VT_2;
else if (strcmp(keysym_str, "F3") == 0 || strcmp(keysym_str, "f3") == 0)
return XKB_KEY_XF86Switch_VT_3;
else if (strcmp(keysym_str, "F4") == 0 || strcmp(keysym_str, "f4") == 0)
return XKB_KEY_XF86Switch_VT_4;
else if (strcmp(keysym_str, "F5") == 0 || strcmp(keysym_str, "f5") == 0)
return XKB_KEY_XF86Switch_VT_5;
else if (strcmp(keysym_str, "F6") == 0 || strcmp(keysym_str, "f6") == 0)
return XKB_KEY_XF86Switch_VT_6;
else if (strcmp(keysym_str, "F7") == 0 || strcmp(keysym_str, "f7") == 0)
return XKB_KEY_XF86Switch_VT_7;
else if (strcmp(keysym_str, "F8") == 0 || strcmp(keysym_str, "f8") == 0)
return XKB_KEY_XF86Switch_VT_8;
else if (strcmp(keysym_str, "F9") == 0 || strcmp(keysym_str, "f9") == 0)
return XKB_KEY_XF86Switch_VT_9;
else if (strcmp(keysym_str, "F10") == 0 || strcmp(keysym_str, "f10") == 0)
return XKB_KEY_XF86Switch_VT_10;
else if (strcmp(keysym_str, "F11") == 0 || strcmp(keysym_str, "f11") == 0)
return XKB_KEY_XF86Switch_VT_11;
else if (strcmp(keysym_str, "F12") == 0 || strcmp(keysym_str, "f12") == 0)
return XKB_KEY_XF86Switch_VT_12;
return xkb_keysym_from_name(keysym_str, XKB_KEYSYM_NO_FLAGS);
}
int parse_button(const char *str) {
// 将输入字符串转换为小写
char lowerStr[20];
int i = 0;
while (str[i] && i < 19) {
lowerStr[i] = tolower(str[i]);
i++;
}
lowerStr[i] = '\0'; // 确保字符串正确终止
// 根据转换后的小写字符串返回对应的按钮编号
if (strcmp(lowerStr, "btn_left") == 0) {
return BTN_LEFT;
} else if (strcmp(lowerStr, "btn_right") == 0) {
return BTN_RIGHT;
} else if (strcmp(lowerStr, "btn_middle") == 0) {
return BTN_MIDDLE;
} else if (strcmp(lowerStr, "btn_side") == 0) {
return BTN_SIDE;
} else if (strcmp(lowerStr, "btn_extra") == 0) {
return BTN_EXTRA;
} else if (strcmp(lowerStr, "btn_forward") == 0) {
return BTN_FORWARD;
} else if (strcmp(lowerStr, "btn_back") == 0) {
return BTN_BACK;
} else if (strcmp(lowerStr, "btn_task") == 0) {
return BTN_TASK;
} else {
return 0;
}
}
int parse_mouse_action(const char *str) {
// 将输入字符串转换为小写
char lowerStr[20];
int i = 0;
while (str[i] && i < 19) {
lowerStr[i] = tolower(str[i]);
i++;
}
lowerStr[i] = '\0'; // 确保字符串正确终止
// 根据转换后的小写字符串返回对应的按钮编号
if (strcmp(lowerStr, "curmove") == 0) {
return CurMove;
} else if (strcmp(lowerStr, "curresize") == 0) {
return CurResize;
} else if (strcmp(lowerStr, "curnormal") == 0) {
return CurNormal;
} else if (strcmp(lowerStr, "curpressed") == 0) {
return CurPressed;
} else {
return 0;
}
}
void convert_hex_to_rgba(float *color, unsigned long int hex) {
color[0] = ((hex >> 24) & 0xFF) / 255.0f;
color[1] = ((hex >> 16) & 0xFF) / 255.0f;
color[2] = ((hex >> 8) & 0xFF) / 255.0f;
color[3] = (hex & 0xFF) / 255.0f;
}
FuncType parse_func_name(char *func_name, Arg *arg, char *arg_value) {
FuncType func = NULL;
(*arg).v = NULL;
if (strcmp(func_name, "focusstack") == 0) {
func = focusstack;
(*arg).i = parse_circle_direction(arg_value);
} else if (strcmp(func_name, "focusdir") == 0) {
func = focusdir;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "incnmaster") == 0) {
func = incnmaster;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "setmfact") == 0) {
func = setmfact;
(*arg).f = atof(arg_value);
} else if (strcmp(func_name, "zoom") == 0) {
func = zoom;
} else if (strcmp(func_name, "exchange_client") == 0) {
func = exchange_client;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "toggleglobal") == 0) {
func = toggleglobal;
} else if (strcmp(func_name, "toggleoverview") == 0) {
func = toggleoverview;
} else if (strcmp(func_name, "set_proportion") == 0) {
func = set_proportion;
(*arg).f = atof(arg_value);
} else if (strcmp(func_name, "switch_proportion_preset") == 0) {
func = switch_proportion_preset;
} else if (strcmp(func_name, "viewtoleft") == 0) {
func = viewtoleft;
} else if (strcmp(func_name, "viewtoright") == 0) {
func = viewtoright;
} else if (strcmp(func_name, "tagtoleft") == 0) {
func = tagtoleft;
} else if (strcmp(func_name, "tagtoright") == 0) {
func = tagtoright;
} else if (strcmp(func_name, "killclient") == 0) {
func = killclient;
} else if (strcmp(func_name, "setlayout") == 0) {
func = setlayout;
(*arg).v = strdup(arg_value);
} else if (strcmp(func_name, "switch_layout") == 0) {
func = switch_layout;
} else if (strcmp(func_name, "togglefloating") == 0) {
func = togglefloating;
} else if (strcmp(func_name, "togglefullscreen") == 0) {
func = togglefullscreen;
} else if (strcmp(func_name, "minized") == 0) {
func = minized;
} else if (strcmp(func_name, "restore_minized") == 0) {
func = restore_minized;
} else if (strcmp(func_name, "toggle_scratchpad") == 0) {
func = toggle_scratchpad;
} else if (strcmp(func_name, "focusmon") == 0) {
func = focusmon;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "tagmon") == 0) {
func = tagmon;
(*arg).i = parse_direction(arg_value);
} else if (strcmp(func_name, "incgaps") == 0) {
func = incgaps;
(*arg).i = atoi(arg_value);
} else if (strcmp(func_name, "togglegaps") == 0) {
func = togglegaps;
} else if (strcmp(func_name, "chvt") == 0) {
func = chvt;
(*arg).ui = atoi(arg_value);
} else if (strcmp(func_name, "spawn") == 0) {
func = spawn;
(*arg).v = strdup(arg_value);
} else if (strcmp(func_name, "quit") == 0) {
func = quit;
} else if (strcmp(func_name, "moveresize") == 0) {
func = moveresize;
(*arg).ui = parse_mouse_action(arg_value);
} else if (strcmp(func_name, "togglemaxmizescreen") == 0) {
func = togglemaxmizescreen;
} else if (strcmp(func_name, "viewtoleft_have_client") == 0) {
func = viewtoleft_have_client;
} else if (strcmp(func_name, "viewtoright_have_client") == 0) {
func = viewtoright_have_client;
} else if (strcmp(func_name, "reload_config") == 0) {
func = reload_config;
} else if (strcmp(func_name, "tag") == 0) {
func = tag;
(*arg).ui = 1 << (atoi(arg_value) - 1);
} else if (strcmp(func_name, "view") == 0) {
func = bind_to_view;
(*arg).ui = 1 << (atoi(arg_value) - 1);
} else {
return NULL;
}
return func;
}
void parse_config_line(Config *config, const char *line) {
char key[256], value[256];
if (sscanf(line, "%[^=]=%[^\n]", key, value) != 2) {
fprintf(stderr, "Error: Invalid line format: %s\n", line);
return;
}
if (strcmp(key, "animations") == 0) {
config->animations = atoi(value);
} else if (strcmp(key, "animation_type") == 0) {
strncpy(config->animation_type, value, sizeof(config->animation_type));
} else if (strcmp(key, "animation_fade_in") == 0) {
config->animation_fade_in = atoi(value);
} else if (strcmp(key, "zoom_initial_ratio") == 0) {
config->zoom_initial_ratio = atof(value);
} else if (strcmp(key, "fadein_begin_opacity") == 0) {
config->fadein_begin_opacity = atof(value);
} else if (strcmp(key, "fadeout_begin_opacity") == 0) {
config->fadeout_begin_opacity = atof(value);
} else if (strcmp(key, "animation_duration_move") == 0) {
config->animation_duration_move = atoi(value);
} else if (strcmp(key, "animation_duration_open") == 0) {
config->animation_duration_open = atoi(value);
} else if (strcmp(key, "animation_duration_tag") == 0) {
config->animation_duration_tag = atoi(value);
} else if (strcmp(key, "animation_duration_close") == 0) {
config->animation_duration_close = atoi(value);
} else if (strcmp(key, "animation_curve_move") == 0) {
if (sscanf(value, "%lf,%lf,%lf,%lf", &config->animation_curve_move[0],
&config->animation_curve_move[1], &config->animation_curve_move[2],
&config->animation_curve_move[3]) != 4) {
fprintf(stderr, "Error: Invalid animation_curve_move format: %s\n", value);
}
} else if (strcmp(key, "animation_curve_open") == 0) {
if (sscanf(value, "%lf,%lf,%lf,%lf", &config->animation_curve_open[0],
&config->animation_curve_open[1], &config->animation_curve_open[2],
&config->animation_curve_open[3]) != 4) {
fprintf(stderr, "Error: Invalid animation_curve_open format: %s\n", value);
}
} else if (strcmp(key, "animation_curve_tag") == 0) {
if (sscanf(value, "%lf,%lf,%lf,%lf", &config->animation_curve_tag[0],
&config->animation_curve_tag[1], &config->animation_curve_tag[2],
&config->animation_curve_tag[3]) != 4) {
fprintf(stderr, "Error: Invalid animation_curve_tag format: %s\n", value);
}
} else if (strcmp(key, "scroller_structs") == 0) {
config->scroller_structs = atoi(value);
} else if (strcmp(key, "scroller_default_proportion") == 0) {
config->scroller_default_proportion = atof(value);
} else if (strcmp(key, "scoller_focus_center") == 0) {
config->scoller_focus_center = atoi(value);
} else if (strcmp(key, "scroller_proportion_preset") == 0) {
// 1. 统计 value 中有多少个逗号,确定需要解析的浮点数个数
int count = 0; // 初始化为 0
for (const char *p = value; *p; p++) {
if (*p == ',') count++;
}
int float_count = count + 1; // 浮点数的数量是逗号数量加 1
// 2. 动态分配内存,存储浮点数
config->scroller_proportion_preset = (float *)malloc(float_count * sizeof(float));
if (!config->scroller_proportion_preset) {
fprintf(stderr, "Error: Memory allocation failed\n");
return;
}
// 3. 解析 value 中的浮点数
char *value_copy = strdup(value); // 复制 value因为 strtok 会修改原字符串
char *token = strtok(value_copy, ",");
int i = 0;
while (token != NULL && i < float_count) {
if (sscanf(token, "%f", &config->scroller_proportion_preset[i]) != 1) {
fprintf(stderr, "Error: Invalid float value in scroller_proportion_preset: %s\n", token);
free(value_copy);
free(config->scroller_proportion_preset); // 释放已分配的内存
config->scroller_proportion_preset = NULL; // 防止野指针
return;
}
token = strtok(NULL, ",");
i++;
}
// 4. 检查解析的浮点数数量是否匹配
if (i != float_count) {
fprintf(stderr, "Error: Invalid scroller_proportion_preset format: %s\n", value);
free(value_copy);
free(config->scroller_proportion_preset); // 释放已分配的内存
config->scroller_proportion_preset = NULL; // 防止野指针
config->scroller_proportion_preset_count = 0;
return;
}
config->scroller_proportion_preset_count = float_count;
// 5. 释放临时复制的字符串
free(value_copy);
} else if (strcmp(key, "circle_layout") == 0) {
// 1. 统计 value 中有多少个逗号,确定需要解析的字符串个数
int count = 0; // 初始化为 0
for (const char *p = value; *p; p++) {
if (*p == ',') count++;
}
int string_count = count + 1; // 字符串的数量是逗号数量加 1
// 2. 动态分配内存,存储字符串指针
config->circle_layout = (char **)malloc(string_count * sizeof(char *));
memset(config->circle_layout, 0, string_count * sizeof(char *));
if (!config->circle_layout) {
fprintf(stderr, "Error: Memory allocation failed\n");
return;
}
// 3. 解析 value 中的字符串
char *value_copy = strdup(value); // 复制 value因为 strtok 会修改原字符串
char *token = strtok(value_copy, ",");
int i = 0;
char *cleaned_token;
while (token != NULL && i < string_count) {
// 为每个字符串分配内存并复制内容
cleaned_token = sanitize_string(token);
config->circle_layout[i] = strdup(cleaned_token);
if (!config->circle_layout[i]) {
fprintf(stderr, "Error: Memory allocation failed for string: %s\n", token);
// 释放之前分配的内存
for (int j = 0; j < i; j++) {
free(config->circle_layout[j]);
}
free(config->circle_layout);
free(value_copy);
config->circle_layout = NULL; // 防止野指针
config->circle_layout_count = 0;
return;
}
token = strtok(NULL, ",");
i++;
}
// 4. 检查解析的字符串数量是否匹配
if (i != string_count) {
fprintf(stderr, "Error: Invalid circle_layout format: %s\n", value);
// 释放之前分配的内存
for (int j = 0; j < i; j++) {
free(config->circle_layout[j]);
}
free(config->circle_layout);
free(value_copy);
config->circle_layout = NULL; // 防止野指针
config->circle_layout_count = 0;
return;
}
config->circle_layout_count = string_count;
// 5. 释放临时复制的字符串
free(value_copy);
} else if (strcmp(key, "new_is_master") == 0) {
config->new_is_master = atoi(value);
} else if (strcmp(key, "default_mfact") == 0) {
config->default_mfact = atof(value);
} else if (strcmp(key, "default_nmaster") == 0) {
config->default_nmaster = atoi(value);
} else if (strcmp(key, "hotarea_size") == 0) {
config->hotarea_size = atoi(value);
} else if (strcmp(key, "enable_hotarea") == 0) {
config->enable_hotarea = atoi(value);
} else if (strcmp(key, "ov_tab_mode") == 0) {
config->ov_tab_mode = atoi(value);
} else if (strcmp(key, "overviewgappi") == 0) {
config->overviewgappi = atoi(value);
} else if (strcmp(key, "overviewgappo") == 0) {
config->overviewgappo = atoi(value);
} else if (strcmp(key, "axis_bind_apply_timeout") == 0) {
config->axis_bind_apply_timeout = atoi(value);
} else if (strcmp(key, "focus_on_activate") == 0) {
config->focus_on_activate = atoi(value);
} else if (strcmp(key, "numlockon") == 0) {
config->numlockon = atoi(value);
} else if (strcmp(key, "bypass_surface_visibility") == 0) {
config->bypass_surface_visibility = atoi(value);
} else if (strcmp(key, "sloppyfocus") == 0) {
config->sloppyfocus = atoi(value);
} else if (strcmp(key, "warpcursor") == 0) {
config->warpcursor = atoi(value);
} else if (strcmp(key, "smartgaps") == 0) {
config->smartgaps = atoi(value);
} else if (strcmp(key, "repeat_rate") == 0) {
config->repeat_rate = atoi(value);
} else if (strcmp(key, "repeat_delay") == 0) {
config->repeat_delay = atoi(value);
} else if (strcmp(key, "tap_to_click") == 0) {
config->tap_to_click = atoi(value);
} else if (strcmp(key, "tap_and_drag") == 0) {
config->tap_and_drag = atoi(value);
} else if (strcmp(key, "drag_lock") == 0) {
config->drag_lock = atoi(value);
} else if (strcmp(key, "natural_scrolling") == 0) {
config->natural_scrolling = atoi(value);
} else if (strcmp(key, "disable_while_typing") == 0) {
config->disable_while_typing = atoi(value);
} else if (strcmp(key, "left_handed") == 0) {
config->left_handed = atoi(value);
} else if (strcmp(key, "middle_button_emulation") == 0) {
config->middle_button_emulation = atoi(value);
} else if (strcmp(key, "gappih") == 0) {
config->gappih = atoi(value);
} else if (strcmp(key, "gappiv") == 0) {
config->gappiv = atoi(value);
} else if (strcmp(key, "gappoh") == 0) {
config->gappoh = atoi(value);
} else if (strcmp(key, "gappov") == 0) {
config->gappov = atoi(value);
} else if (strcmp(key, "borderpx") == 0) {
config->borderpx = atoi(value);
} else if (strcmp(key, "rootcolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid rootcolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->rootcolor, color);
}
} else if (strcmp(key, "bordercolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid bordercolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->bordercolor, color);
}
} else if (strcmp(key, "focuscolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid focuscolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->focuscolor, color);
}
} else if (strcmp(key, "maxmizescreencolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid maxmizescreencolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->maxmizescreencolor, color);
}
} else if (strcmp(key, "urgentcolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid urgentcolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->urgentcolor, color);
}
} else if (strcmp(key, "scratchpadcolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid scratchpadcolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->scratchpadcolor, color);
}
} else if (strcmp(key, "globalcolor") == 0) {
long int color = parse_color(value);
if (color == -1) {
fprintf(stderr, "Error: Invalid globalcolor format: %s\n", value);
} else {
convert_hex_to_rgba(config->globalcolor, color);
}
} else if (strcmp(key, "autostart") == 0) {
if (sscanf(value, "%[^,],%[^,],%[^,]", config->autostart[0],
config->autostart[1], config->autostart[2]) != 3) {
fprintf(stderr, "Error: Invalid autostart format: %s\n", value);
}
} else if (strcmp(key, "tags") == 0) {
int id;
char layout_name[256];
if (sscanf(value, "id:%d,layout_name:%255[^\n]", &id, layout_name) == 2) {
if (id >= 1 && id <= 9) {
config->tags[id - 1].id = id;
strncpy(config->tags[id - 1].layout_name, layout_name,
sizeof(config->tags[id - 1].layout_name));
} else {
fprintf(stderr, "Error: Invalid tag id: %d\n", id);
}
} else {
fprintf(stderr, "Error: Invalid tags format: %s\n", value);
}
} else if (strcmp(key, "windowrule") == 0) {
config->window_rules =
realloc(config->window_rules,
(config->window_rules_count + 1) * sizeof(ConfigWinRule));
if (!config->window_rules) {
fprintf(stderr, "Error: Failed to allocate memory for window rules\n");
return;
}
ConfigWinRule *rule = &config->window_rules[config->window_rules_count];
memset(rule, 0, sizeof(ConfigWinRule));
rule->isfloating = -1;
rule->isfullscreen = -1;
rule->isnoborder = -1;
rule->monitor = -1;
rule->width = -1;
rule->height = -1;
rule->animation_type = NULL;
rule->scroller_proportion = -1;
rule->id = NULL;
rule->title = NULL;
rule->tags = 0;
char *token = strtok(value, ",");
while (token != NULL) {
char *colon = strchr(token, ':');
if (colon != NULL) {
*colon = '\0';
char *key = token;
char *val = colon + 1;
if (strcmp(key, "isfloating") == 0) {
rule->isfloating = atoi(val);
} else if (strcmp(key, "title") == 0) {
rule->title = strdup(val);
} else if (strcmp(key, "appid") == 0) {
rule->id = strdup(val);
} else if (strcmp(key, "animation_type") == 0) {
rule->animation_type = strdup(val);
} else if (strcmp(key, "tags") == 0) {
rule->tags = 1 << (atoi(val) - 1);
} else if (strcmp(key, "monitor") == 0) {
rule->monitor = atoi(val);
} else if (strcmp(key, "width") == 0) {
rule->width = atoi(val);
} else if (strcmp(key, "height") == 0) {
rule->height = atoi(val);
} else if (strcmp(key, "isnoborder") == 0) {
rule->isnoborder = atoi(val);
} else if (strcmp(key, "scroller_proportion") == 0) {
rule->scroller_proportion = atof(val);
} else if (strcmp(key, "isfullscreen") == 0) {
rule->isfullscreen = atoi(val);
}
}
token = strtok(NULL, ",");
}
config->window_rules_count++;
} else if (strcmp(key, "monitorrule") == 0) {
config->monitor_rules =
realloc(config->monitor_rules,
(config->monitor_rules_count + 1) * sizeof(ConfigMonitorRule));
if (!config->monitor_rules) {
fprintf(stderr, "Error: Failed to allocate memory for monitor rules\n");
return;
}
ConfigMonitorRule *rule =
&config->monitor_rules[config->monitor_rules_count];
memset(rule, 0, sizeof(ConfigMonitorRule));
char layout[256], name[256];
int parsed = sscanf(value, "%255[^,],%f,%d,%255[^,],%d,%f,%d,%d", name,
&rule->mfact, &rule->nmaster, layout, &rule->rr,
&rule->scale, &rule->x, &rule->y);
if (parsed == 8) {
rule->name = strdup(name);
rule->layout = strdup(layout);
if (!rule->name || !rule->layout) {
if (rule->name)
free((void *)rule->name);
if (rule->layout)
free((void *)rule->layout);
fprintf(stderr, "Error: Failed to allocate memory for monitor rule\n");
return;
}
config->monitor_rules_count++;
} else {
fprintf(stderr, "Error: Invalid monitorrule format: %s\n", value);
}
} else if (strncmp(key, "bind", 4) == 0) {
config->key_bindings =
realloc(config->key_bindings,
(config->key_bindings_count + 1) * sizeof(KeyBinding));
if (!config->key_bindings) {
fprintf(stderr, "Error: Failed to allocate memory for key bindings\n");
return;
}
KeyBinding *binding = &config->key_bindings[config->key_bindings_count];
memset(binding, 0, sizeof(KeyBinding));
char mod_str[256], keysym_str[256], func_name[256], arg_value[256] = "none";
if (sscanf(value, "%[^,],%[^,],%[^,],%[^\n]", mod_str, keysym_str,
func_name, arg_value) < 3) {
fprintf(stderr, "Error: Invalid bind format: %s\n", value);
return;
}
binding->mod = parse_mod(mod_str);
binding->keysym = parse_keysym(keysym_str);
binding->arg.v = NULL;
binding->func = parse_func_name(func_name, &binding->arg, arg_value);
if (!binding->func) {
fprintf(stderr, "Error: Unknown function in bind: %s\n", func_name);
} else {
config->key_bindings_count++;
}
// 计算默认按键绑定的数量
size_t default_key_bindings_count =
sizeof(default_key_bindings) / sizeof(KeyBinding);
// 重新分配内存以容纳新的默认按键绑定
config->key_bindings =
realloc(config->key_bindings,
(config->key_bindings_count + default_key_bindings_count) *
sizeof(KeyBinding));
if (!config->key_bindings) {
return;
}
// 将默认按键绑定复制到配置的按键绑定数组中
for (size_t i = 0; i < default_key_bindings_count; i++) {
config->key_bindings[config->key_bindings_count + i] =
default_key_bindings[i];
}
// 更新按键绑定的总数
config->key_bindings_count += default_key_bindings_count;
} else if (strncmp(key, "mousebind", 9) == 0) {
config->mouse_bindings =
realloc(config->mouse_bindings,
(config->mouse_bindings_count + 1) * sizeof(MouseBinding));
if (!config->mouse_bindings) {
fprintf(stderr, "Error: Failed to allocate memory for mouse bindings\n");
return;
}
MouseBinding *binding =
&config->mouse_bindings[config->mouse_bindings_count];
memset(binding, 0, sizeof(MouseBinding));
char mod_str[256], button_str[256], func_name[256], arg_value[256] = "none";
if (sscanf(value, "%[^,],%[^,],%[^,],%[^\n]", mod_str, button_str,
func_name, arg_value) < 3) {
fprintf(stderr, "Error: Invalid mousebind format: %s\n", value);
return;
}
binding->mod = parse_mod(mod_str);
binding->button = parse_button(button_str);
binding->arg.v = NULL;
binding->func = parse_func_name(func_name, &binding->arg, arg_value);
if (!binding->func) {
fprintf(stderr, "Error: Unknown function in mousebind: %s\n", func_name);
} else {
config->mouse_bindings_count++;
}
} else if (strncmp(key, "axisbind", 8) == 0) {
config->axis_bindings =
realloc(config->axis_bindings,
(config->axis_bindings_count + 1) * sizeof(AxisBinding));
if (!config->axis_bindings) {
fprintf(stderr, "Error: Failed to allocate memory for axis bindings\n");
return;
}
AxisBinding *binding = &config->axis_bindings[config->axis_bindings_count];
memset(binding, 0, sizeof(AxisBinding));
char mod_str[256], dir_str[256], func_name[256], arg_value[256] = "none";
if (sscanf(value, "%[^,],%[^,],%[^,],%[^\n]", mod_str, dir_str, func_name,
arg_value) < 3) {
fprintf(stderr, "Error: Invalid axisbind format: %s\n", value);
return;
}
binding->mod = parse_mod(mod_str);
binding->dir = parse_direction(dir_str);
binding->arg.v = NULL;
binding->func = parse_func_name(func_name, &binding->arg, arg_value);
if (!binding->func) {
fprintf(stderr, "Error: Unknown function in axisbind: %s\n", func_name);
} else {
config->axis_bindings_count++;
}
} else {
fprintf(stderr, "Error: Unknown key: %s\n", key);
}
}
void parse_config_file(Config *config, const char *file_path) {
FILE *file = fopen(file_path, "r");
if (!file) {
perror("Error opening file");
return;
}
char line[512];
while (fgets(line, sizeof(line), file)) {
if (line[0] == '#' || line[0] == '\n')
continue;
parse_config_line(config, line);
}
fclose(file);
}
void free_circle_layout(Config *config) {
if (config->circle_layout) {
// 释放每个字符串
for (int i = 0; i < config->circle_layout_count; i++) {
if (config->circle_layout[i]) {
free(config->circle_layout[i]); // 释放单个字符串
config->circle_layout[i] = NULL; // 防止野指针
}
}
// 释放 circle_layout 数组本身
free(config->circle_layout);
config->circle_layout = NULL; // 防止野指针
}
config->circle_layout_count = 0; // 重置计数
}
void free_config(void) {
// 释放内存
int i;
for (i = 0; i < config.window_rules_count; i++) {
ConfigWinRule *rule = &config.window_rules[i];
if (rule->id)
free((void *)rule->id);
if (rule->title)
free((void *)rule->title);
if (rule->animation_type)
free((void *)rule->animation_type);
}
free(config.window_rules);
for (i = 0; i < config.monitor_rules_count; i++) {
ConfigMonitorRule *rule = &config.monitor_rules[i];
if (rule->name)
free((void *)rule->name);
if (rule->layout)
free((void *)rule->layout);
}
free(config.monitor_rules);
for (i = 0; i < config.key_bindings_count; i++) {
if (config.key_bindings[i].arg.v) {
free((void *)config.key_bindings[i].arg.v);
config.key_bindings[i].arg.v = NULL; // 避免重复释放
}
}
free(config.key_bindings);
for (i = 0; i < config.mouse_bindings_count; i++) {
if (config.mouse_bindings[i].arg.v) {
free((void *)config.mouse_bindings[i].arg.v);
config.mouse_bindings[i].arg.v = NULL; // 避免重复释放
}
}
free(config.mouse_bindings);
for (i = 0; i < config.axis_bindings_count; i++) {
if (config.axis_bindings[i].arg.v) {
free((void *)config.axis_bindings[i].arg.v);
config.axis_bindings[i].arg.v = NULL; // 避免重复释放
}
}
free(config.axis_bindings);
free(config.scroller_proportion_preset);
config.scroller_proportion_preset = NULL;
config.scroller_proportion_preset_count = 0;
free_circle_layout(&config);
}
void override_config(void) {
animations = config.animations;
animation_type = config.animation_type;
animation_fade_in = config.animation_fade_in;
zoom_initial_ratio = config.zoom_initial_ratio;
fadein_begin_opacity = config.fadein_begin_opacity;
fadeout_begin_opacity = config.fadeout_begin_opacity;
animation_duration_move = config.animation_duration_move;
animation_duration_open = config.animation_duration_open;
animation_duration_tag = config.animation_duration_tag;
animation_duration_close = config.animation_duration_close;
// 复制数组类型的变量
memcpy(animation_curve_move, config.animation_curve_move, sizeof(animation_curve_move));
memcpy(animation_curve_open, config.animation_curve_open, sizeof(animation_curve_open));
memcpy(animation_curve_tag, config.animation_curve_tag, sizeof(animation_curve_tag));
scroller_structs = config.scroller_structs;
scroller_default_proportion = config.scroller_default_proportion;
scoller_focus_center = config.scoller_focus_center;
new_is_master = config.new_is_master;
default_mfact = config.default_mfact;
default_nmaster = config.default_nmaster;
hotarea_size = config.hotarea_size;
enable_hotarea = config.enable_hotarea;
ov_tab_mode = config.ov_tab_mode;
overviewgappi = config.overviewgappi;
overviewgappo = config.overviewgappo;
axis_bind_apply_timeout = config.axis_bind_apply_timeout;
focus_on_activate = config.focus_on_activate;
numlockon = config.numlockon;
bypass_surface_visibility = config.bypass_surface_visibility;
sloppyfocus = config.sloppyfocus;
warpcursor = config.warpcursor;
smartgaps = config.smartgaps;
gappih = config.gappih;
gappiv = config.gappiv;
gappoh = config.gappoh;
gappov = config.gappov;
borderpx = config.borderpx;
repeat_rate = config.repeat_rate;
repeat_delay = config.repeat_delay;
tap_to_click = config.tap_to_click;
tap_and_drag = config.tap_and_drag;
drag_lock = config.drag_lock;
natural_scrolling = config.natural_scrolling;
disable_while_typing = config.disable_while_typing;
left_handed = config.left_handed;
middle_button_emulation = config.middle_button_emulation;
// 复制颜色数组
memcpy(rootcolor, config.rootcolor, sizeof(rootcolor));
memcpy(bordercolor, config.bordercolor, sizeof(bordercolor));
memcpy(focuscolor, config.focuscolor, sizeof(focuscolor));
memcpy(maxmizescreencolor, config.maxmizescreencolor,
sizeof(maxmizescreencolor));
memcpy(urgentcolor, config.urgentcolor, sizeof(urgentcolor));
memcpy(scratchpadcolor, config.scratchpadcolor, sizeof(scratchpadcolor));
memcpy(globalcolor, config.globalcolor, sizeof(globalcolor));
}
void set_value_default() {
/* animaion */
config.animations = 1; // 是否启用动画
config.animation_fade_in = 1; // Enable animation fade in
config.zoom_initial_ratio = 0.5; // 动画起始窗口比例
config.fadein_begin_opacity = 0.5; // Begin opac window ratio for animations
config.fadeout_begin_opacity = 0.5;
config.animation_duration_move = 500; // Animation move speed
config.animation_duration_open = 400; // Animation open speed
config.animation_duration_tag = 300; // Animation tag speed
config.animation_duration_close = 300; // Animation tag speed
/* appearance */
config.axis_bind_apply_timeout = 100; // 滚轮绑定动作的触发的时间间隔
config.focus_on_activate = 1; // 收到窗口激活请求是否自动跳转聚焦
config.new_is_master = 1; // 新窗口是否插在头部
config.default_mfact = 0.55f; // master 窗口比例
config.default_nmaster = 1; // 默认master数量
config.numlockon = 1; // 是否打开右边小键盘
config.ov_tab_mode = 0; // alt tab切换模式
config.hotarea_size = 10; // 热区大小,10x10
config.enable_hotarea = 1; // 是否启用鼠标热区
config.smartgaps = 0; /* 1 means no outer gap when there is only one window */
config.sloppyfocus = 1; /* focus follows mouse */
config.gappih = 5; /* horiz inner gap between windows */
config.gappiv = 5; /* vert inner gap between windows */
config.gappoh = 10; /* horiz outer gap between windows and screen edge */
config.gappov = 10; /* vert outer gap between windows and screen edge */
config.scroller_structs = 20;
config.scroller_default_proportion = 0.9;
config.scoller_focus_center = 0;
config.bypass_surface_visibility =
0; /* 1 means idle inhibitors will disable idle tracking even if it's
surface isn't visible */
config.overviewgappi = 5; /* overview时 窗口与边缘 缝隙大小 */
config.overviewgappo = 30; /* overview时 窗口与窗口 缝隙大小 */
config.warpcursor = 1; /* Warp cursor to focused client */
config.repeat_rate = 25;
config.repeat_delay = 600;
/* Trackpad */
config.tap_to_click = 1;
config.tap_and_drag = 1;
config.drag_lock = 1;
config.natural_scrolling = 0;
config.disable_while_typing = 1;
config.left_handed = 0;
config.middle_button_emulation = 0;
memcpy(config.animation_curve_move, animation_curve_move, sizeof(animation_curve_move));
memcpy(config.animation_curve_open, animation_curve_open, sizeof(animation_curve_open));
memcpy(config.animation_curve_tag, animation_curve_tag, sizeof(animation_curve_tag));
}
void parse_config(void) {
char filename[1024];
memset(&config, 0, sizeof(config));
config.window_rules = NULL;
config.window_rules_count = 0;
config.monitor_rules = NULL;
config.monitor_rules_count = 0;
config.key_bindings = NULL;
config.key_bindings_count = 0;
config.mouse_bindings = NULL;
config.mouse_bindings_count = 0;
config.axis_bindings = NULL;
config.axis_bindings_count = 0;
// 获取 MAOMAOCONFIG 环境变量
const char *maomaoconfig = getenv("MAOMAOCONFIG");
// 如果 MAOMAOCONFIG 环境变量不存在或为空,则使用 HOME 环境变量
if (!maomaoconfig || maomaoconfig[0] == '\0') {
// 获取当前用户家目录
const char *homedir = getenv("HOME");
if (!homedir) {
// 如果获取失败,则无法继续
return;
}
// 构建日志文件路径
snprintf(filename, sizeof(filename), "%s/.config/maomao/config.conf",
homedir);
// 检查文件是否存在
if (access(filename, F_OK) != 0) {
// 如果文件不存在,则使用 /etc/maomao/config.conf
snprintf(filename, sizeof(filename), "%s/maomao/config.conf",SYSCONFDIR);
}
} else {
// 使用 MAOMAOCONFIG 环境变量作为配置文件夹路径
snprintf(filename, sizeof(filename), "%s/config.conf", maomaoconfig);
}
set_value_default();
parse_config_file(&config, filename);
override_config();
}
void reload_config(const Arg *arg) {
Client *c;
Keyboard *kb;
free_config();
parse_config();
init_baked_points();
wl_list_for_each(c, &clients, link) {
if (c && !c->iskilling) {
if (c->bw) {
c->bw = borderpx;
}
}
}
wl_list_for_each(kb, &keyboards, link) {
wlr_keyboard_set_repeat_info(kb->wlr_keyboard, repeat_rate, repeat_delay);
}
arrange(selmon, false);
}