From 3a07d4174c2ac78f9b7618a9d422748acd72b4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jason=20Andr=C3=A9=20Charles=20Gantner?= Date: Tue, 1 Jul 2025 00:40:16 +0200 Subject: [PATCH] PR#11 improvment proposal (#1) * check config path only on startup * use HEADS_MAX from wdisplays.h * remove useless strcpy * update readme and meson syntax * clang-format --- src/store.c | 320 ++++++++++++++++++++++++------------------------ src/wdisplays.h | 15 +++ 2 files changed, 172 insertions(+), 163 deletions(-) diff --git a/src/store.c b/src/store.c index 6dab784..f2c43c2 100644 --- a/src/store.c +++ b/src/store.c @@ -1,164 +1,181 @@ #include "wdisplays.h" #include +#include #include +#include #include #include #include #include -#include -#include + #define MAX_NAME_LENGTH 256 -#define MAX_MONITORS_NUM 10 + struct wd_head_config; + struct profile_line { int start; int end; }; -char *get_config_file_path() { - char defaultPath[PATH_MAX]; // platform based marco PATH_MAX - char wdisplaysPath[PATH_MAX]; - // if $XDG_CONFIG_HOME is set, use it - { - const char *configDir = getenv("XDG_CONFIG_HOME"); - char defaultConfigDir[PATH_MAX]; + +typedef enum { Looking_for_profile, Looking_for_outputs, Found } parser_states; + +char *wd_get_config_file_path() { + char kanshiConfigPath[PATH_MAX]; + char wdisplaysPath[PATH_MAX]; + char defaultConfigDir[PATH_MAX]; + // if $XDG_CONFIG_HOME is set, use it + { + char *configDir = getenv("XDG_CONFIG_HOME"); + if (configDir == NULL) { // fallback to $HOME + configDir = getenv("HOME"); if (configDir == NULL) { - const char *homeDir = getenv("HOME"); - if (homeDir == NULL) { - perror("Cannot find home directory"); - return NULL; - } - snprintf(defaultConfigDir, sizeof(defaultConfigDir), "%s/.config", homeDir); - } else { - snprintf(defaultConfigDir, sizeof(defaultConfigDir), "%s", configDir); + dprintf(2, "%s:%i:%s(): Cannot find $XDG_CONFIG_HOME nor $HOME directories", __FILE__, __LINE__, __func__); + return NULL; + } else { // configdir is $HOME/config + snprintf(defaultConfigDir, sizeof(defaultConfigDir), "%s/.config", configDir); } - snprintf(defaultPath, sizeof(defaultPath), "%s/kanshi/config", defaultConfigDir); - snprintf(wdisplaysPath, sizeof(wdisplaysPath), "%s/wdisplays/config", defaultConfigDir); + } else { // configDir is $XDG_CONFIG_HOME + snprintf(defaultConfigDir, sizeof(defaultConfigDir), "%s", configDir); } + } - FILE *wdisplaysFile = fopen(wdisplaysPath, "r"); - if (wdisplaysFile != NULL) { - char line[LINE_MAX]; // LINE_MAX is a platform based marco + // set default kanshi config path + snprintf(kanshiConfigPath, sizeof(kanshiConfigPath), "%s/kanshi/config", defaultConfigDir); - // try to match "store_path" term - while (fgets(line, sizeof(line), wdisplaysFile) != NULL) { - if (strstr(line, "store_path") != NULL) { - // if found, extract path - char *pathStart = strchr(line, '='); - if (pathStart != NULL) { - pathStart++; // skip '=' - char *pathEnd = strchr(pathStart, '\n'); - if (pathEnd != NULL) { - *pathEnd = '\0'; // replace '\n' with '\0' - fclose(wdisplaysFile); - return strdup(pathStart); // return path - } - } - } - } - fclose(wdisplaysFile); - } + // look for store_path in wdisplays.conf + snprintf(wdisplaysPath, sizeof(wdisplaysPath), "%s/wdisplays.conf", defaultConfigDir); - // if store_path is not found in wdisplays config file, return default path - return strdup(defaultPath); + FILE *wdisplaysFile = fopen(wdisplaysPath, "r"); + if (wdisplaysFile != NULL) { + char line[LINE_MAX]; // LINE_MAX is a platform-dependendant macro + + // try to match "store_path" term + while (fgets(line, sizeof(line), wdisplaysFile) != NULL) { + if (strstr(line, "store_path") != NULL) { + // if found, extract path + char *pathStart = strchr(line, '='); + if (pathStart != NULL) { + pathStart++; // skip '=' + while (isspace(*pathStart)) pathStart++; // skip spaces between '=' and the start of the path + char *pathEnd = strchr(pathStart, '\n'); + size_t pathLen; + if (pathEnd != NULL) pathLen = pathEnd - pathStart; + else // store_path= is the last line and there's no newline at the end of the file + pathLen = strnlen(pathStart, PATH_MAX); + // save path + strncpy(kanshiConfigPath, pathStart, pathLen); + } else + ; // store_path was not followed by an equal sign on this line + } else + ; // this line does not contain store_path + } // reached end of file + fclose(wdisplaysFile); + } else { // can't open config file + dprintf(2, "%s:%i:%s(): Can't open %s : ", __FILE__, __LINE__, __func__, wdisplaysPath); + perror(NULL); + } + + // look for WDISPLAYS_KANSHI_CONFIG + { + char *envKanshiConf = getenv("WDISPLAYS_KANSHI_CONFIG"); + if (envKanshiConf != NULL) strncpy(kanshiConfigPath, envKanshiConf, sizeof(kanshiConfigPath)); + else + ; + } + char *finalPath = strndup(kanshiConfigPath, PATH_MAX); + if (finalPath == NULL) { + dprintf(2, "%s:%i:%s(): ", __FILE__, __LINE__, __func__); + perror("Failed to allocate memory for kanshi config path"); + } + return finalPath; } -struct profile_line match(char **descriptions, int num, char *filename) { +struct profile_line match(char **descriptions, int num, const char *filename) { struct profile_line matched_profile; matched_profile.start = -1; - matched_profile.end = -1; + matched_profile.end = -1; // -1 means not found - FILE *configFile = fopen(filename, "r"); + FILE *configFile = fopen(filename, "r"); if (configFile == NULL) { - perror("File open failed."); + dprintf(2, "%s:%i:%s(): Can't open %s : ", __FILE__, __LINE__, __func__, filename); + perror(NULL); return matched_profile; } // buffer to store each line char buffer[LINE_MAX]; - char profileName[MAX_NAME_LENGTH]; + char *profileName; int profileStartLine = 0; // mark the start line of matched profile - int profileEndLine = 0; // mark the end line of matched profile + int profileEndLine = 0; // mark the end line of matched profile - int lineCount = 0; // current line number - - while (fgets(buffer, sizeof(buffer), configFile) != NULL) { + int lineCount = 0; // current line number + uint32_t profileMatchedNum = 0; // current number of matched outputs + parser_states ps = Looking_for_profile; // current state of the parser + while (ps != Found && fgets(buffer, sizeof(buffer), configFile) != NULL) { lineCount++; + switch (ps) { + case Found: break; // unreachable code - // check if "profile" keyword is in the line - if (strstr(buffer, "profile") != NULL) { - // extract profile name - sscanf(buffer, "profile %s {", profileName); - - // the number of matched outputs - uint32_t profileMatchedNum = 0; - - // record the start line of the profile - profileStartLine = lineCount; - - while (fgets(buffer, sizeof(buffer), configFile) != NULL) { - lineCount++; + case Looking_for_profile:; + // check if "profile" keyword is in the line and remember its position + char *pstart = strstr(buffer, "profile "); + if (pstart != NULL) { + pstart += 7; + char *pend = strchr(pstart, '{'); // find the end of the profile name + while (isspace(*pend)) pend--; + size_t pnsize = pend - pstart; + // use strndup to extract it without being size constrained + profileName = strndup(pstart, pnsize); + // record the start line of the profile + profileStartLine = lineCount; + ps = Looking_for_outputs; + } + break; + case Looking_for_outputs: // check if the profile ends if (buffer[0] == '}') { profileEndLine = lineCount; - break; - } - char outputName[MAX_NAME_LENGTH]; - // 从当前行提取输出名称 - char *trimmedBuffer = buffer; - while (isspace(*trimmedBuffer)) { - trimmedBuffer++; // skip leading spaces - } - sscanf(trimmedBuffer, "output \"%99[^\"]\"", outputName); // extract output name - - // check if the output name is in the descriptions - bool matched = false; - for (int i = 0; descriptions[i] != NULL; i++) { - if (strcmp(outputName, descriptions[i]) == 0) { - matched = true; + if (profileMatchedNum == num) ps = Found; + } else { + char *on_start = strstr(buffer, "output"); + on_start = strchr(on_start, '"'); + on_start++; + char *on_end = strchr(on_start, '"'); + char *outputName = strndup(on_start, on_end - on_start); + // check if the output name is in the descriptions + int i = 0; + while (descriptions[i] != NULL && strcmp(outputName, descriptions[i])) i++; + if (descriptions[i] != NULL) { profileMatchedNum++; - break; + } else { + // if any output is not matched, break + profileMatchedNum = 0; + ps = Looking_for_profile; } } - - if (!matched) { - // if any output is not matched, break - profileMatchedNum = 0; - break; - } - } - - if (profileMatchedNum == num) { - printf("Matched profile:%s\n", profileName); - printf("Start line:%d\n", profileStartLine); - matched_profile.start = profileStartLine; - printf("End line:%d\n", profileEndLine); - matched_profile.end = profileEndLine; - - fclose(configFile); - return matched_profile; - } + break; } } - fclose(configFile); - printf("Cannot find existing profile to match\n"); + if (ps == Found) { + printf("Matched profile:%s\n", profileName); + printf("Start line:%d\nEnd line:%d\n", profileStartLine, profileEndLine); + matched_profile.start = profileStartLine; + matched_profile.end = profileEndLine; + } else dprintf(2, "%s:%i:%s(): Cannot find existing profile to match\n", __FILE__, __LINE__, __func__); return matched_profile; } -int store_config(struct wl_list *outputs) { - char *file_name = get_config_file_path(); +int wd_store_config(struct wl_list *outputs) { + const char *file_name = wd_get_kanshi_config(); char tmp_file_name[PATH_MAX]; - sprintf(tmp_file_name,"%s.tmp",file_name); + sprintf(tmp_file_name, "%s.tmp", file_name); - char *descriptions[MAX_MONITORS_NUM]; - for (int i = 0; i < MAX_MONITORS_NUM; i++) { - descriptions[i] = NULL; - } + char *descriptions[HEADS_MAX]; + for (int i = 0; i < HEADS_MAX; i++) descriptions[i] = NULL; - char *outputConfigs[MAX_MONITORS_NUM]; - for (int i = 0; i < MAX_MONITORS_NUM; i++) { - outputConfigs[i] = (char *)malloc(MAX_NAME_LENGTH); - } + char *outputConfigs[HEADS_MAX]; + for (int i = 0; i < HEADS_MAX; i++) outputConfigs[i] = (char *)malloc(MAX_NAME_LENGTH); struct wd_head_config *output; int description_index = 0; @@ -166,50 +183,29 @@ int store_config(struct wl_list *outputs) { struct wd_head *head = output->head; // for transform - char *trans_str = (char *)malloc(15 * sizeof(char)); + char *trans_str; switch (output->transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - strcpy(trans_str, "normal"); - break; - case WL_OUTPUT_TRANSFORM_90: - strcpy(trans_str, "90"); - break; - case WL_OUTPUT_TRANSFORM_180: - strcpy(trans_str, "180"); - break; - case WL_OUTPUT_TRANSFORM_270: - strcpy(trans_str, "270"); - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - strcpy(trans_str, "flipped-90"); - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - strcpy(trans_str, "flipped-180"); - break; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - strcpy(trans_str, "flipped-270"); - break; - default: - strcpy(trans_str, "normal"); - break; - } + case WL_OUTPUT_TRANSFORM_NORMAL : trans_str = "normal"; + case WL_OUTPUT_TRANSFORM_90 : trans_str = "90"; + case WL_OUTPUT_TRANSFORM_180 : trans_str = "180"; + case WL_OUTPUT_TRANSFORM_270 : trans_str = "270"; + case WL_OUTPUT_TRANSFORM_FLIPPED_90 : trans_str = "flipped-90"; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: trans_str = "flipped-180"; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: trans_str = "flipped-270"; + default : trans_str = "normal"; + }; - if (description_index < MAX_MONITORS_NUM) { + if (description_index < HEADS_MAX) { descriptions[description_index] = strdup(head->description); // write output config in given format - sprintf( - outputConfigs[description_index], - "output \"%s\" position %d,%d mode %dx%d@%.4f scale %.2f transform %s", - head->description, output->x, output->y, output->width, - output->height, output->refresh / 1.0e3, output->scale, trans_str); + sprintf(outputConfigs[description_index], "output \"%s\" position %d,%d mode %dx%d@%.4f scale %.2f transform %s", + head->description, output->x, output->y, output->width, output->height, output->refresh / 1.0e3, output->scale, + trans_str); description_index++; } else { - free(trans_str); - printf("Too many monitor! 10 is the"); + dprintf(2, "Too many monitor!\n\t%i is the maximum allowed number", HEADS_MAX); return 1; } - - free(trans_str); } int num_of_monitors = description_index; @@ -221,59 +217,57 @@ int store_config(struct wl_list *outputs) { // append new profile FILE *file = fopen(file_name, "a"); if (file == NULL) { - perror("File open failed."); - free(file_name); + dprintf(2, "%s:%i:%s(): Can't open %s : ", __FILE__, __LINE__, __func__, file_name); + perror(NULL); return 1; } fprintf(file, "\nprofile {\n"); - for (int i = 0; i= matched_profile.start && _line < matched_profile.end - 1) { - if(_i_output>=num_of_monitors){ + if (_i_output >= num_of_monitors) { perror("Null pointer"); fclose(tmp); fclose(file); return 1; } - fprintf(tmp," %s\n",outputConfigs[_i_output]); + fprintf(tmp, " %s\n", outputConfigs[_i_output]); free(outputConfigs[_i_output]); _i_output++; - } else{ - fprintf(tmp,"%s",_buffer); + } else { + fprintf(tmp, "%s", _buffer); } _line++; } fclose(file); fclose(tmp); - + remove(file_name); rename(tmp_file_name, file_name); - free(file_name); } return 0; -} \ No newline at end of file +} diff --git a/src/wdisplays.h b/src/wdisplays.h index 0e7a3c6..8dfef68 100644 --- a/src/wdisplays.h +++ b/src/wdisplays.h @@ -341,4 +341,19 @@ void wd_redraw_overlay(struct wd_output *output); */ void wd_destroy_overlay(struct wd_output *output); +/* + * Locate kanshi config + */ +char *wd_get_config_file_path(); + +/* + * Returns kanshi config path + */ +char *wd_get_kanshi_config(); + +/* + * Updates kanshi config + */ +int wd_store_config(struct wl_list *outputs); + #endif