diff options
| author | 2024-10-19 12:05:15 -0400 | |
|---|---|---|
| committer | 2024-10-19 12:13:26 -0400 | |
| commit | 42e8c4d28b6fe9cc5e041f02e0c85b390d49a218 (patch) | |
| tree | 59080eec3dd79d225bb31fb0f7e646cc22880d39 /taro | |
| parent | 2bb807dd32e1c18a6f1e8f22c25141de3334de9f (diff) | |
| download | mauri-42e8c4d28b6fe9cc5e041f02e0c85b390d49a218.tar.gz mauri-42e8c4d28b6fe9cc5e041f02e0c85b390d49a218.tar.bz2 mauri-42e8c4d28b6fe9cc5e041f02e0c85b390d49a218.zip | |
Attempt to make usable and packageable
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'taro')
| -rw-r--r-- | taro/attic/sway_background_covered.diff | 313 | ||||
| -rw-r--r-- | taro/attic/sway_test_config | 9 | ||||
| -rw-r--r-- | taro/config.h (renamed from taro/src/config.h) | 74 | ||||
| -rw-r--r-- | taro/daemon.c (renamed from taro/src/daemon.c) | 157 | ||||
| -rw-r--r-- | taro/db.c (renamed from taro/src/db.c) | 15 | ||||
| -rw-r--r-- | taro/db.h (renamed from taro/src/db.h) | 0 | ||||
| -rw-r--r-- | taro/file_ext.c (renamed from taro/src/file_ext.c) | 2 | ||||
| -rw-r--r-- | taro/file_ext.h (renamed from taro/src/file_ext.h) | 2 | ||||
| -rw-r--r-- | taro/meson.build | 27 | ||||
| -rwxr-xr-x | taro/scripts/run_wallpaper.sh | 6 | ||||
| -rw-r--r-- | taro/subprojects/libakiyo.wrap | 4 | ||||
| -rw-r--r-- | taro/subprojects/libalabaster.wrap | 4 | ||||
| -rw-r--r-- | taro/subprojects/notcurses.wrap | 12 | ||||
| -rw-r--r-- | taro/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff | 166 | ||||
| -rw-r--r-- | taro/subprojects/stela.wrap | 4 | ||||
| -rw-r--r-- | taro/ui.c (renamed from taro/src/ui.c) | 351 |
16 files changed, 345 insertions, 801 deletions
diff --git a/taro/attic/sway_background_covered.diff b/taro/attic/sway_background_covered.diff deleted file mode 100644 index 0dc9401..0000000 --- a/taro/attic/sway_background_covered.diff +++ /dev/null @@ -1,313 +0,0 @@ -diff --git a/include/ipc.h b/include/ipc.h -index ff011750..e417b421 100644 ---- a/include/ipc.h -+++ b/include/ipc.h -@@ -29,9 +29,10 @@ enum ipc_command_type { - IPC_EVENT_MODE = ((1<<31) | 2), - IPC_EVENT_WINDOW = ((1<<31) | 3), - IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), -- IPC_EVENT_BINDING = ((1<<31) | 5), -- IPC_EVENT_SHUTDOWN = ((1<<31) | 6), -- IPC_EVENT_TICK = ((1<<31) | 7), -+ IPC_EVENT_BACKGROUND_COVERED = ((1<<31) | 5), -+ IPC_EVENT_BINDING = ((1<<31) | 6), -+ IPC_EVENT_SHUTDOWN = ((1<<31) | 7), -+ IPC_EVENT_TICK = ((1<<31) | 8), - - // sway-specific event types - IPC_EVENT_BAR_STATE_UPDATE = ((1<<31) | 20), -diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h -index d4c00942..41e00679 100644 ---- a/include/sway/ipc-server.h -+++ b/include/sway/ipc-server.h -@@ -17,6 +17,7 @@ void ipc_event_workspace(struct sway_workspace *old, - void ipc_event_window(struct sway_container *window, const char *change); - void ipc_event_barconfig_update(struct bar_config *bar); - void ipc_event_bar_state_update(struct bar_config *bar); -+void ipc_event_background_covered(struct sway_output *output, bool covered); - void ipc_event_mode(const char *mode, bool pango); - void ipc_event_shutdown(const char *reason); - void ipc_event_binding(struct sway_binding *binding); -diff --git a/include/sway/output.h b/include/sway/output.h -index 4584ea45..83e3cd7f 100644 ---- a/include/sway/output.h -+++ b/include/sway/output.h -@@ -53,6 +53,8 @@ struct sway_output { - bool enabled; - list_t *workspaces; - -+ bool background_covered; -+ - struct sway_output_state current; - - struct wl_listener layout_destroy; -diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h -index 58bde20c..1fcd5017 100644 ---- a/include/sway/tree/workspace.h -+++ b/include/sway/tree/workspace.h -@@ -40,6 +40,9 @@ struct sway_workspace { - enum sway_container_layout layout; - enum sway_container_layout prev_split_layout; - -+ bool background_covered; -+ bool pending_background_check; -+ - struct side_gaps current_gaps; - int gaps_inner; - struct side_gaps gaps_outer; -@@ -47,6 +50,7 @@ struct sway_workspace { - struct sway_output *output; // NULL if no outputs are connected - list_t *floating; // struct sway_container - list_t *tiling; // struct sway_container -+ list_t *sorted; // struct sway_container - list_t *output_priority; - bool urgent; - -@@ -89,6 +93,8 @@ bool workspace_is_visible(struct sway_workspace *ws); - - bool workspace_is_empty(struct sway_workspace *ws); - -+void workspace_check_background_covered(struct sway_workspace *ws); -+ - void workspace_output_raise_priority(struct sway_workspace *workspace, - struct sway_output *old_output, struct sway_output *new_output); - -diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c -index 8f12832a..9869c934 100644 ---- a/sway/desktop/transaction.c -+++ b/sway/desktop/transaction.c -@@ -702,30 +702,60 @@ static void transaction_apply(struct sway_transaction *transaction) { - "(%.1f frames if 60Hz)", transaction, ms, ms / (1000.0f / 60)); - } - -+ struct sway_transaction_instruction *instruction; -+ struct sway_node *node; -+ struct sway_workspace *ws = NULL; -+ - // Apply the instruction state to the node's current state - for (int i = 0; i < transaction->instructions->length; ++i) { -- struct sway_transaction_instruction *instruction = -- transaction->instructions->items[i]; -- struct sway_node *node = instruction->node; -+ instruction = transaction->instructions->items[i]; -+ node = instruction->node; - - switch (node->type) { - case N_ROOT: -+ ws = NULL; - break; - case N_OUTPUT: - apply_output_state(node->sway_output, &instruction->output_state); -+ ws = node->sway_output->current.active_workspace; - break; - case N_WORKSPACE: - apply_workspace_state(node->sway_workspace, - &instruction->workspace_state); -+ ws = node->sway_workspace; - break; - case N_CONTAINER: - apply_container_state(node->sway_container, - &instruction->container_state); -+ ws = node->sway_container->current.workspace; - break; - } - -+ if (ws) ws->pending_background_check = true; -+ - node->instruction = NULL; - } -+ -+ for (int i = 0; i < transaction->instructions->length; ++i) { -+ instruction = transaction->instructions->items[i]; -+ node = instruction->node; -+ -+ switch (node->type) { -+ case N_ROOT: -+ continue; -+ case N_OUTPUT: -+ ws = node->sway_output->current.active_workspace; -+ break; -+ case N_WORKSPACE: -+ ws = node->sway_workspace; -+ break; -+ case N_CONTAINER: -+ ws = node->sway_container->current.workspace; -+ break; -+ } -+ -+ if (ws) workspace_check_background_covered(ws); -+ } - } - - static void transaction_commit_pending(void); -diff --git a/sway/ipc-json.c b/sway/ipc-json.c -index 571338a4..f9bdcfae 100644 ---- a/sway/ipc-json.c -+++ b/sway/ipc-json.c -@@ -16,6 +16,7 @@ - #include "sway/tree/view.h" - #include "sway/tree/workspace.h" - #include "sway/output.h" -+#include "sway/layers.h" - #include "sway/input/input-manager.h" - #include "sway/input/cursor.h" - #include "sway/input/seat.h" -@@ -360,6 +361,9 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, - json_object_object_add(object, "current_workspace", - json_object_new_string(ws->name)); - -+ json_object_object_add(object, "covered", -+ json_object_new_boolean(ws->background_covered)); -+ - json_object *modes_array = json_object_new_array(); - struct wlr_output_mode *mode; - wl_list_for_each(mode, &wlr_output->modes, link) { -diff --git a/sway/ipc-server.c b/sway/ipc-server.c -index 7f353c0e..f3093505 100644 ---- a/sway/ipc-server.c -+++ b/sway/ipc-server.c -@@ -362,6 +362,20 @@ void ipc_event_bar_state_update(struct bar_config *bar) { - json_object_put(json); - } - -+void ipc_event_background_covered(struct sway_output *output, bool covered) { -+ if (!ipc_has_event_listeners(IPC_EVENT_BACKGROUND_COVERED)) { -+ return; -+ } -+ sway_log(SWAY_DEBUG, "Sending background_covered event"); -+ -+ json_object *json = json_object_new_object(); -+ json_object_object_add(json, "output", json_object_new_string(output->wlr_output->name)); -+ json_object_object_add(json, "covered", json_object_new_boolean(covered)); -+ const char *json_string = json_object_to_json_string(json); -+ ipc_send_event(json_string, IPC_EVENT_BACKGROUND_COVERED); -+ json_object_put(json); -+} -+ - void ipc_event_mode(const char *mode, bool pango) { - if (!ipc_has_event_listeners(IPC_EVENT_MODE)) { - return; -@@ -744,6 +758,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt - client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); - } else if (strcmp(event_type, "bar_state_update") == 0) { - client->subscribed_events |= event_mask(IPC_EVENT_BAR_STATE_UPDATE); -+ } else if (strcmp(event_type, "background_covered") == 0) { -+ client->subscribed_events |= event_mask(IPC_EVENT_BACKGROUND_COVERED); - } else if (strcmp(event_type, "mode") == 0) { - client->subscribed_events |= event_mask(IPC_EVENT_MODE); - } else if (strcmp(event_type, "shutdown") == 0) { -diff --git a/sway/tree/output.c b/sway/tree/output.c -index 44b941ca..9004c3cb 100644 ---- a/sway/tree/output.c -+++ b/sway/tree/output.c -@@ -142,6 +142,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { - - output->workspaces = create_list(); - output->current.workspaces = create_list(); -+ output->background_covered = false; - - return output; - } -diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c -index f8709a4c..36ea5fc0 100644 ---- a/sway/tree/workspace.c -+++ b/sway/tree/workspace.c -@@ -88,8 +88,12 @@ struct sway_workspace *workspace_create(struct sway_output *output, - ws->layout = output_get_default_layout(output); - ws->floating = create_list(); - ws->tiling = create_list(); -+ ws->sorted = create_list(); - ws->output_priority = create_list(); - -+ ws->background_covered = false; -+ ws->pending_background_check = true; -+ - ws->gaps_outer = config->gaps_outer; - ws->gaps_inner = config->gaps_inner; - if (name) { -@@ -153,6 +157,7 @@ void workspace_destroy(struct sway_workspace *workspace) { - list_free_items_and_destroy(workspace->output_priority); - list_free(workspace->floating); - list_free(workspace->tiling); -+ list_free(workspace->sorted); - list_free(workspace->current.floating); - list_free(workspace->current.tiling); - free(workspace); -@@ -616,6 +621,77 @@ bool workspace_is_empty(struct sway_workspace *ws) { - return true; - } - -+static bool x_covered_at_y(list_t *containers, double y, double x, double width) -+{ -+ double end = x + width; -+ double extent = x; -+ for (int i = 0; i < containers->length; ++i) { -+ struct sway_container *n = containers->items[i]; -+ if (!(n->current.y <= y && n->current.y + n->current.height >= y)) { -+ continue; -+ } -+ if (n->current.x > extent) return false; -+ x = n->current.x + n->current.width; -+ if (x >= end) return true; -+ if (x > extent) extent = x; -+ } -+ return false; -+} -+ -+static int sort_containers_x_cmp(const void *_a, const void *_b) { -+ struct sway_container *a = *(void **)_a; -+ struct sway_container *b = *(void **)_b; -+ return a->current.x - b->current.x; -+} -+ -+#define TERMINAL_APP_ID "kitty" -+ -+static void add_con_and_or_children_to_list(list_t *result, list_t *list) -+{ -+ struct sway_container *con; -+ for (int i = 0; i < list->length; ++i) { -+ con = list->items[i]; -+ if (con->current.children) { -+ add_con_and_or_children_to_list(result, con->current.children); -+ if (con->current.layout != L_NONE) return; -+ } -+ if (!con->view || !con->view->wlr_xdg_toplevel) continue; // Hack. -+ const char *app_id = view_get_app_id(con->view); -+ if (app_id == NULL || strcmp(app_id, TERMINAL_APP_ID) != 0) { -+ list_add(result, con); -+ } -+ } -+} -+ -+void workspace_check_background_covered(struct sway_workspace *ws) { -+ if (!ws->output || !ws->pending_background_check) return; -+ ws->sorted->length = 0; -+ add_con_and_or_children_to_list(ws->sorted, ws->tiling); -+ add_con_and_or_children_to_list(ws->sorted, ws->floating); -+ list_stable_sort(ws->sorted, sort_containers_x_cmp); -+ ws->background_covered = ws->sorted->length > 0; -+ struct sway_container *con; -+ for (int i = 0; i < ws->sorted->length; ++i) { -+ con = ws->sorted->items[i]; -+ double y = con->current.y - 1.0; -+ if (y < ws->y) y = ws->y; -+ double span = con->current.y + con->current.height + 1.0; -+ if (span > ws->y + ws->height) span = ws->y + ws->height; -+ bool top = x_covered_at_y(ws->sorted, y, ws->x, ws->width); -+ if (!top || !x_covered_at_y(ws->sorted, span, ws->x, ws->width)) { -+ ws->background_covered = false; -+ break; -+ } -+ } -+ if (ws == ws->output->current.active_workspace) { -+ if (ws->background_covered != ws->output->background_covered) { -+ ws->output->background_covered = ws->background_covered; -+ ipc_event_background_covered(ws->output, ws->output->background_covered); -+ } -+ ws->pending_background_check = false; -+ } -+} -+ - static int find_output(const void *id1, const void *id2) { - return strcmp(id1, id2); - } diff --git a/taro/attic/sway_test_config b/taro/attic/sway_test_config deleted file mode 100644 index 1223428..0000000 --- a/taro/attic/sway_test_config +++ /dev/null @@ -1,9 +0,0 @@ -workspace "1" output "WL-1" -workspace "2" output "WL-1" -bindsym Mod4+Return exec kitty -bindsym Mod4+Shift+q kill -bindsym Mod2+1 workspace 1 -bindsym Mod2+2 workspace 2 -bindsym Mod4+v split v -floating_modifier Mod4 -bindsym Mod4+space floating toggle diff --git a/taro/src/config.h b/taro/config.h index ddf3430..1a1f10d 100644 --- a/taro/src/config.h +++ b/taro/config.h @@ -1,6 +1,7 @@ #pragma once #include <al/str.h> +#include <al/log.h> #include "file_ext.h" @@ -19,37 +20,15 @@ static void close_config(struct config *conf) al_str_free(&conf->wallpaper_dir); } -static bool open_config(struct config *conf) +static bool parse_config_json(struct config *conf, char *envhome, json_t *root) { - conf->config_dir = al_str_zero(); - conf->wallpaper_dir = al_str_zero(); - - char *envhome = getenv("HOME"); - if (!envhome) return false; - al_str_from(&conf->config_dir, envhome); - al_str_cat(&conf->config_dir, al_str_c(CONFIG_PATH)); - if (!aki_dir_exists(&conf->config_dir)) { - if (!aki_dir_create(&conf->config_dir)) { - close_config(conf); - return false; - } - } - - str config_path; - al_str_clone(&config_path, &conf->config_dir); - al_str_cat(&config_path, al_str_c("/config.json")); - json_t *root = aki_file_open_as_json(&config_path); - al_str_free(&config_path); - if (!root) { - close_config(conf); - return false; - } + bool valid = true; json_t *wallpaper_dir = json_object_get(root, "wallpaper_directory"); - if (!wallpaper_dir) { - json_decref(root); - close_config(conf); - return false; + if (!wallpaper_dir || !json_is_string(wallpaper_dir) || al_strlen(json_string_value(wallpaper_dir)) == 0) { + al_log_error("taro", "'wallpaper_directory' not set or invalid"); + valid = false; + goto out; } const char *dir_str = json_string_value(wallpaper_dir); @@ -61,9 +40,48 @@ static bool open_config(struct config *conf) al_str_from(&conf->wallpaper_dir, dir_str); } +out: json_decref(root); + return valid; +} + +static bool open_config(struct config *conf, bool create) +{ + al_memset(conf, 0, sizeof(struct config)); + + char *envhome = getenv("HOME"); + if (!envhome) goto err; + + al_str_from(&conf->config_dir, envhome); + al_str_cat(&conf->config_dir, al_str_c(CONFIG_PATH)); + if (!aki_dir_exists(&conf->config_dir)) { + if (!create || !aki_dir_create(&conf->config_dir)) { + goto err; + } + } + + str config_path; + al_str_clone(&config_path, &conf->config_dir); + al_str_cat(&config_path, al_str_c("/config.json")); + if (!aki_file_exists(&config_path)) { + if (aki_file_create(&config_path)) { + json_t *blank = json_object(); + json_object_set_new(blank, "wallpaper_directory", json_string_nocheck("")); + aki_dump_json_and_decref(&config_path, blank); + } + } + + json_t *root = aki_file_open_as_json(&config_path); + al_str_free(&config_path); + if (!root) goto err; + + if (!parse_config_json(conf, envhome, root)) goto err; + return true; +err: + close_config(conf); + return false; } AL_UNUSED_FUNCTION_POP diff --git a/taro/src/daemon.c b/taro/daemon.c index 45eb599..d632e3d 100644 --- a/taro/src/daemon.c +++ b/taro/daemon.c @@ -5,6 +5,7 @@ #include <al/str.h> #include <aki/common.h> #include <aki/event_loop.h> +#include <aki/signal.h> #include <aki/fs_event.h> #include <aki/socket.h> #include <stl/window.h> @@ -25,11 +26,12 @@ struct monitor { struct daemon *dmon; }; -#define IPC_HEADER_LENGTH 14 -#define IPC_MESSAGE_BUF_SIZE 64 +#define SWAY_IPC_HEADER_LENGTH 14 +#define SWAY_IPC_SUBSCRIBE 2 struct daemon { struct aki_event_loop loop; + struct aki_signal quit_signal; struct aki_poll context_poll; array(struct monitor *) monitors; struct aki_socket ipc_socket; @@ -86,6 +88,16 @@ static bool send_signal_to_pid(pid_t pid, s32 signal) return true; } +static bool signal_monitor(struct monitor *monitor, s32 signal) +{ + if (monitor->pid == -1) return false; + if (!send_signal_to_pid(monitor->pid, signal)) { + monitor->pid = -1; + return false; + } + return true; +} + static pid_t exec_or_exit(char *args[]) { pid_t pid = fork(); @@ -98,6 +110,7 @@ static pid_t exec_or_exit(char *args[]) // This should return the pid of init if the parent process has already exited. pid_t ppid = getppid(); if (ppid != 1) send_signal_to_pid(ppid, SIGINT); + exit(EXIT_FAILURE); } } return pid; @@ -108,13 +121,11 @@ static void maybe_kill_wallpaper(struct monitor *monitor) if (monitor->pid == -1) return; if (monitor->paused) { - if (!send_signal_to_pid(monitor->pid, SIGCONT)) { - send_signal_to_pid(monitor->pid, SIGKILL); - } + signal_monitor(monitor, SIGCONT); monitor->paused = false; } - send_signal_to_pid(monitor->pid, SIGTERM); + signal_monitor(monitor, SIGTERM); monitor->pid = -1; } @@ -149,19 +160,28 @@ static bool run_wallpaper(struct daemon *dmon, struct monitor *monitor) return false; } + if (is_package) { + al_str_cat(&wallpaper_path, al_str_c("/scene.pkg")); + } + al_log_info("tarod", "setting wallpaper %.*s on %s", AL_STR_PRINTF(&wallpaper_path), monitor->os->name); char width[8], height[8]; - al_sprintf(width, "%d", monitor->os->width / monitor->os->scale); - al_sprintf(height, "%d", monitor->os->height / monitor->os->scale); + al_snprintf(width, sizeof(width), "%d", monitor->os->width / monitor->os->scale); + al_snprintf(height, sizeof(height), "%d", monitor->os->height / monitor->os->scale); char *c_str0 = al_str_to_c_str(&wallpaper_path); al_str_free(&wallpaper_path); - monitor->pid = exec_or_exit((char *[]){ "./run_wallpaper.sh", - AL_BOOLSTR(is_package), c_str0, monitor->os->name, width, height, NULL }); + if (is_package) { + monitor->pid = exec_or_exit((char *[]){ + "mauri", "-p", c_str0, "-m", monitor->os->name, "-w", width, "-h", height, NULL}); + } else { + monitor->pid = exec_or_exit((char *[]){ + "mauri", "-d", c_str0, "-m", monitor->os->name, "-w", width, "-h", height, NULL}); + } al_free(c_str0); - if (monitor->covered && send_signal_to_pid(monitor->pid, SIGSTOP)) { + if (monitor->covered && signal_monitor(monitor, SIGSTOP)) { monitor->paused = true; } @@ -194,13 +214,13 @@ static void output_discovered_callback(void *userdata, struct stl_monitor *os_mo } } - str config_dir; - al_str_clone(&config_dir, &dmon->conf.config_dir); - al_str_cat(&config_dir, al_str_c("/")); - al_str_cat(&config_dir, al_str_cr(os_monitor->name)); + str monitor_dir; + al_str_clone(&monitor_dir, &dmon->conf.config_dir); + al_str_cat(&monitor_dir, al_str_c("/")); + al_str_cat(&monitor_dir, al_str_cr(os_monitor->name)); - if (!aki_dir_exists(&config_dir)) { - if (!aki_dir_create(&config_dir)) return; + if (!aki_dir_exists(&monitor_dir)) { + if (!aki_dir_create(&monitor_dir)) return; } monitor = al_alloc_object(struct monitor); @@ -208,8 +228,8 @@ static void output_discovered_callback(void *userdata, struct stl_monitor *os_mo monitor->dmon = dmon; al_array_push(dmon->monitors, monitor); - al_str_clone(&monitor->config_dir, &config_dir); - al_str_free(&config_dir); + al_str_clone(&monitor->config_dir, &monitor_dir); + al_str_free(&monitor_dir); al_str_clone(&monitor->selection_path, &monitor->config_dir); al_str_cat(&monitor->selection_path, al_str_c("/selection")); @@ -273,9 +293,9 @@ static void ipc_handle_response(struct daemon *dmon, u8 *buf, u32 size) if (strcmp(monitor->os->name, json_string_value(output)) != 0) continue; monitor->covered = covered; if (monitor->pid == -1) goto done; - if (covered && !monitor->paused && send_signal_to_pid(monitor->pid, SIGSTOP)) { + if (covered && !monitor->paused && signal_monitor(monitor, SIGSTOP)) { monitor->paused = true; - } else if (!covered && monitor->paused && send_signal_to_pid(monitor->pid, SIGCONT)) { + } else if (!covered && monitor->paused && signal_monitor(monitor, SIGCONT)) { monitor->paused = false; } break; @@ -289,7 +309,7 @@ done: static void ensure_ipc_buf_size(struct daemon *dmon, ssize_t size) { if (dmon->ipc.alloc < size) { - dmon->ipc.buf = realloc(dmon->ipc.buf, size); + dmon->ipc.buf = al_realloc(dmon->ipc.buf, size); dmon->ipc.alloc = size; } } @@ -316,20 +336,20 @@ static void ipc_poll_callback(void *userdata, s32 revents) } if (revents & AKI_POLL_READ) { - if (dmon->ipc.index < IPC_HEADER_LENGTH) { + if (dmon->ipc.index < SWAY_IPC_HEADER_LENGTH) { dmon->ipc.index += aki_socket_read(&dmon->ipc_socket, dmon->ipc.buf + dmon->ipc.index, - IPC_HEADER_LENGTH - dmon->ipc.index); + SWAY_IPC_HEADER_LENGTH - dmon->ipc.index); } - if (dmon->ipc.index == IPC_HEADER_LENGTH && !dmon->ipc.size) { - dmon->ipc.size = *((s32 *)&dmon->ipc.buf[6]) + IPC_HEADER_LENGTH; + if (dmon->ipc.index == SWAY_IPC_HEADER_LENGTH && !dmon->ipc.size) { + dmon->ipc.size = *((s32 *)&dmon->ipc.buf[6]) + SWAY_IPC_HEADER_LENGTH; ensure_ipc_buf_size(dmon, dmon->ipc.size); } if (dmon->ipc.size) { dmon->ipc.index += aki_socket_read(&dmon->ipc_socket, dmon->ipc.buf + dmon->ipc.index, dmon->ipc.size - dmon->ipc.index); if (dmon->ipc.index == dmon->ipc.size) { - ipc_handle_response(dmon, dmon->ipc.buf + IPC_HEADER_LENGTH, - dmon->ipc.size - IPC_HEADER_LENGTH); + ipc_handle_response(dmon, dmon->ipc.buf + SWAY_IPC_HEADER_LENGTH, + dmon->ipc.size - SWAY_IPC_HEADER_LENGTH); dmon->ipc.size = 0; dmon->ipc.index = 0; } @@ -344,39 +364,56 @@ static void context_poll_callback(void *userdata, s32 revents) stl_global_process_events(); } +static void quit_signal_callback(void *userdata) +{ + struct daemon *dmon = (struct daemon *)userdata; + aki_event_loop_break_one(&dmon->loop); +} + +static struct daemon dmon = { 0 }; // https://stackoverflow.com/a/59071370 -static void handle_sigchld(s32 sig) +static void sigchld_handler(s32 signum) { - (void)sig; + (void)signum; s32 saved_errno = errno; while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {} errno = saved_errno; } -#define SWAY_IPC_SUBSCRIBE 2 +static void sigint_handler(s32 signum) +{ + (void)signum; + aki_signal_send(&dmon.quit_signal); +} -static struct daemon dmon = { 0 }; +// TODO: +// - daemon quit +// - systemd? and related testing s32 main(void) { - if (!aki_common_init()) return EXIT_FAILURE; + if (!aki_common_init() || !open_config(&dmon.conf, true)) return EXIT_FAILURE; - signal(SIGCHLD, handle_sigchld); + signal(SIGINT, sigint_handler); + signal(SIGCHLD, sigchld_handler); s32 ret = EXIT_FAILURE; - if (!open_config(&dmon.conf)) goto out; - aki_event_loop_init(&dmon.loop); - al_array_init(dmon.monitors); + aki_signal_init(&dmon.quit_signal, quit_signal_callback, &dmon); + aki_signal_start(&dmon.quit_signal, &dmon.loop); + al_array_init(dmon.monitors); stl_set_output_discovered_callback(output_discovered_callback, &dmon); - if (!stl_global_init()) return EXIT_FAILURE; - aki_poll_init(&dmon.context_poll, context_poll_callback, &dmon); - aki_poll_set(&dmon.context_poll, stl_global_get_poll_fd(), AKI_POLL_READ); - aki_poll_start(&dmon.context_poll, &dmon.loop); + if (!stl_global_init(true)) goto out; + s32 poll_fd = stl_global_get_poll_fd(); + if (poll_fd != -1) { + aki_poll_init(&dmon.context_poll, context_poll_callback, &dmon); + aki_poll_set(&dmon.context_poll, poll_fd, AKI_POLL_READ); + aki_poll_start(&dmon.context_poll, &dmon.loop); + } str monitors; al_str_from(&monitors, ""); @@ -389,28 +426,35 @@ s32 main(void) str monitors_path; al_str_clone(&monitors_path, &dmon.conf.config_dir); al_str_cat(&monitors_path, al_str_c("/monitors")); - if (!aki_file_exists(&monitors_path)) { - aki_file_create(&monitors_path); + bool monitors_exists = aki_file_exists(&monitors_path) || aki_file_create(&monitors_path); + if (monitors_exists) { + aki_file_open_and_replace(&monitors_path, &monitors, false); } - aki_file_open_and_replace(&monitors_path, &monitors, false); + al_str_free(&monitors_path); + al_str_free(&monitors); + if (!monitors_exists) goto out; dmon.ipc.buf = NULL; dmon.ipc.size = 0; dmon.ipc.index = 0; dmon.ipc.alloc = 0; - ensure_ipc_buf_size(&dmon, IPC_HEADER_LENGTH); + ensure_ipc_buf_size(&dmon, SWAY_IPC_HEADER_LENGTH); - json_t *payload = json_array(); - json_array_append(payload, json_string("background_covered")); - dmon.out.buf = al_malloc(IPC_MESSAGE_BUF_SIZE); - al_memcpy(dmon.out.buf, "i3-ipc", strlen("i3-ipc")); + size_t bufsize = SWAY_IPC_HEADER_LENGTH + 64; + dmon.out.buf = al_malloc(bufsize); + al_memcpy(dmon.out.buf, "i3-ipc", al_strlen("i3-ipc")); s32 type = SWAY_IPC_SUBSCRIBE; al_memcpy(&dmon.out.buf[10], &type, sizeof(s32)); - s32 len = json_dumpb(payload, (char *)&dmon.out.buf[14], IPC_MESSAGE_BUF_SIZE - IPC_HEADER_LENGTH, 0); + + json_t *payload = json_array(); + json_array_append_new(payload, json_string_nocheck("background_covered")); + s32 len = json_dumpb(payload, (char *)&dmon.out.buf[SWAY_IPC_HEADER_LENGTH], + bufsize - SWAY_IPC_HEADER_LENGTH, 0); + json_decref(payload); al_memcpy(&dmon.out.buf[6], &len, sizeof(s32)); - dmon.out.size = IPC_HEADER_LENGTH + len; + + dmon.out.size = SWAY_IPC_HEADER_LENGTH + len; dmon.out.index = 0; - json_decref(payload); dmon.ipc_socket.type = AKI_SOCKET_UNIX; aki_socket_init(&dmon.ipc_socket); @@ -422,7 +466,7 @@ s32 main(void) if (swaysock && aki_socket_connect(&dmon.ipc_socket, al_str_cr(swaysock), 0)) { aki_poll_start(&dmon.ipc_poll, &dmon.loop); } else { - al_log_error("tarod", "failed to connect to sway ipc socket"); + al_log_warn("tarod", "couldn't connect to sway ipc socket"); } aki_event_loop_run(&dmon.loop); @@ -431,15 +475,18 @@ s32 main(void) close_monitor(monitor); } al_array_free(dmon.monitors); + al_free(dmon.ipc.buf); + aki_socket_close(&dmon.ipc_socket); aki_event_loop_destroy(&dmon.loop); - close_config(&dmon.conf); - ret = EXIT_SUCCESS; out: + close_config(&dmon.conf); + + stl_global_close(); aki_common_close(); return ret; diff --git a/taro/src/db.c b/taro/db.c index 9d2ffa2..5202df8 100644 --- a/taro/src/db.c +++ b/taro/db.c @@ -147,17 +147,14 @@ bool db_ensure_entry_loaded(struct db *db, struct entry *e) return e->loaded; } -static s32 id_compare(void *a, void *b) +static s32 wallpaper_id_compare(const void *a, const void *b) { + // Non-integer ids will be at the end. s64 aid = al_str_to_long(&((struct entry *)a)->id, 10); s64 bid = al_str_to_long(&((struct entry *)b)->id, 10); - if (aid > bid) { - return 1; - } else if (aid < bid) { - return -1; - } else { - return 0; - } + if (aid > bid) return 1; + else if (aid < bid) return -1; + return 0; } bool db_load_entries(struct db *db, str *path) @@ -189,7 +186,7 @@ bool db_load_entries(struct db *db, str *path) al_array_push(db->entries, e); } - al_array_sort(db->entries, struct entry, id_compare); + al_array_sort(db->entries, struct entry, wallpaper_id_compare); return true; } diff --git a/taro/src/db.h b/taro/db.h index 78f4c77..78f4c77 100644 --- a/taro/src/db.h +++ b/taro/db.h diff --git a/taro/src/file_ext.c b/taro/file_ext.c index 48dffce..3091fa4 100644 --- a/taro/src/file_ext.c +++ b/taro/file_ext.c @@ -39,7 +39,7 @@ json_t *aki_file_open_as_json(str *path) return root; } -bool aki_file_dump_and_decref_json(str *path, json_t *root) +bool aki_dump_json_and_decref(str *path, json_t *root) { str s; size_t size = json_dumpb(root, NULL, 0, JSON_INDENT(4)); diff --git a/taro/src/file_ext.h b/taro/file_ext.h index ddaceba..9398eb3 100644 --- a/taro/src/file_ext.h +++ b/taro/file_ext.h @@ -4,4 +4,4 @@ bool aki_file_open_and_read_wholly(str *path, str *out, bool lock); bool aki_file_open_and_replace(str *path, str *buf, bool lock); json_t *aki_file_open_as_json(str *path); -bool aki_file_dump_and_decref_json(str *path, json_t *root); +bool aki_dump_json_and_decref(str *path, json_t *root); diff --git a/taro/meson.build b/taro/meson.build index 0291648..935678e 100644 --- a/taro/meson.build +++ b/taro/meson.build @@ -1,10 +1,3 @@ -project('taro', 'c', version: '0.01', - default_options: ['warning_level=2', 'c_std=c99']) - -cmake = import('cmake') - -is_debug = get_option('buildtype').startswith('debug') - notcurses = dependency('notcurses', required: false, allow_fallback: false) notcurses_core = dependency('notcurses-core', required: false, allow_fallback: false) if not notcurses.found() or not notcurses_core.found() @@ -19,27 +12,11 @@ if not notcurses.found() or not notcurses_core.found() notcurses_core = notcurses_proj.dependency('notcurses-core') endif -akiyo = subproject('libakiyo', - default_options: [ - 'curl=disabled', - 'json=enabled', - 'event-loop=enabled' - ]).get_variable('akiyo') - -stela = subproject('stela', - default_options: [ - 'api=null', - 'window=wayland', - 'poll=fd', - 'event-buffer=false', - 'pause=false' - ]).get_variable('stela') - taro_deps = [akiyo, notcurses, notcurses_core] -taro_src = ['src/ui.c', 'src/db.c', 'src/file_ext.c'] +taro_src = ['ui.c', 'db.c', 'file_ext.c'] tarod_deps = [akiyo, stela] -tarod_src = ['src/daemon.c', 'src/file_ext.c'] +tarod_src = ['daemon.c', 'file_ext.c'] executable('taro', taro_src, dependencies: taro_deps, install: true) executable('tarod', tarod_src, dependencies: tarod_deps, install: true) diff --git a/taro/scripts/run_wallpaper.sh b/taro/scripts/run_wallpaper.sh deleted file mode 100755 index a274889..0000000 --- a/taro/scripts/run_wallpaper.sh +++ /dev/null @@ -1,6 +0,0 @@ -#! /usr/bin/env sh -if [ "$1" = "true" ]; then - exec mauri -p $2/scene.pkg -m $3 -w $4 -h $5 -else - exec mauri -d $2 -m $3 -w $4 -h $5 -fi diff --git a/taro/subprojects/libakiyo.wrap b/taro/subprojects/libakiyo.wrap deleted file mode 100644 index 2cd2caa..0000000 --- a/taro/subprojects/libakiyo.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url = https://git.akon.city/libakiyo -revision = master -depth = 1 diff --git a/taro/subprojects/libalabaster.wrap b/taro/subprojects/libalabaster.wrap deleted file mode 100644 index c0358cf..0000000 --- a/taro/subprojects/libalabaster.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url = https://git.akon.city/libalabaster -revision = master -depth = 1 diff --git a/taro/subprojects/notcurses.wrap b/taro/subprojects/notcurses.wrap deleted file mode 100644 index 64f6731..0000000 --- a/taro/subprojects/notcurses.wrap +++ /dev/null @@ -1,12 +0,0 @@ -[wrap-git] -url = https://github.com/dankamongmen/notcurses.git -revision = 9377370b59e31f3f3adde6eaa89aa875dd84377f -depth = 1 - -patch_directory = notcurses -diff_files = notcurses/duration_and_ffmpeg_fix.diff - -method = cmake - -[provide] -dependency_names = notcurses, notcurses-core diff --git a/taro/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff b/taro/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff deleted file mode 100644 index 71680f7..0000000 --- a/taro/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff +++ /dev/null @@ -1,166 +0,0 @@ -diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h -index 9e09cb3..f4da60f 100644 ---- a/include/notcurses/notcurses.h -+++ b/include/notcurses/notcurses.h -@@ -3406,6 +3406,10 @@ API int ncvisual_geom(const struct notcurses* nc, const struct ncvisual* n, - const struct ncvisual_options* vopts, ncvgeom* geom) - __attribute__ ((nonnull (4))); - -+ -+API uint64_t ncvisual_get_duration(const struct ncvisual* n) -+ __attribute__ ((nonnull (1))); -+ - // Destroy an ncvisual. Rendered elements will not be disrupted, but the visual - // can be neither decoded nor rendered any further. - API void ncvisual_destroy(struct ncvisual* ncv); -diff --git a/src/lib/visual-details.h b/src/lib/visual-details.h -index e9347e3..6852b76 100644 ---- a/src/lib/visual-details.h -+++ b/src/lib/visual-details.h -@@ -25,6 +25,7 @@ typedef struct ncvisual { - unsigned pixx, pixy; // pixel geometry, *not* cell geometry - // lines are sometimes padded. this many true bytes per row in data. - unsigned rowstride; -+ uint64_t duration; - bool owndata; // we own data iff owndata == true - } ncvisual; - -diff --git a/src/lib/visual.c b/src/lib/visual.c -index 047b68e..25e8422 100644 ---- a/src/lib/visual.c -+++ b/src/lib/visual.c -@@ -458,6 +458,10 @@ int ncvisual_geom(const notcurses* nc, const ncvisual* n, - &disppxy, &disppxx, &outy, &outx, &placey, &placex); - } - -+uint64_t ncvisual_get_duration(const ncvisual* n){ -+ return n->duration; -+} -+ - void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){ - if(*rowstride % 4){ // must be a multiple of 4 bytes - return NULL; -diff --git a/src/media/ffmpeg.c b/src/media/ffmpeg.c -index 655f269..d2bf399 100644 ---- a/src/media/ffmpeg.c -+++ b/src/media/ffmpeg.c -@@ -33,6 +33,7 @@ typedef struct ncvisual_details { - struct SwsContext* swsctx; - struct SwsContext* rgbactx; - AVSubtitle subtitle; -+ AVRational timebase; - int stream_index; // match against this following av_read_frame() - int sub_stream_index; // subtitle stream index, can be < 0 if no subtitles - bool packet_outstanding; -@@ -224,9 +225,7 @@ struct ncplane* ffmpeg_subtitle(ncplane* parent, const ncvisual* ncv){ - - static int - averr2ncerr(int averr){ -- if(averr == AVERROR_EOF){ -- return 1; -- } -+ (void)averr; - // FIXME need to map averror codes to ncerrors - //fprintf(stderr, "AVERR: %d/%x %d/%x\n", averr, averr, -averr, -averr); - return -1; -@@ -326,6 +325,11 @@ ffmpeg_decode(ncvisual* n){ - /*if(averr != AVERROR_EOF){ - fprintf(stderr, "Error reading frame info (%s)\n", av_err2str(averr)); - }*/ -+ if(averr == AVERROR_EOF){ -+ // Start flush. -+ unref = false; -+ break; -+ } - return averr2ncerr(averr); - } - unref = true; -@@ -339,10 +343,10 @@ ffmpeg_decode(ncvisual* n){ - } - }while(n->details->packet->stream_index != n->details->stream_index); - n->details->packet_outstanding = true; -- int averr = avcodec_send_packet(n->details->codecctx, n->details->packet); -+ int averr = avcodec_send_packet(n->details->codecctx, unref ? n->details->packet : NULL); - if(averr < 0){ -- n->details->packet_outstanding = false; - av_packet_unref(n->details->packet); -+ n->details->packet_outstanding = false; - //fprintf(stderr, "Error processing AVPacket\n"); - return averr2ncerr(averr); - } -@@ -350,10 +354,16 @@ ffmpeg_decode(ncvisual* n){ - int averr = avcodec_receive_frame(n->details->codecctx, n->details->frame); - if(averr >= 0){ - have_frame = true; -+ }else if(averr == AVERROR_EOF){ -+ n->details->packet_outstanding = false; -+ // Where EOF gets returned during normal operation. -+ return 1; - }else if(averr < 0){ -- av_packet_unref(n->details->packet); -- have_frame = false; -+ if(unref){ -+ av_packet_unref(n->details->packet); -+ } - n->details->packet_outstanding = false; -+ have_frame = false; - if(averr != AVERROR(EAGAIN)){ - return averr2ncerr(averr); - } -@@ -365,6 +375,8 @@ ffmpeg_decode(ncvisual* n){ - n->rowstride = f->linesize[0]; - n->pixx = n->details->frame->width; - n->pixy = n->details->frame->height; -+ const uint64_t pktduration = ffmpeg_pkt_duration(f); -+ n->duration = av_rescale_q(pktduration, n->details->timebase, AV_TIME_BASE_Q); - //fprintf(stderr, "good decode! %d/%d %d %p\n", n->details->frame->height, n->details->frame->width, n->rowstride, f->data); - ncvisual_set_data(n, f->data[0], false); - force_rgba(n); -@@ -457,6 +469,7 @@ ffmpeg_from_file(const char* filename){ - goto err; - } - AVStream* st = ncv->details->fmtctx->streams[ncv->details->stream_index]; -+ ncv->details->timebase = st->time_base; - if((ncv->details->codecctx = avcodec_alloc_context3(ncv->details->codec)) == NULL){ - //fprintf(stderr, "Couldn't allocate decoder for %s\n", filename); - goto err; -@@ -501,14 +514,6 @@ ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale, - memcpy(&activevopts, vopts, sizeof(*vopts)); - int ncerr; - do{ -- // codecctx seems to be off by a factor of 2 regularly. instead, go with -- // the time_base from the avformatctx. except ts isn't properly reset for -- // all media when we loop =[. we seem to be accurate enough now with the -- // tbase/ppd. see https://github.com/dankamongmen/notcurses/issues/1352. -- double tbase = av_q2d(ncv->details->fmtctx->streams[ncv->details->stream_index]->time_base); -- if(isnan(tbase)){ -- tbase = 0; -- } - if(activevopts.n){ - ncplane_erase(activevopts.n); // new frame could be partially transparent - } -@@ -529,7 +534,7 @@ ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale, - } - // display duration in units of time_base - const uint64_t pktduration = ffmpeg_pkt_duration(ncv->details->frame); -- uint64_t duration = pktduration * tbase * NANOSECS_IN_SEC; -+ uint64_t duration = av_rescale_q(pktduration, ncv->details->timebase, (AVRational){1, NANOSECS_IN_SEC}); - double schedns = nsbegin; - sum_duration += (duration * timescale); - schedns += sum_duration; -@@ -561,11 +566,13 @@ static int - ffmpeg_decode_loop(ncvisual* ncv){ - int r = ffmpeg_decode(ncv); - if(r == 1){ -- if(av_seek_frame(ncv->details->fmtctx, ncv->details->stream_index, 0, AVSEEK_FLAG_FRAME) < 0){ -+ avformat_flush(ncv->details->fmtctx); -+ if(avformat_seek_file(ncv->details->fmtctx, ncv->details->stream_index, 0, 0, 0, AVSEEK_FLAG_FRAME) < 0){ - // FIXME log error - return -1; - } -- if(ffmpeg_decode(ncv) < 0){ -+ avcodec_flush_buffers(ncv->details->codecctx); -+ if(ffmpeg_decode(ncv) != 0){ - return -1; - } - } diff --git a/taro/subprojects/stela.wrap b/taro/subprojects/stela.wrap deleted file mode 100644 index 7807a8c..0000000 --- a/taro/subprojects/stela.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url = https://git.akon.city/stela -revision = master -depth = 1 diff --git a/taro/src/ui.c b/taro/ui.c index b2c700e..5bcaf1d 100644 --- a/taro/src/ui.c +++ b/taro/ui.c @@ -1,9 +1,11 @@ #include <notcurses/notcurses.h> #include <notcurses/nckeys.h> +#include <al/log.h> +#include <al/lib.h> +#include <al/random.h> #include <aki/common.h> #include <aki/event_loop.h> #include <aki/timer.h> -#include <al/log.h> #include "db.h" #include "file_ext.h" @@ -13,36 +15,39 @@ #define TIMER_UPDATE_BASE 1.f -#define TILE_WIDTH 18 -#define TILE_HEIGHT 10 -#define FLASH_DURATION 0.3f +#define TEXT_UPDATE_RATE 0.4f +#define FLASH_DURATION 0.4f + +#define UI_RESERVE_WIDTH 33 +#define UI_RESERVE_HEIGHT 2 -#define TEXT_WIDTH (TILE_WIDTH - 4) -#define TEXT_UPDATE_RATE 0.3f; +#define GRID_TILE_WIDTH 18 // img = 16 +#define GRID_TILE_HEIGHT 10 // img = 8 -#define INFO_RES_WIDTH 33 -#define INFO_RES_HEIGHT 2 +#define GRID_TEXT_WIDTH (GRID_TILE_WIDTH - 4) #define INFO_X_OFFSET 2 #define INFO_Y_OFFSET 1 #define INFO_WIDTH 30 -// INFO_HEIGHT = term_rows + INFO_RES_HEIGHT +// INFO_HEIGHT = term_rows + UI_RESERVE_HEIGHT -#define INFO_TILE_WIDTH 24 -#define INFO_TILE_HEIGHT 12 +#define INFO_IMG_WIDTH 24 +#define INFO_IMG_HEIGHT 12 -#define INFO_TEXT_WIDTH (TEXT_WIDTH + 4) +#define INFO_TEXT_WIDTH (GRID_TEXT_WIDTH + 6) #define PROP_TEXT_WIDTH 16 -#define MAX_MONITOR_NAME_LEN 255 +#define MAX_MONITOR_NAME 255 struct scrolling_text { wstr *s; + s32 y; + s32 x; s32 width; - s32 index, max; - s32 x, y; + u32 index; + s32 skip; }; struct image { @@ -75,13 +80,14 @@ struct monitors { struct grid { struct ncplane *p; - s32 tiles_x, tiles_y; + s32 tiles_x; + s32 tiles_y; s32 min_index; s32 max_index; s32 selected_index; s32 set_index; f32 flash; - s32 tiles_per_page; + u32 tiles_per_page; array(struct tile) tiles; }; @@ -114,7 +120,8 @@ struct ui { struct aki_timer timer; bool layed_out; f32 text_update; - u32 term_rows, term_cols; + u32 term_rows; + u32 term_cols; u8 selected_menu; struct ncplane *p, *mp, *gp, *ip; struct monitors m; @@ -200,79 +207,66 @@ static void write_selected_entry_to_config(struct ui *u) // Interface // --------- -static void set_scrolling_text(struct scrolling_text *text, wstr *s, s32 text_width, s32 width, s32 x, s32 y) +static bool wide_string_to_width(wstr *w, u32 index, s32 max_width, void (*func)(void *, wchar_t), void *userdata) { - text->index = 0; - text->width = width; - text->s = s; - if (text_width > text->width) { - text->max = (text_width - text->width); - // If the character at text->index is wider than 1 cell, - // that is the amount the text will scroll by. So, account - // for that here. - for (s32 i = 0; i < text->max; i++) { - s32 wcl = al_wchar_width(al_wstr_at(s, i)); - if (wcl > 1) { - text->max -= wcl - 1; - } + u32 i = index; + s32 width = 0; + do { + wchar_t wc = al_wstr_at(w, i); + width += al_wchar_width(wc); + if (width >= max_width) { + break; } - text->max++; - } else { - text->max = -1; - } - text->x = x; - text->y = y; + func(userdata, wc); + } while (++i < w->len); + return i == w->len; } -static void draw_scrolling_text(struct ncplane *p, struct scrolling_text *t, bool update) +static void set_scrolling_text(struct scrolling_text *text, wstr *s, s32 y, s32 x, s32 width) { - ncplane_cursor_move_yx(p, t->y, t->x); - if (t->max != -1) { - s32 i = 0; - s32 width = 0; - while (width < t->width) { - wchar_t wc = al_wstr_at(t->s, t->index + i); - width += al_wchar_width(wc); - if (width > t->width) { - break; - } - ncplane_putwc(p, wc); - i++; - } - if (update) { - t->index = (t->index + 1) % t->max; - } - } else { - ncplane_putwstr(p, &al_wstr_at(t->s, 0)); - } + text->s = s; + text->y = y; + text->x = x; + text->width = width; + text->index = 0; + text->skip = al_wchar_width(al_wstr_at(s, text->index)); } -static void set_info_entry(struct ui *u, struct info *n) +static void draw_func(void *userdata, wchar_t wc) { - n->selected_prop = 0; - n->props.size = 0; + struct ncplane *p = (struct ncplane *)userdata; + ncplane_putwc(p, wc); +} - if (u->g.selected_index == -1 || u->g.selected_index >= (s32)u->db.entries.size) { - n->e = NULL; - return; +static void draw_scrolling_text(struct ncplane *p, struct scrolling_text *text, bool update) +{ + ncplane_cursor_move_yx(p, text->y, text->x); + bool end = wide_string_to_width(text->s, text->index, text->width, draw_func, p); + if (update && !--text->skip) { + text->skip = al_wchar_width(al_wstr_at(text->s, text->index)); + text->index = end ? 0 : text->index + 1; } +} +static void set_info_entry(struct ui *u, struct info *n) +{ n->e = &al_array_at(u->db.entries, u->g.selected_index); - n->blitted = false; s32 width = al_wstr_width(&u->n.e->title); s32 tx = 2 + (width < INFO_TEXT_WIDTH ? (INFO_TEXT_WIDTH - width) / 2 : 0); - set_scrolling_text(&n->text, &u->n.e->title, width, INFO_TEXT_WIDTH, tx, INFO_TILE_HEIGHT + 1); + set_scrolling_text(&n->text, &u->n.e->title, INFO_IMG_HEIGHT + 1, tx, INFO_TEXT_WIDTH); + n->selected_prop = 0; + n->props.size = 0; struct prop *p; + struct info_prop *ip; al_array_foreach_ptr(u->n.e->props, i, p) { - struct info_prop ip = { 0 }; - ip.p = p; - width = al_wstr_width(&p->title); - s32 ty = (INFO_TILE_HEIGHT + 3) + ((i % n->max_props) * 2); - set_scrolling_text(&ip.text, &p->title, width, PROP_TEXT_WIDTH, 0, ty); - al_array_push(n->props, ip); + al_array_push(n->props, (struct info_prop){ 0 }); + ip = &al_array_last(n->props); + ip->p = p; + s32 ty = INFO_IMG_HEIGHT + 3 + ((i % n->max_props) * 2); + set_scrolling_text(&ip->text, &p->title, ty, 0, PROP_TEXT_WIDTH); } } @@ -296,9 +290,9 @@ static bool init_grid(struct grid *g) return true; } -static s32 monitor_cmp(void *va, void *vb) +static s32 monitor_cmp(const void *va, const void *vb) { - return strncmp(*(char **)va, *(char **)vb, MAX_MONITOR_NAME_LEN); + return strncmp(*(char **)va, *(char **)vb, MAX_MONITOR_NAME); } static bool init_monitors(struct ui *u, struct monitors *m) @@ -311,7 +305,7 @@ static bool init_monitors(struct ui *u, struct monitors *m) bool ret = aki_file_open_and_read_wholly(&monitors_path, &monitors, false); al_str_free(&monitors_path); if (!ret) { - al_log_error("taro", "failed to open monitor list, is the daemon configured?"); + al_log_error("taro", "failed to open monitor list, has `tarod` run?"); return false; } str monitor_dir; @@ -337,6 +331,8 @@ static bool init_monitors(struct ui *u, struct monitors *m) } if (u->db.entries.size > 0) { u->g.selected_index = (u->g.set_index >= 0) ? u->g.set_index : 0; + } else { + u->g.selected_index = -1; } return true; } @@ -373,12 +369,10 @@ struct image *get_image_from_pool(struct image_pool *pool, str *path) } if (pool->images.size >= pool->max) { - // al_array_remove_range()... for (u32 i = 0; i < pool->chunk_size; i++) { - struct image img; - al_array_pop_at(pool->images, 0, img); - ncvisual_destroy(img.v); + ncvisual_destroy(al_array_at(pool->images, i).v); } + al_array_remove_range(pool->images, 0, pool->chunk_size); } char *c_str = al_str_to_c_str(path); @@ -436,19 +430,17 @@ static void maybe_destroy_info(struct info *n) } } -static bool layout_info(struct ui *u, struct info *n, struct ncplane *parent, s32 width, s32 height) +static bool layout_info(struct ui *u, struct info *n, struct ncplane *parent, s32 height) { - (void)width; - maybe_destroy_info(n); - n->max_props = (height - INFO_TILE_HEIGHT - 3) / 2; + n->max_props = (height - INFO_IMG_HEIGHT - 3) / 2; if (!n->max_props) return false; struct ncplane_options nopts = { 0 }; - nopts.cols = INFO_TILE_WIDTH; - nopts.rows = INFO_TILE_HEIGHT; + nopts.cols = INFO_IMG_WIDTH; + nopts.rows = INFO_IMG_HEIGHT; nopts.x = 0; nopts.y = 1; @@ -458,7 +450,9 @@ static bool layout_info(struct ui *u, struct info *n, struct ncplane *parent, s3 return false; } - set_info_entry(u, &u->n); + if (u->g.selected_index >= 0) { + set_info_entry(u, &u->n); + } return true; } @@ -492,27 +486,35 @@ static void set_grid_tiles(struct ui *u, struct grid *g) t->blitted = false; ncplane_erase(t->p); - if (g->max_index >= (s32)u->db.entries.size) { + while (g->max_index < (s32)u->db.entries.size) { + t->e = &al_array_at(u->db.entries, g->max_index); + if (db_ensure_entry_loaded(&u->db, t->e)) { + break; + } + } + + al_assert(g->max_index <= (s32)u->db.entries.size); + + if (g->max_index == (s32)u->db.entries.size) { t->e = NULL; t->text.s = NULL; ncplane_erase(t->ip); continue; } - t->e = &al_array_at(u->db.entries, g->max_index); - if (!db_ensure_entry_loaded(&u->db, t->e)) { - x--; - continue; - } - s32 width = al_wstr_width(&t->e->title); - s32 tx = 2 + ((width < TEXT_WIDTH) ? (TEXT_WIDTH - width) / 2 : 0); - set_scrolling_text(&t->text, &t->e->title, width, TEXT_WIDTH, tx, TILE_HEIGHT - 1); + s32 tx = 2 + (width < GRID_TEXT_WIDTH ? (GRID_TEXT_WIDTH - width) / 2 : 0); + set_scrolling_text(&t->text, &t->e->title, GRID_TILE_HEIGHT - 1, tx, GRID_TEXT_WIDTH); g->max_index++; }} g->max_index--; + + // This can happen in grid_next_page() and shuffle. Any other case where + // `selected_index` goes over `max_index` is undefined. Handling it this way + // saves us from a lot of pre-loaded. + if (g->selected_index > g->max_index) g->selected_index = g->max_index; } static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s32 width, s32 height) @@ -522,8 +524,8 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 s32 inner_width = width; s32 inner_height = height; - g->tiles_x = (s32)(width / TILE_WIDTH); - g->tiles_y = (s32)(height / TILE_HEIGHT); + g->tiles_x = width / GRID_TILE_WIDTH; + g->tiles_y = height / GRID_TILE_HEIGHT; if (!g->tiles_x || !g->tiles_y) { // Window is too small. @@ -532,8 +534,8 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 g->tiles_per_page = g->tiles_x * g->tiles_y; - // Allow 4 pages of tiles in the pool, but never shrink. - if (u->pool.chunk_size < (u32)g->tiles_per_page) { + // Reserve space for 4 pages, but never shrink. + if (u->pool.chunk_size < g->tiles_per_page) { u->pool.chunk_size = g->tiles_per_page; u->pool.max = u->pool.chunk_size * 4; al_array_reserve(u->pool.images, u->pool.max); @@ -551,8 +553,8 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 nopts.cols = inner_width; nopts.rows = inner_height; - nopts.x = (width - (g->tiles_x * TILE_WIDTH)) / 2; - nopts.y = (height - (g->tiles_y * TILE_HEIGHT)) / 2; + nopts.x = (width - (g->tiles_x * GRID_TILE_WIDTH)) / 2; + nopts.y = (height - (g->tiles_y * GRID_TILE_HEIGHT)) / 2; g->p = ncplane_create(parent, &nopts); if (!g->p) return false; @@ -561,10 +563,10 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 for (s32 x = 0; x < g->tiles_x; x++) { struct tile t = { 0 }; - nopts.cols = TILE_WIDTH; - nopts.rows = TILE_HEIGHT; - nopts.x = x * TILE_WIDTH; - nopts.y = y * TILE_HEIGHT; + nopts.cols = GRID_TILE_WIDTH; + nopts.rows = GRID_TILE_HEIGHT; + nopts.x = x * GRID_TILE_WIDTH; + nopts.y = y * GRID_TILE_HEIGHT; t.p = ncplane_create(g->p, &nopts); if (!t.p) { @@ -572,8 +574,8 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 return false; } - nopts.cols = TILE_WIDTH - 2; - nopts.rows = TILE_HEIGHT - 2; + nopts.cols = GRID_TILE_WIDTH - 2; + nopts.rows = GRID_TILE_HEIGHT - 2; nopts.x = 1; nopts.y = 1; @@ -586,7 +588,9 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3 al_array_push(g->tiles, t); }} - set_grid_tiles(u, g); + if (g->selected_index >= 0) { + set_grid_tiles(u, g); + } return true; } @@ -597,14 +601,17 @@ static void maybe_destroy_ui(struct ui *u) ncplane_destroy(u->ip); u->ip = NULL; } + if (u->gp) { ncplane_destroy(u->gp); u->gp = NULL; } + if (u->mp) { ncplane_destroy(u->mp); u->mp = NULL; } + if (u->p) { ncplane_destroy(u->p); u->p = NULL; @@ -615,10 +622,10 @@ bool layout_ui(struct ui *u) { bool even = u->term_cols % 2 == 0; - u32 rwidth = (even) ? INFO_RES_WIDTH + 1 : INFO_RES_WIDTH; - u32 rheight = (even) ? INFO_RES_HEIGHT + 1 : INFO_RES_HEIGHT; + u32 rwidth = (even) ? UI_RESERVE_WIDTH + 1 : UI_RESERVE_WIDTH; + u32 rheight = (even) ? UI_RESERVE_HEIGHT + 1 : UI_RESERVE_HEIGHT; - if (u->term_cols < TILE_WIDTH + rwidth || u->term_rows < TILE_HEIGHT + rheight) { + if (u->term_cols < GRID_TILE_WIDTH + rwidth || u->term_rows < GRID_TILE_HEIGHT + rheight) { return false; } @@ -634,8 +641,8 @@ bool layout_ui(struct ui *u) return false; } - s32 cols = u->term_cols - rwidth; - s32 rows = u->term_rows - rheight; + s32 cols = (s32)u->term_cols - rwidth; + s32 rows = (s32)u->term_rows - rheight; if (cols < 0 || rows < 0) { maybe_destroy_ui(u); @@ -669,7 +676,7 @@ bool layout_ui(struct ui *u) return false; } - nopts.x = nopts.cols + ((even) ? INFO_X_OFFSET + 1 : INFO_X_OFFSET); + nopts.x = nopts.cols + (even ? INFO_X_OFFSET + 1 : INFO_X_OFFSET); nopts.y = INFO_Y_OFFSET; nopts.cols = INFO_WIDTH; nopts.rows = u->term_rows - rheight; @@ -680,7 +687,7 @@ bool layout_ui(struct ui *u) return false; } - if (!layout_info(u, &u->n, u->ip, nopts.cols, nopts.rows)) { + if (!layout_info(u, &u->n, u->ip, nopts.rows)) { maybe_destroy_ui(u); return false; } @@ -704,15 +711,15 @@ void draw_monitors(struct ui *u, struct monitors *m) ncplane_cursor_move_yx(u->mp, 0, x); al_array_foreach(m->monitors, i, monitor) { if (m->flash <= 0.f && u->selected_menu == MENU_MONITORS && i == (u32)m->selected_monitor) { - ncplane_set_bg_rgb8(u->mp, 211, 184, 152); - ncplane_set_fg_rgb8(u->mp, 13, 22, 45); + ncplane_set_bg_palindex(u->mp, 8); + ncplane_set_fg_palindex(u->mp, 0); } else if (i == (u32)m->set_monitor) { - ncplane_set_bg_rgb8(u->mp, 242, 233, 215); - ncplane_set_fg_rgb8(u->mp, 13, 22, 45); + ncplane_set_bg_palindex(u->mp, 7); + ncplane_set_fg_palindex(u->mp, 0); m->flash -= AL_MAX(TIMER_UPDATE_BASE * (u->delay / 1000000.f), 0.001f); } else { - ncplane_set_fg_rgb8(u->mp, 0xff, 0xff, 0xff); - ncplane_set_bg_palindex(u->mp, 0); + ncplane_set_fg_default(u->mp); + ncplane_set_bg_default(u->mp); } ncplane_cursor_move_rel(u->mp, 0, 1); ncplane_putstr(u->mp, monitor); @@ -726,21 +733,25 @@ void draw_info(struct ui *u, struct info *n, bool update_text) ncplane_erase(u->ip); struct ncvisual_options vopts = { 0 }; - vopts.scaling = NCSCALE_SCALE; + vopts.scaling = NCSCALE_STRETCH; vopts.blitter = PIXEL_BLIT ? NCBLIT_PIXEL : NCBLIT_2x1; struct image *img = get_image_from_pool(&u->pool, &n->e->preview_path); if (img && !n->blitted && !img->errored) { ncplane_erase(n->ip); img->errored = img->gif && ncvisual_decode_loop(img->v) < 0; - u->delay = ncvisual_get_duration(img->v); if (!img->errored) { vopts.n = n->ip; ncvisual_blit(u->nc, img->v, &vopts); n->blitted = !img->gif; + u->delay = ncvisual_get_duration(img->v); + if (u->delay == 10000) { + // Default 100fps gifs to 10fps, seems like some weird wallpaper engine behavior. + u->delay = 100000; + } } } - ncplane_set_fg_rgb8(u->ip, 0xff, 0xff, 0xff); + ncplane_set_fg_default(u->ip); draw_scrolling_text(u->ip, &n->text, update_text); u32 selected_index = n->selected_prop - (n->selected_prop % n->max_props); @@ -753,9 +764,9 @@ void draw_info(struct ui *u, struct info *n, bool update_text) } ip = &al_array_at(n->props, index); if (u->selected_menu == MENU_INFO && index == (u32)n->selected_prop) { - ncplane_set_fg_rgb8(u->ip, 211, 184, 152); + ncplane_set_fg_palindex(u->ip, 8); } else { - ncplane_set_fg_rgb8(u->ip, 0xff, 0xff, 0xff); + ncplane_set_fg_default(u->ip); } draw_scrolling_text(u->ip, &ip->text, update_text); switch (ip->p->type) { @@ -765,31 +776,31 @@ void draw_info(struct ui *u, struct info *n, bool update_text) break; case PROP_TYPE_SLIDER: { char c[16]; - sprintf(c, "%.2f", ((struct slider_value *)ip->p->value)->value); + al_snprintf(c, sizeof(c), "%.2f", ((struct slider_value *)ip->p->value)->value); ncplane_putstr_yx(u->ip, ip->text.y, ip->text.width + 2, c); break; } } } if (ip && i == n->max_props && i != n->props.size) { - ncplane_putstr_yx(u->ip, ip->text.y + 1, (INFO_TILE_WIDTH - 1) / 2, "V"); + ncplane_putstr_yx(u->ip, ip->text.y + 1, (INFO_IMG_WIDTH - 1) / 2, "V"); } } -static void set_highlighted_style(struct tile *t, s32 br, s32 bg, s32 bb, s32 fr, s32 fg, s32 fb) +static void set_tile_highlight(struct tile *t, s32 bindex, s32 findex) { - ncplane_set_fg_rgb8(t->p, br, bg, bb); ncplane_set_bg_alpha(t->p, NCALPHA_TRANSPARENT); - u64 c = 0; - ncchannels_set_bg_rgb8(&c, br, bg, bb); - ncchannels_set_fg_rgb8(&c, br, bg, bb); - c = ncchannels_reverse(c); - ncplane_gradient(t->p, 1, 0, TILE_HEIGHT - 1, TILE_WIDTH, "", 0, c, c, c, c); - for (s32 i = 0; i < TILE_WIDTH; i++) { - ncplane_putwc_yx(t->p, 0, i, L'▂'); + struct nccell ncl; + nccell_init(&ncl); + nccell_load(t->p, &ncl, " "); + nccell_set_bg_palindex(&ncl, bindex); + ncplane_polyfill_yx(t->p, 1, 0, &ncl); + ncplane_set_fg_palindex(t->p, bindex); + for (s32 i = 0; i < GRID_TILE_WIDTH; i++) { + ncplane_putwc_yx(t->p, 0, i, L'▃'); } - ncplane_set_bg_rgb8(t->p, br, bg, bb); - ncplane_set_fg_rgb8(t->p, fr, fg, fb); + ncplane_set_fg_palindex(t->p, findex); + ncplane_set_bg_palindex(t->p, bindex); ncplane_set_bg_alpha(t->p, NCALPHA_OPAQUE); ncplane_set_styles(t->p, NCSTYLE_BOLD); } @@ -799,15 +810,13 @@ void draw_grid_tiles(struct ui *u, struct grid *g, bool update_text) if (!u->layed_out) return; struct ncvisual_options vopts = { 0 }; - vopts.scaling = NCSCALE_SCALE; + vopts.scaling = NCSCALE_STRETCH; vopts.blitter = PIXEL_BLIT ? NCBLIT_PIXEL : NCBLIT_2x1; s32 index = (g->selected_index - (g->selected_index % g->tiles_per_page)); - struct tile *t; al_array_foreach_ptr(g->tiles, i, t) { - if (!t->e) continue; - if (t->e->has_preview) { + if (t->e && t->e->has_preview) { struct image *img = get_image_from_pool(&u->pool, &t->e->preview_path); if (img && !t->blitted && !img->errored) { vopts.n = t->ip; @@ -815,16 +824,17 @@ void draw_grid_tiles(struct ui *u, struct grid *g, bool update_text) t->blitted = true; } } - if (g->flash <= 0.f && u->selected_menu == MENU_GRID && index == g->selected_index) { - set_highlighted_style(t, 211, 184, 152, 13, 22, 45); - } else if (g->set_index >= 0 && index == g->set_index) { - set_highlighted_style(t, 242, 233, 215, 13, 22, 45); + ncplane_erase(t->p); + if (index == g->selected_index && g->flash <= 0.f && u->selected_menu == MENU_GRID) { + set_tile_highlight(t, 8, 0); + } else if (index == g->set_index && g->set_index >= 0) { + set_tile_highlight(t, 7, 0); g->flash -= AL_MAX(TIMER_UPDATE_BASE * (u->delay / 1000000.f), 0.001f); } else { - ncplane_erase(t->p); - ncplane_set_fg_rgb8(t->p, 0xff, 0xff, 0xff); - ncplane_set_bg_palindex(t->p, 0); - ncplane_set_styles(t->p, 0); + ncplane_set_fg_default(t->p); + ncplane_set_bg_default(t->p); + ncplane_set_bg_alpha(t->p, NCALPHA_TRANSPARENT); + ncplane_set_styles(t->p, NCSTYLE_NONE); } if (t->text.s && t->text.s->len) { draw_scrolling_text(t->p, &t->text, update_text); @@ -946,6 +956,7 @@ static bool handle_input_info(struct ui *u, struct info *n, struct ncinput *inpu static bool grid_next_page(struct ui *u, struct grid *g, s32 offset) { + if (u->db.entries.size == 0) return false; s32 max = (s32)u->db.entries.size - 1; if (g->selected_index == max) { return false; @@ -959,7 +970,7 @@ static bool grid_next_page(struct ui *u, struct grid *g, s32 offset) static bool grid_previous_page(struct grid *g, s32 offset) { - if (g->selected_index == 0) { + if (g->selected_index <= 0) { return false; } g->selected_index -= g->tiles_per_page - offset; @@ -1020,14 +1031,18 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu should_redraw_grid = true; break; case 'b': - should_update_grid = grid_previous_page(&u->g, 0); - should_redraw_info = true; - should_redraw_grid = true; + if (grid_previous_page(&u->g, 0)) { + should_update_grid = true; + should_redraw_info = true; + should_redraw_grid = true; + } break; case 'f': - should_update_grid = grid_next_page(u, &u->g, 0); - should_redraw_info = true; - should_redraw_grid = true; + if (grid_next_page(u, &u->g, 0)) { + should_update_grid = true; + should_redraw_info = true; + should_redraw_grid = true; + } break; case 'r': g->set_index = -1; @@ -1035,6 +1050,14 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu should_redraw_grid = true; should_redraw_info = true; break; + case 's': { + s32 max = (s32)u->db.entries.size - 1; + g->selected_index = al_rand() % max; + should_update_grid = true; + should_redraw_info = true; + should_redraw_grid = true; + break; + } case NCKEY_RETURN: case NCKEY_SPACE: { if (g->selected_index >= 0) { @@ -1152,15 +1175,16 @@ s32 main(void) s32 ret = EXIT_FAILURE; - if (!open_config(&u.conf)) goto out; + if (!open_config(&u.conf, false)) { + al_log_error("taro", "failed to open config, run `tarod` at least once to generate needed config"); + goto out; + } if (!db_load_entries(&u.db, &u.conf.wallpaper_dir)) { - close_config(&u.conf); goto out; } if (!init_ui(&u)) { - close_config(&u.conf); goto out; } @@ -1171,7 +1195,6 @@ s32 main(void) if (!layout_ui(&u)) { notcurses_stop(u.nc); - close_config(&u.conf); al_log_error("taro", "failed to layout ui, your terminal window is probably too small"); goto out; } @@ -1192,11 +1215,11 @@ s32 main(void) notcurses_stop(u.nc); - close_config(&u.conf); - ret = EXIT_SUCCESS; out: + close_config(&u.conf); + aki_common_close(); return ret; |