diff --git a/.clang-format b/.clang-format index 912692d..a981890 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,55 @@ --- -IndentWidth: '2' - -... +Language: Cpp +IndentWidth: 2 +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true + AlignCompound: true + PadOperators: true +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveMacros: Consecutive +AlignConsecutiveShortCaseStatements: + Enabled: true + # AlignCaseArrows: true + AlignCaseColons: true +AlignOperands: Align +AlignTrailingComments: Always +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: true +# AllowShortCaseExpressionOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Attach +BreakBeforeConceptDeclarations: Never +BreakBeforeBinaryOperators: true +BreakBeforeTernaryOperators: true +ColumnLimit: 128 +IndentCaseBlocks: false +IndentCaseLabels: true +IndentPPDirectives: AfterHash +InsertBraces: false +InsertNewlineAtEOF: true +LineEnding: LF +PointerAlignment: Right +QualifierAlignment: Left +ReferenceAlignment: Right +ReflowComments: true +RemoveBracesLLVM: true +SeparateDefinitionBlocks: Always +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: ControlStatementsExceptControlMacros +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesInAngles: Never +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParens: Never +UseTab: Never diff --git a/CHANGELOG.md b/CHANGELOG.md index 3803a77..86d5c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ This project tries to adhere to [Semantic Versioning][2]. ## [Unreleased] +### Added + +Support for saving kanshi config file + +### Changed + +### Fixed + ## [1.1.1] - 2023-07-01 ### Added diff --git a/protocol/meson.build b/protocol/meson.build index 5f34a7a..670a4af 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -5,7 +5,7 @@ wayland_scanner = find_program('wayland-scanner') wayland_client = dependency('wayland-client') wayland_protos = dependency('wayland-protocols', version: '>=1.17') -wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir') +wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_scanner_code = generator( wayland_scanner, diff --git a/src/main.c b/src/main.c index e9690c4..a81e348 100644 --- a/src/main.c +++ b/src/main.c @@ -1083,7 +1083,7 @@ static void activate(GtkApplication* app, gpointer user_data) { int main(int argc, char *argv[]) { g_setenv("GDK_GL", "gles", FALSE); - GtkApplication *app = gtk_application_new(WDISPLAYS_APP_ID, G_APPLICATION_FLAGS_NONE); + GtkApplication *app = gtk_application_new(WDISPLAYS_APP_ID, G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); int status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); diff --git a/src/meson.build b/src/meson.build index 8527452..286f631 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,7 +6,7 @@ m_dep = cc.find_library('m', required : false) rt_dep = cc.find_library('rt', required : false) gdk = dependency('gdk-3.0', version: '>= 3.24') gtk = dependency('gtk+-3.0', version: '>= 3.24') -assert(gdk.get_pkgconfig_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present') +assert(gdk.get_variable('targets').split().contains('wayland'), 'Wayland GDK backend not present') epoxy = dependency('epoxy') configure_file(input: 'config.h.in', output: 'config.h', configuration: conf) diff --git a/src/outputs.c b/src/outputs.c index 5108f61..f4ac15c 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -533,7 +533,7 @@ static void output_manager_handle_done(void *data, static const struct zwlr_output_manager_v1_listener output_manager_listener = { .head = output_manager_handle_head, .done = output_manager_handle_done, - .finished = noop, + .finished = (void (*)(void *, struct zwlr_output_manager_v1 *))noop, }; static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { @@ -560,7 +560,7 @@ static void registry_handle_global(void *data, struct wl_registry *registry, static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, - .global_remove = noop, + .global_remove = (void (*)(void *, struct wl_registry *, uint32_t))noop, }; void wd_add_output_management_listener(struct wd_state *state, struct @@ -610,10 +610,10 @@ static void output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, static const struct zxdg_output_v1_listener output_listener = { .logical_position = output_logical_position, - .logical_size = noop, - .done = noop, + .logical_size = (void (*)(void *, struct zxdg_output_v1 *, int32_t, int32_t))noop, + .done = (void (*)(void *, struct zxdg_output_v1 *))noop, .name = output_name, - .description = noop + .description = (void (*)(void *, struct zxdg_output_v1 *, const char *))noop }; void wd_add_output(struct wd_state *state, struct wl_output *wl_output, 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); + + 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); + } - // if store_path is not found in wdisplays config file, return default path - return strdup(defaultPath); + // 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 4824017..e13f975 100644 --- a/src/wdisplays.h +++ b/src/wdisplays.h @@ -14,7 +14,7 @@ #include "config.h" -#define HEADS_MAX 64 +#define HEADS_MAX 64 #define HOVER_USECS (100 * 1000) #include @@ -110,10 +110,12 @@ struct wd_head { bool enabled; struct wd_mode *mode; + struct { int32_t width, height; int32_t refresh; } custom_mode; + int32_t x, y; enum wl_output_transform transform; double scale; @@ -224,7 +226,6 @@ struct wd_state { struct wd_render_data render; }; - /* * Creates the application state structure. */ @@ -339,4 +340,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