diff --git a/README.md b/README.md index 0612e81..e468d22 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,27 @@ Grab a swappshot from a specific window under Sway, using `swaymsg` and `jq`: grim -g "$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)" - | swappy -f - ``` +## Config + +The config file is located at `$XDG_CONFIG_HOME/swappy/config` or at `$HOME/.config/swappy/config`. + +The file follows the GLib `conf` format. See the `man` page for details. There is example config file [here](example/config). + +The following lines can be used as swappy's default: + +``` + [Default] + save_dir=$HOME/Desktop + line_size=5 + text_size=20 + text_font=sans-serif +``` + +- `save_dir` is where swappshots will be saved, can contain env variables and must exist in your filesystem +- `line_size` is the default line size (must be between 1 and 50) +- `text_size` is the default text size (must be between 10 and 50) +- `text_font` is the font used to render text, its format is pango friendly + ## Keyboard Shortcuts - `Ctrl+b`: Toggle Paint Panel diff --git a/example/config b/example/config new file mode 100644 index 0000000..3411a01 --- /dev/null +++ b/example/config @@ -0,0 +1,5 @@ +[Default] +save_dir=$HOME/Desktop +line_size=5 +text_size=20 +text_font=sans-serif diff --git a/include/config.h b/include/config.h index 4d6c483..ad0f2ee 100644 --- a/include/config.h +++ b/include/config.h @@ -1,3 +1,8 @@ #include "swappy.h" -bool config_get_storage_path(struct swappy_state *state); \ No newline at end of file +#define CONFIG_LINE_SIZE_DEFAULT 5 +#define CONFIG_TEXT_FONT_DEFAULT "sans-serif" +#define CONFIG_TEXT_SIZE_DEFAULT 20 + +void config_load(struct swappy_state *state); +void config_free(struct swappy_state *state); \ No newline at end of file diff --git a/include/swappy.h b/include/swappy.h index 8b90bb8..41e2e7e 100644 --- a/include/swappy.h +++ b/include/swappy.h @@ -16,12 +16,9 @@ #define GEOMETRY_PATTERN "xx,yy wwxhh" -#define SWAPPY_STROKE_SIZE_DEFAULT 5 -#define SWAPPY_STROKE_SIZE_MIN 1 -#define SWAPPY_STROKE_SIZE_MAX 50 +#define SWAPPY_LINE_SIZE_MIN 1 +#define SWAPPY_LINE_SIZE_MAX 50 -#define SWAPPY_TEXT_FONT_DEFAULT "serif" -#define SWAPPY_TEXT_SIZE_DEFAULT 20 #define SWAPPY_TEXT_SIZE_MIN 10 #define SWAPPY_TEXT_SIZE_MAX 50 @@ -49,6 +46,7 @@ struct swappy_paint_text { double b; double a; double s; + gchar *font; gchar *text; size_t cursor; struct swappy_point from; @@ -124,7 +122,7 @@ struct swappy_state_ui { GtkRadioButton *custom; GtkColorButton *color; - GtkButton *stroke_size; + GtkButton *line_size; GtkButton *text_size; }; @@ -170,17 +168,24 @@ struct swappy_wayland { #endif }; +struct swappy_config { + char *config_file; + char *save_dir; + guint32 line_size; + guint32 text_size; + char *text_font; +}; + struct swappy_state { GtkApplication *app; struct swappy_state_ui *ui; + struct swappy_config *config; struct swappy_wayland *wl; cairo_surface_t *cairo_surface; GList *patterns; // List of cairo_pattern_t - char *storage_path; - enum swappy_paint_type mode; /* Options */ diff --git a/src/application.c b/src/application.c index a15c7d1..bdc0ef1 100644 --- a/src/application.c +++ b/src/application.c @@ -5,6 +5,7 @@ #include "buffer.h" #include "clipboard.h" +#include "config.h" #include "file.h" #include "notification.h" #include "paint.h" @@ -22,7 +23,7 @@ static void update_ui_undo_redo(struct swappy_state *state) { } static void update_ui_stroke_size_widget(struct swappy_state *state) { - GtkButton *button = GTK_BUTTON(state->ui->stroke_size); + GtkButton *button = GTK_BUTTON(state->ui->line_size); char label[255]; snprintf(label, 255, "%.0lf", state->settings.w); gtk_button_set_label(button, label); @@ -115,15 +116,15 @@ static void action_stroke_size_decrease(struct swappy_state *state) { state->settings.w -= step; - if (state->settings.w < SWAPPY_STROKE_SIZE_MIN) { - state->settings.w = SWAPPY_STROKE_SIZE_MIN; + if (state->settings.w < SWAPPY_LINE_SIZE_MIN) { + state->settings.w = SWAPPY_LINE_SIZE_MIN; } update_ui_stroke_size_widget(state); } static void action_stroke_size_reset(struct swappy_state *state) { - state->settings.w = SWAPPY_STROKE_SIZE_DEFAULT; + state->settings.w = state->config->line_size; update_ui_stroke_size_widget(state); } @@ -132,8 +133,8 @@ static void action_stroke_size_increase(struct swappy_state *state) { guint step = state->settings.w >= 10 ? 5 : 1; state->settings.w += step; - if (state->settings.w > SWAPPY_STROKE_SIZE_MAX) { - state->settings.w = SWAPPY_STROKE_SIZE_MAX; + if (state->settings.w > SWAPPY_LINE_SIZE_MAX) { + state->settings.w = SWAPPY_LINE_SIZE_MAX; } update_ui_stroke_size_widget(state); @@ -150,7 +151,7 @@ static void action_text_size_decrease(struct swappy_state *state) { update_ui_text_size_widget(state); } static void action_text_size_reset(struct swappy_state *state) { - state->settings.t = SWAPPY_TEXT_SIZE_DEFAULT; + state->settings.t = state->config->text_size; update_ui_text_size_widget(state); } static void action_text_size_increase(struct swappy_state *state) { @@ -188,7 +189,6 @@ void application_finish(struct swappy_state *state) { paint_free_all(state); buffer_free_all(state); cairo_surface_destroy(state->cairo_surface); - g_free(state->storage_path); g_free(state->file_str); g_free(state->geometry_str); g_free(state->geometry); @@ -196,6 +196,7 @@ void application_finish(struct swappy_state *state) { g_object_unref(state->app); wayland_finish(state); + config_free(state); } static void action_save_area_to_file(struct swappy_state *state) { @@ -213,7 +214,7 @@ static void action_save_area_to_file(struct swappy_state *state) { c_time_string = ctime(¤t_time); c_time_string[strlen(c_time_string) - 1] = '\0'; char path[MAX_PATH]; - snprintf(path, MAX_PATH, "%s/%s %s.png", state->storage_path, "Swappshot", + snprintf(path, MAX_PATH, "%s/%s %s.png", state->config->save_dir, "Swappshot", c_time_string); gdk_pixbuf_savev(pixbuf, path, "png", NULL, NULL, &error); @@ -561,7 +562,7 @@ static bool load_layout(struct swappy_state *state) { state->ui->color = GTK_COLOR_BUTTON(gtk_builder_get_object(builder, "custom-color-button")); - state->ui->stroke_size = + state->ui->line_size = GTK_BUTTON(gtk_builder_get_object(builder, "stroke-size-button")); state->ui->text_size = GTK_BUTTON(gtk_builder_get_object(builder, "text-size-button")); @@ -616,9 +617,21 @@ static gboolean is_file_from_stdin(const char *file) { return (strcmp(file, "-") == 0); } +static void init_settings(struct swappy_state *state) { + state->settings.r = 1; + state->settings.g = 0; + state->settings.b = 0; + state->settings.a = 1; + state->settings.w = state->config->line_size; + state->settings.t = state->config->text_size; +} + static gint command_line_handler(GtkApplication *app, GApplicationCommandLine *cmdline, struct swappy_state *state) { + config_load(state); + init_settings(state); + if (!wayland_init(state)) { g_warning( "error while initializing wayland objects, can only be used in file " @@ -688,13 +701,6 @@ bool application_init(struct swappy_state *state) { g_signal_connect(state->app, "command-line", G_CALLBACK(command_line_handler), state); - state->settings.r = 1; - state->settings.g = 0; - state->settings.b = 0; - state->settings.a = 1; - state->settings.w = SWAPPY_STROKE_SIZE_DEFAULT; - state->settings.t = SWAPPY_TEXT_SIZE_DEFAULT; - return true; } diff --git a/src/config.c b/src/config.c index 1a8fe78..2f47811 100644 --- a/src/config.c +++ b/src/config.c @@ -1,3 +1,6 @@ +#include "config.h" + +#include #include #include #include @@ -6,11 +9,21 @@ #include "file.h" #include "swappy.h" -bool config_get_storage_path(struct swappy_state *state) { +static void print_config(struct swappy_config *config) { + g_info("printing config:"); + g_info("config_dir: %s", config->config_file); + g_info("save_dir: %s", config->save_dir); + g_info("line_size: %d", config->line_size); + g_info("text_font: %s", config->text_font); + g_info("text_size: %d", config->text_size); +} + +static char *get_default_save_dir() { static const char *storage_paths[] = { "$XDG_DESKTOP_DIR", "$XDG_CONFIG_HOME/Desktop", "$HOME/Desktop", + "$HOME", }; for (size_t i = 0; i < sizeof(storage_paths) / sizeof(char *); ++i) { @@ -19,13 +32,161 @@ bool config_get_storage_path(struct swappy_state *state) { char *path = g_strdup(p.we_wordv[0]); wordfree(&p); if (path && folder_exists(path)) { - g_info("storage path is: %s", path); - state->storage_path = path; - return true; + return path; } g_free(path); } } - return false; + return NULL; } + +static char *get_config_file() { + static const char *storage_paths[] = { + "$XDG_CONFIG_HOME/swappy/config", + "$HOME/.config/swappy/config", + }; + + for (size_t i = 0; i < sizeof(storage_paths) / sizeof(char *); ++i) { + wordexp_t p; + if (wordexp(storage_paths[i], &p, 0) == 0) { + char *path = g_strdup(p.we_wordv[0]); + wordfree(&p); + if (path && file_exists(path)) { + return path; + } + g_free(path); + } + } + + return NULL; +} + +static void load_config_from_file(struct swappy_config *config, + const char *file) { + GKeyFile *gkf; + const gchar *group = "Default"; + gchar *save_dir = NULL; + gchar *save_dir_expanded = NULL; + guint64 line_size; + gchar *text_font = NULL; + guint64 text_size; + GError *error = NULL; + + if (file == NULL) { + return; + } + + gkf = g_key_file_new(); + + if (!g_key_file_load_from_file(gkf, file, G_KEY_FILE_NONE, NULL)) { + g_warning("could not read config file %s", file); + g_key_file_free(gkf); + return; + } + + save_dir = g_key_file_get_string(gkf, group, "save_dir", &error); + + if (error == NULL) { + wordexp_t p; + if (wordexp(save_dir, &p, 0) == 0) { + save_dir_expanded = g_strdup(p.we_wordv[0]); + wordfree(&p); + if (!save_dir_expanded || !folder_exists(save_dir_expanded)) { + g_warning("save_dir: %s is not a valid directory", save_dir_expanded); + } + + g_free(save_dir); + g_free(config->save_dir); + config->save_dir = save_dir_expanded; + } + } else { + g_info("save_dir is missing in %s (%s)", file, error->message); + g_error_free(error); + error = NULL; + } + + line_size = g_key_file_get_uint64(gkf, group, "line_size", &error); + + if (error == NULL) { + if (line_size >= SWAPPY_LINE_SIZE_MIN && + line_size <= SWAPPY_LINE_SIZE_MAX) { + config->line_size = line_size; + } else { + g_warning( + "line_size is not a valid value: %ld - see man page for details", + line_size); + } + } else { + g_info("line_size is missing in %s (%s)", file, error->message); + g_error_free(error); + error = NULL; + } + + text_size = g_key_file_get_uint64(gkf, group, "text_size", &error); + + if (error == NULL) { + if (text_size >= SWAPPY_TEXT_SIZE_MIN && + text_size <= SWAPPY_TEXT_SIZE_MAX) { + config->text_size = text_size; + } else { + g_warning( + "text_size is not a valid value: %ld - see man page for details", + text_size); + } + } else { + g_info("text_size is missing in %s (%s)", file, error->message); + g_error_free(error); + error = NULL; + } + + text_font = g_key_file_get_string(gkf, group, "text_font", &error); + + if (error == NULL) { + g_free(config->text_font); + config->text_font = text_font; + } else { + g_info("text_font is missing in %s (%s)", file, error->message); + g_error_free(error); + error = NULL; + } + + g_key_file_free(gkf); +} + +static void load_default_config(struct swappy_config *config) { + if (config == NULL) { + return; + } + + config->save_dir = get_default_save_dir(); + config->line_size = CONFIG_LINE_SIZE_DEFAULT; + config->text_font = g_strdup(CONFIG_TEXT_FONT_DEFAULT); + config->text_size = CONFIG_TEXT_SIZE_DEFAULT; +} + +void config_load(struct swappy_state *state) { + struct swappy_config *config = g_new(struct swappy_config, 1); + + load_default_config(config); + + char *file = get_config_file(); + if (file) { + load_config_from_file(config, file); + } else { + g_info("could not find swappy config file, using defaults"); + } + + config->config_file = file; + state->config = config; + + print_config(state->config); +} + +void config_free(struct swappy_state *state) { + g_free(state->config->config_file); + g_free(state->config->save_dir); + g_free(state->config->text_font); + g_free(state->config); + state->config = NULL; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 3bc3b66..bcdfe38 100644 --- a/src/main.c +++ b/src/main.c @@ -11,11 +11,6 @@ int main(int argc, char *argv[]) { state.argv = argv; state.mode = SWAPPY_PAINT_MODE_BRUSH; - if (!config_get_storage_path(&state)) { - g_critical("could not find a valid pictures path in your env variables"); - exit(1); - } - if (!application_init(&state)) { g_critical("failed to initialize gtk application"); exit(1); diff --git a/src/paint.c b/src/paint.c index c397ff3..ab8332a 100644 --- a/src/paint.c +++ b/src/paint.c @@ -27,6 +27,7 @@ void paint_free(gpointer data) { break; case SWAPPY_PAINT_MODE_TEXT: g_free(paint->content.text.text); + g_free(paint->content.text.font); break; default: break; @@ -111,6 +112,7 @@ void paint_add_temporary(struct swappy_state *state, double x, double y, paint->content.text.b = b; paint->content.text.a = a; paint->content.text.s = t; + paint->content.text.font = g_strdup(state->config->text_font); paint->content.text.cursor = 0; paint->content.text.mode = SWAPPY_TEXT_MODE_EDIT; paint->content.text.text = g_new(gchar, 1); diff --git a/src/render.c b/src/render.c index db4b0ee..eb7e1c3 100644 --- a/src/render.c +++ b/src/render.c @@ -40,7 +40,7 @@ static void render_text(cairo_t *cr, struct swappy_paint_text text) { pango_layout_t *layout = pango_cairo_create_layout(crt); pango_layout_set_text(layout, text.text, -1); - snprintf(pango_font, 255, "%s %d", SWAPPY_TEXT_FONT_DEFAULT, (int)text.s); + snprintf(pango_font, 255, "%s %d", text.font, (int)text.s); pango_font_description_t *desc = pango_font_description_from_string(pango_font); pango_layout_set_width(layout, pango_units_from_double(w)); diff --git a/swappy.1.scd b/swappy.1.scd index 8642068..912b5df 100644 --- a/swappy.1.scd +++ b/swappy.1.scd @@ -13,14 +13,16 @@ swappy - grab and edit on the fly snapshots of a Wayland compositor swappy is a command-line utility to take and edit screenshots of Wayland desktops. It can also work on regular X11 desktops if using the *-f* option. -swappy will save the swappshot images to a timestamped file name in -*$XDG_DESKTOP_DIR*. If this variable is not set, it will revert to: -*$XDG_CONFIG_HOME/Desktop*. If *$XDG_CONFIG_HOME* is not set, it will revert -to: *$HOME/Desktop*. - Can be used in two ways, either as the output of grim (recommended) or by grabbing the geometry (if the compositor supports the screencopy protocol). +swappy will save the swappshot images to the config *save_dir*, see below. + +If absent, then if it will try to default to a *Desktop* folder following this +pattern: *$XDG\_DESKTOP\_DIR*. If this variable is not set, it will revert to: +*$XDG\_CONFIG\_HOME/Desktop*. If *$XDG\_CONFIG\_HOME* is not set, it will revert +to: *$HOME/Desktop*. + # OPTIONS *-h* @@ -37,6 +39,31 @@ grabbing the geometry (if the compositor supports the screencopy protocol). If set to *-*, read the file from standard input instead. This is grim friendly. +# CONFIG FILE + +The config file is located at *$XDG\_CONFIG\_HOME/swappy/config* or at +*$HOME/.config/swappy/config*. The file follows the GLib *conf* format. + +``` + [Section] + key=value +``` + +The following lines can be used as swappy's default: + +``` + [Default] + save_dir=$HOME/Desktop + line_size=5 + text_size=20 + text_font=sans-serif +``` + +- *save_dir* is where swappshots will be saved, can contain env variables and must exist in your filesystem +- *line_size* is the default line size (must be between 1 and 50) +- *text_size* is the default text size (must be between 10 and 50) +- *text_font* is the font used to render text, its format is pango friendly + # KEY BINDINGS ## LAYOUT