summaryrefslogtreecommitdiff
path: root/taro
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2024-10-19 12:05:15 -0400
committerAndrew Opalach <andrew@akon.city> 2024-10-19 12:13:26 -0400
commit42e8c4d28b6fe9cc5e041f02e0c85b390d49a218 (patch)
tree59080eec3dd79d225bb31fb0f7e646cc22880d39 /taro
parent2bb807dd32e1c18a6f1e8f22c25141de3334de9f (diff)
downloadmauri-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.diff313
-rw-r--r--taro/attic/sway_test_config9
-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.build27
-rwxr-xr-xtaro/scripts/run_wallpaper.sh6
-rw-r--r--taro/subprojects/libakiyo.wrap4
-rw-r--r--taro/subprojects/libalabaster.wrap4
-rw-r--r--taro/subprojects/notcurses.wrap12
-rw-r--r--taro/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff166
-rw-r--r--taro/subprojects/stela.wrap4
-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;