summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore10
-rw-r--r--README.txt114
-rw-r--r--flake.lock6
-rw-r--r--flake.nix5
-rw-r--r--meson.build2
-rw-r--r--src/assets.cc4
-rw-r--r--src/assets.h3
-rw-r--r--src/context.h6
-rw-r--r--src/gl.cc4
-rw-r--r--src/gl.h5
-rw-r--r--src/json.h1
-rw-r--r--src/log.h5
-rw-r--r--src/mauri.cc100
-rw-r--r--src/objects/object.cc2
-rw-r--r--src/objects/pass.cc31
-rw-r--r--src/objects/scene.cc6
-rw-r--r--src/shader.cc7
-rw-r--r--src/shader.h4
-rw-r--r--src/texture.cc1
-rw-r--r--src/util.h11
-rw-r--r--subprojects/glm.wrap4
-rw-r--r--subprojects/libalabaster.wrap2
-rw-r--r--subprojects/libnaunet.wrap2
-rw-r--r--subprojects/lz4.wrap2
-rw-r--r--subprojects/stela.wrap2
-rw-r--r--taro/config.h57
-rw-r--r--taro/daemon.c274
-rw-r--r--taro/db.c249
-rw-r--r--taro/db.h5
-rw-r--r--taro/file_ext.c9
-rw-r--r--taro/meson.build4
-rw-r--r--taro/ui.c428
32 files changed, 823 insertions, 542 deletions
diff --git a/.gitignore b/.gitignore
index 6fab9ba..b788ee1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,12 +2,15 @@
.cache/
compile_commands.json
subprojects/c89atomic.wrap
+subprojects/c89atomic-*/
subprojects/glfw3.wrap
-subprojects/glfw3/
-subprojects/glm.wrap
+subprojects/glfw3-*/
subprojects/glm/
subprojects/jansson.wrap
+subprojects/jansson-*/
subprojects/janus/
+subprojects/jsmn.wrap
+subprojects/jsmn/
subprojects/json/
subprojects/libalabaster
subprojects/libalabaster/
@@ -22,5 +25,8 @@ subprojects/s3tc-dxt-decompression/
subprojects/stb/
subprojects/stela
subprojects/stela/
+subprojects/wayland-protocols.wrap
+subprojects/wayland-protocols-*/
subprojects/wlr-protocols.wrap
+subprojects/wlr-protocols-*/
workshop_wallpapers*/
diff --git a/README.txt b/README.txt
index aebc4be..9824bca 100644
--- a/README.txt
+++ b/README.txt
@@ -1,3 +1,5 @@
+:source-highlighter: pygments
+
== mauri: Wallpaper Engine Implementation (for Linux)
=== Building
@@ -9,12 +11,118 @@
* nlohmann_json
* fftw3 `optional (audio)`
* notcurses `optional (taro)`
+* jansson `optional (taro)`
+* libev `optional (taro)`
+
+debian12: sudo apt install gcc g++ meson ninja-build
==== Compiling
[source,sh]
----
-meson setup build -Dwindow=(x11|wayland) -Daudio=(enabled|disabled) -Dtaro=(enabled|disabled)
-ninja -C build
+$ meson setup build -Dwindow=(x11|wayland) -Daudio=(enabled|disabled) -Dtaro=(enabled|disabled)
+$ ninja -C build
+----
+
+=== Installing
+==== Local
+[source,sh]
+----
+$ sudo ninja -C build install
+----
+
+==== Nix Flake
+[source,nix]
+----
+inputs = {
+ mauri = {
+ url = "git+https://git.akon.city/mauri";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+};
+
+nixosSystem {
+ system = system;
+ modules = [
+ ({ ... }: {
+ nixpkgs.overlays = [
+ (self: super: { mauri = mauri.packages.${system}.default; })
+ ];
+ })
+ ];
+};
+----
+
+=== Usage
+[source,sh]
+----
+$ mauri --help
+----
+[source,plain]
+----
+Usage: mauri (-p, --package [path_to_scene.pkg]|-d, --directory [path_to_wallpaper]) [OPTIONS]
+ -a, --assets search path for common assets
+ -w, --width window width (default: 640)
+ -h, --height window height (default: 480)
+ -o, --offset offset of the scene camera (default: 0,0)
+ -s, --scale scale the window to the scene
+ -m, --monitor set as the background of this monitor (by name)
+ -t, --target dump the scene tree into this directory and exit
+----
+==== Examples
+[source,sh]
+----
+# Set as the background of DP-1.
+$ ./mauri -p /mnt/hdd/SteamLibrary/steamapps/workshop/content/431960/2452236180/scene.pkg \
+ -a /mnt/hdd/SteamLibrary/steamapps/common/wallpaper_engine/assets -w 2560 -h 1440 -m DP-1
+
+# Dump the scene into ./output.
+$ ./mauri -p /mnt/hdd/SteamLibrary/steamapps/workshop/content/431960/2108234011/scene.pkg \
+ -a /mnt/hdd/SteamLibrary/steamapps/common/wallpaper_engine/assets -t ./output
+----
+
+==== taro
+[source,sh]
+----
+# You must run tarod once before anything to generate the necessary files for your setup.
+$ tarod
+
+# Open the terminal UI.
+$ taro
+----
+
+===== Keybinds
+*←↓↑→/hjkl* - Move Selection, Edit Value +
+*Enter/Space* - Confirm Selection +
+*Tab* - Cycle Between Grid, Info/Options, and Monitor Selection +
+*f/b* - Next/Previous Page +
+*r* - Remove Current Wallpaper +
+
+===== Config
+[source,sh]
+----
+$ cat $HOME/.config/taro/config.json
+----
+[source,json]
+----
+{
+ "directories": {
+ "assets": "/mnt/hdd/SteamLibrary/steamapps/common/wallpaper_engine/assets",
+ "wallpapers": [
+ "/mnt/hdd/SteamLibrary/steamapps/workshop/content/431960"
+ ]
+ }
+}
+----
+
+===== Sway Config Example
+[source,sh]
+----
+# Set wallpaper
+bindsym Mod4+F1 exec printf '1633608994_p|false,100|' > $HOME/.config/taro/DP-1/selection
+# Remove wallpaper
+bindsym Mod4+F2 exec printf '||' > $HOME/.config/taro/DP-1/selection
+# Run when starting Sway
+exec tarod
----
=== Known Issues
@@ -22,7 +130,7 @@ ninja -C build
- Some objects are incorrectly scaled and/or positioned.
- Some shaders fail to compile for various reasons.
-==== Not Implemented:
+==== Not Implemented
- Particles
- Mouse Paralax
- Camera Shake
diff --git a/flake.lock b/flake.lock
index ae3a73f..49805da 100644
--- a/flake.lock
+++ b/flake.lock
@@ -19,11 +19,11 @@
},
"nixpkgs": {
"locked": {
- "lastModified": 1734649271,
- "narHash": "sha256-4EVBRhOjMDuGtMaofAIqzJbg4Ql7Ai0PSeuVZTHjyKQ=",
+ "lastModified": 1743315132,
+ "narHash": "sha256-6hl6L/tRnwubHcA4pfUUtk542wn2Om+D4UnDhlDW9BE=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "d70bd19e0a38ad4790d3913bf08fcbfc9eeca507",
+ "rev": "52faf482a3889b7619003c0daec593a1912fddc1",
"type": "github"
},
"original": {
diff --git a/flake.nix b/flake.nix
index 96ada27..62d6f15 100644
--- a/flake.nix
+++ b/flake.nix
@@ -76,7 +76,7 @@
wrapProgram $out/bin/taro --prefix LD_LIBRARY_PATH : "$out/lib64"
'';
meta = with lib; {
- homepage = "https://akon.city/";
+ homepage = "https://git.akon.city/mauri";
description = "";
changelog = "";
license = [ licenses.gpl3Only ];
@@ -94,6 +94,9 @@
perf-tools
valgrind
asciidoctor
+ (python3.withPackages (python-pkgs: [
+ python-pkgs.pygments
+ ]))
];
buildInputs = packages.${system}.default.buildInputs ++ [
# sway build
diff --git a/meson.build b/meson.build
index 7d13da0..7fa43ee 100644
--- a/meson.build
+++ b/meson.build
@@ -16,7 +16,7 @@ naunet = subproject('libnaunet', default_options: [
]).get_variable('naunet')
stela = subproject('stela', default_options: [
- 'api=opengl',
+ 'api=gl',
is_windows ? 'window=glfw' : 'window='+get_option('window'),
'poll=inline',
'event-buffer=false',
diff --git a/src/assets.cc b/src/assets.cc
index 8d2ac18..2bc227f 100644
--- a/src/assets.cc
+++ b/src/assets.cc
@@ -3,6 +3,8 @@
#include "assets.h"
#include "texture.h"
+//#define AL_LOG_ENABLE_TRACE
+#include "log.h"
namespace Mauri
{
@@ -192,7 +194,7 @@ auto AssetManager::get(const std::string &part, AssetTypeHint hint) -> Asset *
continue;
}
asset->hash = hash;
- debug("found asset in: (%s)", dir.c_str());
+ trace("found asset in: (%s)", dir.c_str());
break;
}
diff --git a/src/assets.h b/src/assets.h
index d1b8144..015564e 100644
--- a/src/assets.h
+++ b/src/assets.h
@@ -1,6 +1,9 @@
#ifndef _ASSET_H
#define _ASSET_H
+#include <string>
+#include <vector>
+
#include "util.h"
namespace Mauri
diff --git a/src/context.h b/src/context.h
index a64dde7..ab27cb2 100644
--- a/src/context.h
+++ b/src/context.h
@@ -2,12 +2,12 @@
#define _CONTEXT_H
extern "C" {
+#include <nnwt/thread.h>
#include <stl/window.h>
#include <stl/gl.h>
-#include <nnwt/thread.h>
}
-#include "gl.h"
+#include <string>
namespace Mauri
{
@@ -31,7 +31,7 @@ class Context
auto create_window(s32 width, s32 height, const std::string &name, const std::string monitor) -> bool
{
- this->window = stl_window_create(NULL);
+ this->window = stl_window_create();
this->window->pointer_pos_callback = pointer_pos_callback;
this->window->should_close_callback = should_close_callback;
this->window->userdata = this;
diff --git a/src/gl.cc b/src/gl.cc
index d753142..6ae8f1f 100644
--- a/src/gl.cc
+++ b/src/gl.cc
@@ -1,3 +1,7 @@
+extern "C" {
+#include <nnwt/net.h>
+}
+
#include "gl.h"
#include "log.h"
#include "context.h"
diff --git a/src/gl.h b/src/gl.h
index 95d8b84..47c5f8a 100644
--- a/src/gl.h
+++ b/src/gl.h
@@ -5,14 +5,15 @@ extern "C" {
#include <stl/gl.h>
}
+#include <vector>
+#include <unordered_map>
+
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
#include <glm/gtx/matrix_major_storage.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
-#include "util.h"
-
using namespace glm;
namespace Mauri
diff --git a/src/json.h b/src/json.h
index 300f1b3..0cd7ab9 100644
--- a/src/json.h
+++ b/src/json.h
@@ -2,7 +2,6 @@
#define _JSON_H
#define JSON_NO_IO
-#undef array
#include <nlohmann/json.hpp>
#include "gl.h"
diff --git a/src/log.h b/src/log.h
index b7655ff..43d0e09 100644
--- a/src/log.h
+++ b/src/log.h
@@ -5,9 +5,4 @@ extern "C" {
#include <al/log.h>
}
-#define info(fmt, ...) al_log_info(NULL, fmt, ##__VA_ARGS__)
-#define warn(fmt, ...) al_log_warn(NULL, fmt, ##__VA_ARGS__)
-#define error(fmt, ...) al_log_error(NULL, fmt, ##__VA_ARGS__)
-#define debug(fmt, ...) al_log_debug(NULL, fmt, ##__VA_ARGS__)
-
#endif // _LOG_H
diff --git a/src/mauri.cc b/src/mauri.cc
index e562a76..9351320 100644
--- a/src/mauri.cc
+++ b/src/mauri.cc
@@ -2,12 +2,12 @@ extern "C" {
#include <nnwt/common.h>
#include <stl/window.h>
}
-
#include <janus.h>
-#include "context.h"
#include "assets.h"
+#include "context.h"
#include "engine.h"
+#include "log.h"
#include "objects/scene.h"
@@ -19,33 +19,16 @@ Usage: mauri (-p, --package [path_to_scene.pkg]|-d, --directory [path_to_wallpap
-w, --width window width (default: 640)\n\
-h, --height window height (default: 480)\n\
-o, --offset offset of the scene camera (default: 0,0)\n\
+ -s, --scale scale the window to the scene\n\
-m, --monitor set as the background of this monitor (by name)\n\
- -t, --target dump scene into this directory and exit\
+ -t, --target dump the scene tree into this directory and exit\
"
-#ifndef _WIN32
-auto main(s32 argc, char *argv[]) -> s32
-#else
-auto wmain(s32 argc, wchar_t **argv) -> s32
-#endif
+auto window_system_main(u32 argc, str *argv, void *extra) -> s32
{
- if (!nn_common_init()) return EXIT_FAILURE;
-
- janus::ArgParser args(HELP_TEXT, "0.13");
-
- args.newString("package p", "");
- args.newString("directory d", "");
- args.newString("assets a", "");
- args.newInt("width w", 0);
- args.newInt("height h", 0);
- args.newString("offset o", "0,0");
- args.newString("monitor m", "");
- args.newString("target t", "");
-#ifndef _WIN32
- args.parse(argc, argv);
-#else
- args.wparse(argc, argv);
-#endif
+ (void)argc;
+ (void)argv;
+ janus::ArgParser args = *(janus::ArgParser *)extra;
if (!(args.found("package") ^ args.found("directory")))
{
@@ -53,7 +36,7 @@ auto wmain(s32 argc, wchar_t **argv) -> s32
return EXIT_FAILURE;
}
- s32 width = 640;
+ s32 width = 720;
s32 height = 480;
if (args.found("width")) width = args.getInt("width");
if (args.found("height")) height = args.getInt("height");
@@ -65,16 +48,22 @@ auto wmain(s32 argc, wchar_t **argv) -> s32
s32 xoffset;
s32 yoffset;
- if (sscanf(args.getString("offset").c_str(), "%d,%d", &xoffset, &yoffset) != 2) {
+ if (sscanf(args.getString("offset").c_str(), "%d,%d", &xoffset, &yoffset) != 2)
+ {
error("invalid offset in arguments");
return EXIT_FAILURE;
}
- if (!stl_global_init(false)) return EXIT_FAILURE;
-
+ bool gifscene = false;
if (args.found("package"))
{
- if (!asset_manager()->arm_package(args.getString("package"), args.found("target")))
+ std::string package_path = args.getString("package");
+ size_t slash = package_path.rfind("/");
+ std::string filename;
+ if (slash != std::string::npos) filename = package_path.substr(slash + 1);
+ else filename = package_path;
+ gifscene = filename == "gifscene.pkg";
+ if (!asset_manager()->arm_package(package_path, args.found("target")))
{
return EXIT_FAILURE;
}
@@ -86,15 +75,26 @@ auto wmain(s32 argc, wchar_t **argv) -> s32
asset_manager()->add_search_directory(args.getString("directory"));
}
- if (args.found("assets")) {
+ if (args.found("assets"))
+ {
asset_manager()->add_search_directory(args.getString("assets"));
}
- Scene *scene = Scene::parse("scene.json");
+ Scene *scene = Scene::parse(gifscene ? "gifscene.json" : "scene.json");
if (!scene) return EXIT_FAILURE;
- if (width <= 0) width = scene->width;
- if (height <= 0) height = scene->height;
+ if (width <= 0 || height <= 0)
+ {
+ width = scene->width;
+ height = scene->height;
+ }
+
+ if (args.getFlag("scale"))
+ {
+ f32 ratio = scene->width / (f32)scene->height;
+ if (ratio > 1.f) height = width / ratio;
+ else width = height * ratio;
+ }
Context *new_context = new Context();
if (!new_context->create_window(width, height, "mauri", args.getString("monitor")))
@@ -131,3 +131,35 @@ auto wmain(s32 argc, wchar_t **argv) -> s32
return EXIT_SUCCESS;
}
+
+#ifndef _WIN32
+auto main(s32 argc, char *argv[]) -> s32
+#else
+auto wmain(s32 argc, wchar_t **argv) -> s32
+#endif
+{
+ if (!nn_common_init()) return EXIT_FAILURE;
+
+ janus::ArgParser args(HELP_TEXT, "0.13");
+ args.newString("package p", "");
+ args.newString("directory d", "");
+ args.newString("assets a", "");
+ args.newInt("width w", 0);
+ args.newInt("height h", 0);
+ args.newString("offset o", "0,0");
+ args.newFlag("scale s");
+ args.newString("monitor m", "");
+ args.newString("target t", "");
+#ifndef _WIN32
+ args.parse(argc, argv);
+#else
+ args.wparse(argc, argv);
+#endif
+
+ if (!stl_global_init(false)) return EXIT_FAILURE;
+
+ s32 ret = stl_swallow_main(0, NULL, window_system_main, &args);
+ stl_global_close();
+
+ return ret;
+}
diff --git a/src/objects/object.cc b/src/objects/object.cc
index 4cf9790..6240c30 100644
--- a/src/objects/object.cc
+++ b/src/objects/object.cc
@@ -1,3 +1,5 @@
+#include "../log.h"
+
#include "object.h"
#include "scene.h"
#include "pass.h"
diff --git a/src/objects/pass.cc b/src/objects/pass.cc
index 0ed32ae..dc19378 100644
--- a/src/objects/pass.cc
+++ b/src/objects/pass.cc
@@ -1,5 +1,6 @@
#include "../gl.h"
-#include "../context.h"
+//#define AL_LOG_ENABLE_TRACE
+#include "../log.h"
#include "pass.h"
@@ -271,14 +272,13 @@ RenderPass::RenderPass(Engine *engine, Pass *pass)
}
}
-#ifdef _DEBUG_
- debug("PASS (%s)", this->command == COPY ? "copy" : "draw");
- debug(" object: %s (passthrough: %s, fullscreen %s)", this->object->name.c_str(),
+ trace("PASS (%s)", this->command == COPY ? "copy" : "draw");
+ trace(" object: %s (passthrough: %s, fullscreen %s)", this->object->name.c_str(),
BOOLSTR(this->object->passthrough), BOOLSTR(this->object->fullscreen));
- debug(" visible: %s", BOOLSTR(this->object->visible));
- debug(" shader: %s", this->shader->origin()->name.c_str());
- debug(" target: %s", pass->target.c_str());
- debug(" textures (%u):", this->shader->origin()->texture_count);
+ trace(" visible: %s", BOOLSTR(this->object->visible));
+ trace(" shader: %s", this->shader->origin()->name.c_str());
+ trace(" target: %s", pass->target.c_str());
+ trace(" textures (%u):", this->shader->origin()->texture_count);
for (u32 i = 0; i < this->shader->origin()->texture_count; i++)
{
switch (this->texture_types[i])
@@ -286,27 +286,26 @@ RenderPass::RenderPass(Engine *engine, Pass *pass)
case STATIC:
if (!this->shader->origin()->textures[i].empty() && pass->textures[i] == this->shader->origin()->textures[i])
{
- debug(" [%u] %s (DEFAULT)", i, this->shader->origin()->textures[i].c_str());
+ trace(" [%u] %s (DEFAULT)", i, this->shader->origin()->textures[i].c_str());
}
else
{
- debug(" [%u] %s", i, pass->textures[i].c_str());
+ trace(" [%u] %s", i, pass->textures[i].c_str());
}
break;
case PREVIOUS:
- debug(" [%u] previous", i);
+ trace(" [%u] previous", i);
break;
}
}
- debug(" uniforms (%u)", this->shader->uniforms.size());
+ trace(" uniforms (%u)", this->shader->uniforms.size());
for (Uniform *uniform : this->shader->uniforms)
{
- debug(" %s %s %s", uniform_type_to_string(uniform->type).c_str(),
+ trace(" %s %s %s", uniform_type_to_string(uniform->type).c_str(),
uniform->uname.c_str(), glm::to_string(*uniform->value).c_str());
}
- debug(" model: %s", BOOLSTR(this->model));
- debug(" combine: %s", BOOLSTR(this->combine));
-#endif
+ trace(" model: %s", BOOLSTR(this->model));
+ trace(" combine: %s", BOOLSTR(this->combine));
}
auto RenderPass::draw(Engine *engine) -> void
diff --git a/src/objects/scene.cc b/src/objects/scene.cc
index b9728c7..fa03622 100644
--- a/src/objects/scene.cc
+++ b/src/objects/scene.cc
@@ -37,6 +37,12 @@ auto Scene::parse(const std::string &path) -> Scene *
if (obj) scene->objects.emplace_back(obj);
}
+ if ((scene->width == 0 || scene->height == 0) && scene->objects.size() > 0)
+ {
+ scene->width = scene->objects[0]->size[0];
+ scene->height = scene->objects[0]->size[1];
+ }
+
return scene;
}
diff --git a/src/shader.cc b/src/shader.cc
index c8ba7b0..71597da 100644
--- a/src/shader.cc
+++ b/src/shader.cc
@@ -1,11 +1,11 @@
#include "shader.h"
-#include "gl.h"
#include "json.h"
+#include "gl.h"
#include "objects/object.h"
+static const char *version = "#version 330\n";
static const char *compat = \
-// "#define mul(x, y) ((y) * (x))\n"
"#define max(x, y) max(y, x)\n"
"#define frac fract\n"
"#define log10 log\n"
@@ -25,9 +25,6 @@ static const char *compat = \
"#define GLSL 1\n";
static const char *mul = "#define mul(x, y) ((y) * (x))\n";
-//static const char *mul = "";
-
-static const char *version = "#version 330\n";
namespace Mauri
{
diff --git a/src/shader.h b/src/shader.h
index 132b843..ae02887 100644
--- a/src/shader.h
+++ b/src/shader.h
@@ -1,6 +1,10 @@
#ifndef _SHADER_H
#define _SHADER_H
+#include <array>
+#include <sstream>
+#include <algorithm>
+
#include "texture.h"
#define MAX_TEXTURES 8
diff --git a/src/texture.cc b/src/texture.cc
index 4ea7211..3128a23 100644
--- a/src/texture.cc
+++ b/src/texture.cc
@@ -15,6 +15,7 @@ _Pragma("GCC diagnostic pop")
#include <s3tc.h>
#include "texture.h"
+#include "log.h"
using namespace Mauri;
diff --git a/src/util.h b/src/util.h
index db954c0..774409a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -3,19 +3,8 @@
extern "C" {
#include <al/types.h>
-#include <nnwt/socket.h>
}
-#include "log.h"
-
-#undef array
-#include <array>
-#include <vector>
-#include <unordered_map>
-#include <sstream>
-#include <algorithm>
-#include <cmath>
-
// Pause after every pass.
//#define FRAME_STEP
diff --git a/subprojects/glm.wrap b/subprojects/glm.wrap
new file mode 100644
index 0000000..dd52366
--- /dev/null
+++ b/subprojects/glm.wrap
@@ -0,0 +1,4 @@
+[wrap-git]
+url = https://github.com/g-truc/glm.git
+revision =7a878c23723a5122078e07b0d870016afd8854ce
+depth = 1
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index 49f70eb..038be37 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libalabaster
-revision = bb4d5418f2173bf9a2fa1c0be31c3eb9f2025a21
+revision = f6a8686b2c209b0e25cd64a587d6e9fb55200ec4
depth = 1
diff --git a/subprojects/libnaunet.wrap b/subprojects/libnaunet.wrap
index 6db8ca9..14965e1 100644
--- a/subprojects/libnaunet.wrap
+++ b/subprojects/libnaunet.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libnaunet
-revision = 822e754cafdc5a32c80c97ca015cd6f9008329c3
+revision = de6f6090f94b14ed98dac71956ff1bede48f1d8e
depth = 1
diff --git a/subprojects/lz4.wrap b/subprojects/lz4.wrap
index 7de9716..cdbcd4c 100644
--- a/subprojects/lz4.wrap
+++ b/subprojects/lz4.wrap
@@ -1,6 +1,6 @@
[wrap-git]
url = https://github.com/lz4/lz4.git
-revision = 4032c8c787e640f6cb217b89ee8057949bea1b01
+revision = 07c526c0afe28866cdcb7f84f4aadcebd0d63ba7
depth = 1
patch_directory = lz4
diff_files = lz4/move_meson_build.diff
diff --git a/subprojects/stela.wrap b/subprojects/stela.wrap
index a44296c..c6daa98 100644
--- a/subprojects/stela.wrap
+++ b/subprojects/stela.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/stela
-revision = 6fe15ce0d41bd55331cac006134b61c1704775c3
+revision = a3fb42db50542a7e1fca8662d08d3764006c9887
depth = 1
diff --git a/taro/config.h b/taro/config.h
index 0357d70..16fad79 100644
--- a/taro/config.h
+++ b/taro/config.h
@@ -1,7 +1,8 @@
#pragma once
-#include <al/str.h>
#include <al/log.h>
+#include <al/str.h>
+#include <al/array.h>
#include "file_ext.h"
@@ -11,18 +12,22 @@ struct config {
str config_dir;
str config_path;
str asset_dir;
- str wallpaper_dir;
+ array(str) wallpaper_dirs;
bool valid;
};
-AL_UNUSED_FUNCTION_PUSH
+AL_IGNORE_WARNING("-Wunused-function")
static void close_config(struct config *conf)
{
al_str_free(&conf->config_dir);
al_str_free(&conf->config_path);
al_str_free(&conf->asset_dir);
- al_str_free(&conf->wallpaper_dir);
+ str *dir;
+ al_array_foreach_ptr(conf->wallpaper_dirs, i, dir) {
+ al_str_free(dir);
+ }
+ al_array_free(conf->wallpaper_dirs);
}
static void maybe_replace_home_in_path(str *dest, const char *envhome, const char *path)
@@ -30,11 +35,11 @@ static void maybe_replace_home_in_path(str *dest, const char *envhome, const cha
if (path[0] == '~') {
al_str_from(dest, envhome);
path++;
- al_str_cat(dest, al_str_cr(path));
- } else if (al_str_cmp(al_str_cr(path), al_str_c("$HOME"), 0, 5) == 0) {
+ al_str_cat(dest, &al_str_cr(path));
+ } else if (al_str_cmp(&al_str_cr(path), &al_str_c("$HOME"), 0, 5) == 0) {
al_str_from(dest, envhome);
path += 5;
- al_str_cat(dest, al_str_cr(path));
+ al_str_cat(dest, &al_str_cr(path));
} else {
al_str_from(dest, path);
}
@@ -52,20 +57,40 @@ static void parse_config_json(struct config *conf, char *envhome, json_t *root)
json_t *assets = json_object_get(directories, "assets");
if (!assets || !json_is_string(assets) || al_strlen(json_string_value(assets)) == 0) {
- al_log_error("taro", "asset directory not set or invalid");
+ error("asset directory not set or invalid");
conf->valid = false;
goto out;
}
json_t *wallpapers = json_object_get(directories, "wallpapers");
- if (!wallpapers || !json_is_string(wallpapers) || al_strlen(json_string_value(wallpapers)) == 0) {
- al_log_error("taro", "wallpaper directory not set or invalid");
+ if (!wallpapers) {
+ conf->valid = false;
+ goto out;
+ }
+ size_t index;
+ json_t *value;
+ json_array_foreach(wallpapers, index, value) {
+ if (!json_is_string(value) || al_strlen(json_string_value(value)) == 0) {
+ error("invalid wallpaper directory (index: %zu)", index);
+ continue;
+ }
+ str path;
+ maybe_replace_home_in_path(&path, envhome, json_string_value(value));
+ if (!nn_dir_exists(&path)) {
+ warn("directory does not exist %.*s", al_str_x(&path));
+ al_str_free(&path);
+ continue;
+ }
+ al_array_push(conf->wallpaper_dirs, path);
+ }
+
+ if (conf->wallpaper_dirs.count == 0) {
+ error("no valid wallpaper directories");
conf->valid = false;
goto out;
}
maybe_replace_home_in_path(&conf->asset_dir, envhome, json_string_value(assets));
- maybe_replace_home_in_path(&conf->wallpaper_dir, envhome, json_string_value(wallpapers));
out:
json_decref(root);
@@ -79,7 +104,7 @@ static bool open_config(struct config *conf, bool create)
if (!envhome) goto err;
al_str_from(&conf->config_dir, envhome);
- al_str_cat(&conf->config_dir, al_str_c(CONFIG_PATH));
+ al_str_cat(&conf->config_dir, &al_str_c(CONFIG_PATH));
if (!nn_dir_exists(&conf->config_dir)) {
if (!create || !nn_dir_create(&conf->config_dir)) {
goto err;
@@ -87,13 +112,13 @@ static bool open_config(struct config *conf, bool create)
}
al_str_clone(&conf->config_path, &conf->config_dir);
- al_str_cat(&conf->config_path, al_str_c("/config.json"));
+ al_str_cat(&conf->config_path, &al_str_c("/config.json"));
if (!nn_file_exists(&conf->config_path)) {
if (nn_file_create(&conf->config_path)) {
json_t *blank = json_object();
json_t *directories = json_object();
json_object_set_new(directories, "assets", json_string_nocheck(""));
- json_object_set_new(directories, "wallpapers", json_string_nocheck(""));
+ json_object_set_new(directories, "wallpapers", json_array());
json_object_set_new(blank, "directories", directories);
nn_dump_json_and_decref(&conf->config_path, blank);
}
@@ -102,6 +127,8 @@ static bool open_config(struct config *conf, bool create)
json_t *root = nn_file_open_as_json(&conf->config_path);
if (!root) goto err;
+ al_array_init(conf->wallpaper_dirs);
+
parse_config_json(conf, envhome, root);
return true;
@@ -110,4 +137,4 @@ err:
return false;
}
-AL_UNUSED_FUNCTION_POP
+AL_IGNORE_WARNING_END
diff --git a/taro/daemon.c b/taro/daemon.c
index 6641b0b..4a70ceb 100644
--- a/taro/daemon.c
+++ b/taro/daemon.c
@@ -1,7 +1,6 @@
-#include <sys/wait.h>
-#include <al/types.h>
-#include <al/array.h>
+#define AL_LOG_SECTION "tarod"
#include <al/log.h>
+#include <al/array.h>
#include <al/str.h>
#include <nnwt/common.h>
#include <nnwt/event_loop.h>
@@ -10,6 +9,7 @@
#include <nnwt/socket.h>
#include <stl/window.h>
#include <jansson.h>
+#include <sys/wait.h>
#include "file_ext.h"
#include "config.h"
@@ -54,26 +54,24 @@ struct daemon {
struct nn_fs_event conf_event;
};
-static s32 parse_and_set_props(str *selection, str *wallpaper_path, bool *is_package)
+static s32 parse_and_set_props(str *selection, str *wallpaper_id, bool *is_package)
{
- s32 index = al_str_find(selection, '|');
- s32 rindex = al_str_rfind(selection, '|');
- if (index == -1 || rindex == -1 || index == rindex) {
+ u32 index = al_str_find(selection, '|');
+ u32 rindex = al_str_rfind(selection, '|');
+ if (index == AL_STR_NPOS || rindex == AL_STR_NPOS || index == rindex) {
return -1;
}
- // index < 2 or id->len == 0 is expected behavior
- // when a monitor has no wallpaper set.
+ // index < 2 or id->length == 0 is expected when a monitor has no wallpaper set.
if (index < 2) return 1;
- str *id = al_str_substr(selection, 0, index - 2);
- str *type = al_str_substr(selection, index - 2, index);
- if (id->len == 0) return 1;
+ str id = al_str_substr(selection, 0, index - 2);
+ str type = al_str_substr(selection, index - 2, index);
+ if (id.length == 0) return 1;
- al_str_cat(wallpaper_path, id);
-
- if (al_str_eq(type, al_str_c("_p"))) {
+ al_str_clone(wallpaper_id, &id);
+ if (al_str_eq(&type, &al_str_c("_p"))) {
*is_package = true;
- } else if (al_str_eq(type, al_str_c("_d"))) {
+ } else if (al_str_eq(&type, &al_str_c("_d"))) {
*is_package = false;
} else {
return -1;
@@ -85,7 +83,7 @@ static s32 parse_and_set_props(str *selection, str *wallpaper_path, bool *is_pac
static bool send_signal_to_pid(pid_t pid, s32 signal)
{
if (kill(pid, signal) != 0) {
- al_log_error("tarod", "kill(%d) failed (%s)", pid, nn_strerror(errno));
+ error("kill(%d) failed (%s)", pid, nn_strerror(errno));
return false;
}
return true;
@@ -105,11 +103,11 @@ static pid_t exec_or_exit(char *args[])
{
pid_t pid = fork();
if (pid == -1) {
- al_log_error("tarod", "fatal: failed to fork process, exiting...");
+ error("fatal: failed to fork process, exiting...");
exit(EXIT_FAILURE);
} else if (pid == 0) {
if (execvp(args[0], args) == -1) {
- al_log_error("tarod", "fatal: failed to run '%s', exiting...", args[0]);
+ error("fatal: failed to run '%s', exiting...", args[0]);
// 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);
@@ -131,12 +129,34 @@ static void maybe_kill_wallpaper(struct monitor *monitor)
}
}
+static bool search_wallpaper_dirs(struct daemon *dmon, str *id, str *path)
+{
+ bool found = false;
+ debug("searching for wallpaper of id %.*s", al_str_x(id));
+ str *dir;
+ al_array_foreach_ptr(dmon->conf.wallpaper_dirs, i, dir) {
+ debug("checking dir %.*s", al_str_x(dir));
+ al_str_clone(path, dir);
+ if (al_str_at(path, 1) != '/') al_str_cat(path, &al_str_c("/"));
+ al_str_cat(path, id);
+ if (nn_dir_exists(path)) {
+ debug("found wallpaper");
+ found = true;
+ break;
+ }
+ al_str_free(path);
+ }
+ if (!found) error("failed to find wallpaper of id %.*s", al_str_x(id));
+ al_str_free(id);
+ return found;
+}
+
static bool run_wallpaper(struct daemon *dmon, struct monitor *monitor)
{
maybe_kill_wallpaper(monitor);
if (!dmon->conf.valid) {
- al_log_error("tarod", "config invalid, not setting wallpaper on %s", monitor->os->name);
+ error("config invalid, not setting wallpaper on %s", monitor->os->name);
return false;
}
@@ -147,37 +167,42 @@ static bool run_wallpaper(struct daemon *dmon, struct monitor *monitor)
return false;
}
- if (selection.len == 0) {
- al_log_error("tarod", "config for %s is empty", monitor->os->name);
+ if (selection.length == 0) {
+ error("config for %s is empty", monitor->os->name);
al_str_free(&selection);
return false;
}
- str wallpaper_path;
- al_str_clone(&wallpaper_path, &dmon->conf.wallpaper_dir);
- al_str_cat(&wallpaper_path, al_str_c("/"));
+ str wallpaper_id = al_str_null();
bool is_package;
- s32 ret = parse_and_set_props(&selection, &wallpaper_path, &is_package);
+ s32 ret = parse_and_set_props(&selection, &wallpaper_id, &is_package);
al_str_free(&selection);
if (ret != 0) {
if (ret == -1) {
- al_log_error("tarod", "failed to set properties for wallpaper on %s", monitor->os->name);
+ error("failed to set properties for wallpaper on %s", monitor->os->name);
}
- al_str_free(&wallpaper_path);
+ al_str_free(&wallpaper_id);
return false;
}
- if (is_package) {
- al_str_cat(&wallpaper_path, al_str_c("/scene.pkg"));
+ str wallpaper_path = al_str_null();
+ if (!search_wallpaper_dirs(dmon, &wallpaper_id, &wallpaper_path)) {
+ return false;
}
- if ((is_package && !nn_file_exists(&wallpaper_path)) || (!is_package && !nn_dir_exists(&wallpaper_path))) {
- al_log_error("tarod", "wallpaper doesn't exist at %.*s", AL_STR_PRINTF(&wallpaper_path));
- al_str_free(&wallpaper_path);
- return false;
+ if (is_package) {
+ al_str_cat(&wallpaper_path, &al_str_c("/scene.pkg"));
+ if (!nn_file_exists(&wallpaper_path)) {
+ al_str_insert(&wallpaper_path, wallpaper_path.length - 9, &al_str_c("gif"));
+ if (!nn_file_exists(&wallpaper_path)) {
+ error("wallpaper at %.*s has no package file", al_str_x(&wallpaper_path));
+ al_str_free(&wallpaper_path);
+ return false;
+ }
+ }
}
- al_log_info("tarod", "setting wallpaper %.*s on %s", AL_STR_PRINTF(&wallpaper_path), monitor->os->name);
+ info("setting wallpaper %.*s on %s", al_str_x(&wallpaper_path), monitor->os->name);
char width[8], height[8];
al_snprintf(width, sizeof(width), "%d", monitor->os->width / monitor->os->scale);
@@ -187,10 +212,10 @@ static bool run_wallpaper(struct daemon *dmon, struct monitor *monitor)
char *c_str1 = al_str_to_c_str(&dmon->conf.asset_dir);
if (is_package) {
monitor->pid = exec_or_exit((char *[]){
- "mauri", "-p", c_str0, "-a", c_str1, "-m", monitor->os->name, "-w", width, "-h", height, NULL});
+ "./mauri", "-p", c_str0, "-a", c_str1, "-m", monitor->os->name, "-w", width, "-h", height, NULL});
} else {
monitor->pid = exec_or_exit((char *[]){
- "mauri", "-d", c_str0, "-a", c_str1, "-m", monitor->os->name, "-w", width, "-h", height, NULL});
+ "./mauri", "-d", c_str0, "-a", c_str1, "-m", monitor->os->name, "-w", width, "-h", height, NULL});
}
al_str_free(&wallpaper_path);
al_free(c_str0);
@@ -215,43 +240,21 @@ static void selection_fs_event_callback(void *userdata, struct inotify_event *ev
run_wallpaper(dmon, monitor);
}
-static void output_discovered_callback(void *userdata, struct stl_monitor *os_monitor)
+static void create_monitor(struct daemon *dmon, str *dir, struct stl_monitor *os_monitor)
{
- struct daemon *dmon = (struct daemon *)userdata;
-
- struct monitor *monitor;
- al_array_foreach(dmon->monitors, i, monitor) {
- if (strcmp(monitor->os->name, os_monitor->name) == 0) {
- if (monitor->pid == -1) {
- run_wallpaper(dmon, monitor);
- }
- return;
- }
- }
-
- 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 (!nn_dir_exists(&monitor_dir)) {
- if (!nn_dir_create(&monitor_dir)) return;
- }
-
- monitor = al_alloc_object(struct monitor);
+ struct monitor *monitor = al_alloc_object(struct monitor);
monitor->os = os_monitor;
monitor->dmon = dmon;
al_array_push(dmon->monitors, monitor);
- al_str_clone(&monitor->config_dir, &monitor_dir);
- al_str_free(&monitor_dir);
+ al_str_clone(&monitor->config_dir, dir);
al_str_clone(&monitor->selection_path, &monitor->config_dir);
- al_str_cat(&monitor->selection_path, al_str_c("/selection"));
+ al_str_cat(&monitor->selection_path, &al_str_c("/selection"));
if (!nn_file_exists(&monitor->selection_path)) {
nn_file_create(&monitor->selection_path);
- nn_file_open_and_replace(&monitor->selection_path, al_str_c("||"), false);
+ nn_file_open_and_replace(&monitor->selection_path, &al_str_c("||"), false);
}
monitor->pid = -1;
@@ -261,11 +264,36 @@ static void output_discovered_callback(void *userdata, struct stl_monitor *os_mo
run_wallpaper(dmon, monitor);
monitor->self_event_skip = false;
- nn_fs_event_init(&monitor->selection_event, &monitor->selection_path,
- NNWT_CLOSE_WRITE, selection_fs_event_callback, monitor);
+ nn_fs_event_init(&monitor->selection_event, &monitor->selection_path, NNWT_CLOSE_WRITE, selection_fs_event_callback, monitor);
nn_fs_event_start(&monitor->selection_event, &monitor->dmon->loop);
}
+static void output_discovered_callback(void *userdata, struct stl_monitor *os_monitor)
+{
+ struct daemon *dmon = (struct daemon *)userdata;
+
+ struct monitor *monitor;
+ al_array_foreach(dmon->monitors, i, monitor) {
+ if (strcmp(monitor->os->name, os_monitor->name) == 0) {
+ if (monitor->pid == -1) {
+ run_wallpaper(dmon, monitor);
+ }
+ return;
+ }
+ }
+
+ 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 (nn_dir_exists(&monitor_dir) || nn_dir_create(&monitor_dir)) {
+ create_monitor(dmon, &monitor_dir, os_monitor);
+ }
+
+ al_str_free(&monitor_dir);
+}
+
// https://vi.stackexchange.com/questions/25030/vim-not-firing-inotify-events-when-writing-file
static void conf_fs_event_callback(void *userdata, struct inotify_event *event)
{
@@ -273,7 +301,7 @@ static void conf_fs_event_callback(void *userdata, struct inotify_event *event)
(void)event;
close_config(&dmon->conf);
if (!open_config(&dmon->conf, false)) {
- al_log_error("tarod", "config became unavailable, exiting...");
+ error("config became unavailable, exiting...");
exit(EXIT_FAILURE);
}
struct monitor *monitor;
@@ -292,20 +320,20 @@ static void close_monitor(struct monitor *monitor)
static void ipc_handle_subscribe_success(json_t *root)
{
if (json_boolean_value(root)) {
- al_log_info("tarod", "successfully subscribed to sway background covered events");
+ info("successfully subscribed to sway background covered events");
} else {
- al_log_info("tarod", "failed to subscribe to sway background covered events");
+ info("failed to subscribe to sway background covered events");
}
}
static void ipc_handle_response(struct daemon *dmon, u8 *buf, u32 size)
{
- al_log_debug("tarod", "IN: %.*s", size, (char *)buf);
+ debug("IN: %.*s", size, (char *)buf);
json_error_t error;
json_t *root = json_loadb((char *)buf, size, 0, &error);
if (!root) {
- al_log_error("tarod", "failed to parse sway-ipc json payload (%s)", error.text);
+ error("failed to parse sway-ipc json payload (%s)", error.text);
goto done;
}
@@ -322,7 +350,7 @@ static void ipc_handle_response(struct daemon *dmon, u8 *buf, u32 size)
al_array_foreach(dmon->monitors, i, monitor) {
if (strcmp(monitor->os->name, json_string_value(output)) != 0) continue;
monitor->covered = covered;
- if (monitor->pid == -1) goto done;
+ if (monitor->pid == -1) break;
if (covered && !monitor->paused && signal_monitor(monitor, SIGSTOP)) {
monitor->paused = true;
} else if (!covered && monitor->paused && signal_monitor(monitor, SIGCONT)) {
@@ -336,6 +364,25 @@ done:
if (root) json_decref(root);
}
+static bool ipc_prepare_payload(struct daemon *dmon)
+{
+ json_t *payload = json_array();
+ json_array_append_new(payload, json_string_nocheck("background_covered"));
+ size_t length = json_dumpb(payload, NULL, 0, 0);
+ if (length == 0) return false;
+ size_t bufsize = SWAY_IPC_HEADER_LENGTH + length;
+ dmon->out.buf = al_malloc(bufsize);
+ al_memcpy(dmon->out.buf, "i3-ipc", al_strlen("i3-ipc"));
+ al_memcpy(dmon->out.buf + 6, &length, sizeof(s32));
+ s32 type = SWAY_IPC_SUBSCRIBE;
+ al_memcpy(dmon->out.buf + 10, &type, sizeof(s32));
+ length = json_dumpb(payload, (char *)dmon->out.buf + SWAY_IPC_HEADER_LENGTH, bufsize - SWAY_IPC_HEADER_LENGTH, 0);
+ json_decref(payload);
+ dmon->out.size = bufsize;
+ dmon->out.index = 0;
+ return true;
+}
+
static void ensure_ipc_buf_size(struct daemon *dmon, ssize_t size)
{
if (dmon->ipc.alloc < size) {
@@ -351,11 +398,10 @@ static void ipc_poll_callback(void *userdata, s32 revents)
if (revents & NNWT_POLL_WRITE) {
al_assert(!dmon->ipc_connected);
if (!dmon->ipc_connected) {
- al_log_info("tarod", "connected to sway ipc socket");
+ info("connected to sway ipc socket");
dmon->ipc_connected = true;
}
- ssize_t ret = nn_socket_write(&dmon->ipc_socket,
- dmon->out.buf + dmon->out.index, dmon->out.size - dmon->out.index);
+ ssize_t ret = nn_socket_write(&dmon->ipc_socket, dmon->out.buf + dmon->out.index, dmon->out.size - dmon->out.index);
if (ret <= 0) {
nn_signal_send(&dmon->quit_signal);
return;
@@ -370,29 +416,26 @@ static void ipc_poll_callback(void *userdata, s32 revents)
if (revents & NNWT_POLL_READ) {
if (dmon->ipc.index < SWAY_IPC_HEADER_LENGTH) {
- ssize_t ret = nn_socket_read(&dmon->ipc_socket,
- dmon->ipc.buf + dmon->ipc.index, SWAY_IPC_HEADER_LENGTH - dmon->ipc.index);
+ ssize_t ret = nn_socket_read(&dmon->ipc_socket, dmon->ipc.buf + dmon->ipc.index, SWAY_IPC_HEADER_LENGTH - dmon->ipc.index);
if (ret <= 0) {
nn_signal_send(&dmon->quit_signal);
return;
}
dmon->ipc.index += ret;
}
- if (dmon->ipc.index == SWAY_IPC_HEADER_LENGTH && !dmon->ipc.size) {
- dmon->ipc.size = *((s32 *)&dmon->ipc.buf[6]) + SWAY_IPC_HEADER_LENGTH;
+ if (!dmon->ipc.size && dmon->ipc.index == SWAY_IPC_HEADER_LENGTH) {
+ dmon->ipc.size = *(s32 *)(dmon->ipc.buf + 6) + SWAY_IPC_HEADER_LENGTH;
ensure_ipc_buf_size(dmon, dmon->ipc.size);
}
if (dmon->ipc.size) {
- ssize_t ret = nn_socket_read(&dmon->ipc_socket,
- dmon->ipc.buf + dmon->ipc.index, dmon->ipc.size - dmon->ipc.index);
+ ssize_t ret = nn_socket_read(&dmon->ipc_socket, dmon->ipc.buf + dmon->ipc.index, dmon->ipc.size - dmon->ipc.index);
if (ret <= 0) {
nn_signal_send(&dmon->quit_signal);
return;
}
dmon->ipc.index += ret;
if (dmon->ipc.index == dmon->ipc.size) {
- ipc_handle_response(dmon, dmon->ipc.buf + SWAY_IPC_HEADER_LENGTH,
- dmon->ipc.size - SWAY_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;
}
@@ -413,15 +456,10 @@ static void quit_signal_callback(void *userdata)
struct monitor *monitor;
al_array_foreach(dmon->monitors, i, monitor) {
nn_fs_event_stop(&monitor->selection_event);
- maybe_kill_wallpaper(monitor);
}
nn_fs_event_stop(&dmon->conf_event);
- if (!dmon->ipc_errored) {
- nn_poll_stop(&dmon->ipc_poll);
- }
- if (dmon->context_poll_fd != -1) {
- nn_poll_stop(&dmon->context_poll);
- }
+ if (!dmon->ipc_errored) nn_poll_stop(&dmon->ipc_poll);
+ if (dmon->context_poll_fd != -1) nn_poll_stop(&dmon->context_poll);
nn_signal_stop(&dmon->quit_signal);
}
@@ -444,7 +482,9 @@ static void sigint_handler(s32 signum)
s32 main(void)
{
- if (!nn_common_init() || !open_config(&dmon.conf, true)) return EXIT_FAILURE;
+ if (!nn_common_init() || !open_config(&dmon.conf, true)) {
+ return EXIT_FAILURE;
+ }
signal(SIGINT, sigint_handler);
signal(SIGCHLD, sigchld_handler);
@@ -470,13 +510,13 @@ s32 main(void)
al_str_from(&monitors, "");
struct monitor *monitor;
al_array_foreach(dmon.monitors, i, monitor) {
- al_str_cat(&monitors, al_str_cr(monitor->os->name));
- al_str_cat(&monitors, al_str_c("\n"));
+ al_str_cat(&monitors, &al_str_cr(monitor->os->name));
+ al_str_cat(&monitors, &al_str_c("\n"));
}
str monitors_path;
al_str_clone(&monitors_path, &dmon.conf.config_dir);
- al_str_cat(&monitors_path, al_str_c("/monitors"));
+ al_str_cat(&monitors_path, &al_str_c("/monitors"));
bool monitors_exists = nn_file_exists(&monitors_path) || nn_file_create(&monitors_path);
if (monitors_exists) {
nn_file_open_and_replace(&monitors_path, &monitors, false);
@@ -490,48 +530,27 @@ s32 main(void)
dmon.ipc.index = 0;
dmon.ipc.alloc = 0;
ensure_ipc_buf_size(&dmon, SWAY_IPC_HEADER_LENGTH);
-
- 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));
-
- 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 = SWAY_IPC_HEADER_LENGTH + len;
- dmon.out.index = 0;
-
+ ipc_prepare_payload(&dmon);
+ dmon.ipc_connected = false;
+ dmon.ipc_errored = false;
dmon.ipc_socket.type = NNWT_SOCKET_UNIX;
nn_socket_init(&dmon.ipc_socket, NNWT_SOCKET_NONBLOCKING);
nn_poll_init(&dmon.ipc_poll, ipc_poll_callback, &dmon);
nn_poll_set(&dmon.ipc_poll, nn_socket_get_fd(&dmon.ipc_socket), NNWT_POLL_WRITE);
- dmon.ipc_connected = false;
- dmon.ipc_errored = false;
char *swaysock = getenv("SWAYSOCK");
- if (swaysock && nn_socket_connect(&dmon.ipc_socket, al_str_cr(swaysock), 0)) {
+ if (swaysock && nn_socket_connect(&dmon.ipc_socket, &al_str_cr(swaysock), 0)) {
nn_poll_start(&dmon.ipc_poll, &dmon.loop);
} else {
dmon.ipc_errored = true;
- al_log_warn("tarod", "couldn't connect to sway ipc socket");
+ warn("couldn't connect to sway ipc socket");
}
- nn_fs_event_init(&dmon.conf_event, &dmon.conf.config_path,
- NNWT_CLOSE_WRITE, conf_fs_event_callback, &dmon);
+ nn_fs_event_init(&dmon.conf_event, &dmon.conf.config_path, NNWT_CLOSE_WRITE, conf_fs_event_callback, &dmon);
nn_fs_event_start(&dmon.conf_event, &dmon.loop);
nn_event_loop_run(&dmon.loop);
- al_array_foreach(dmon.monitors, i, monitor) {
- close_monitor(monitor);
- al_free(monitor);
- }
- al_array_free(dmon.monitors);
+ ret = EXIT_SUCCESS;
nn_fs_event_free(&dmon.conf_event);
@@ -539,11 +558,16 @@ s32 main(void)
al_free(dmon.ipc.buf);
nn_socket_close(&dmon.ipc_socket);
- nn_event_loop_destroy(&dmon.loop);
+out:
+ al_array_foreach(dmon.monitors, i, monitor) {
+ maybe_kill_wallpaper(monitor);
+ close_monitor(monitor);
+ al_free(monitor);
+ }
+ al_array_free(dmon.monitors);
- ret = EXIT_SUCCESS;
+ nn_event_loop_destroy(&dmon.loop);
-out:
close_config(&dmon.conf);
stl_global_close();
diff --git a/taro/db.c b/taro/db.c
index f3e5ede..da9af8a 100644
--- a/taro/db.c
+++ b/taro/db.c
@@ -1,3 +1,4 @@
+#define AL_LOG_SECTION "db"
#include <al/log.h>
#include "db.h"
@@ -9,125 +10,122 @@
static bool load_entry(struct entry *e)
{
struct nn_dir dir;
- if (!nn_dir_open(&dir, &e->path)) return false;
+ if (!nn_dir_open(&dir, &e->path)) {
+ return false;
+ }
- struct nn_dir_entry entry;
- while (nn_dir_read(&dir, &entry)) {
- if (entry.entry->d_type != DT_REG) {
- continue;
- }
+ str project_path;
+ al_str_clone(&project_path, &e->path);
+ al_str_cat(&project_path, &al_str_c("/"));
+ al_str_cat(&project_path, &al_str_c("project.json"));
- s32 index = al_str_rfind(al_str_cr(entry.entry->d_name), '.');
- if (index == -1) {
- continue;
- }
+ json_t *root = nn_file_open_as_json(&project_path);
+ al_str_free(&project_path);
+ if (!root) {
+ return false;
+ }
- if (al_str_eq(al_str_w(entry.entry->d_name, 0, index), al_str_c("project"))) {
- str package_path;
- al_str_clone(&package_path, &e->path);
- al_str_cat(&package_path, al_str_c("/scene.pkg"));
- e->has_package = nn_file_exists(&package_path);
- al_str_free(&package_path);
+ e->loaded = true;
- str project_path;
- al_str_clone(&project_path, &e->path);
- al_str_cat(&project_path, al_str_c("/"));
- al_str_cat(&project_path, al_str_cr(entry.entry->d_name));
+ str package_path;
+ al_str_clone(&package_path, &e->path);
+ al_str_cat(&package_path, &al_str_c("/scene.pkg"));
+ e->has_package = nn_file_exists(&package_path);
+ if (!e->has_package) {
+ al_str_insert(&package_path, package_path.length - 9, &al_str_c("gif"));
+ e->has_package = nn_file_exists(&package_path);
+ }
+ al_str_free(&package_path);
- json_t *root = nn_file_open_as_json(&project_path);
- al_str_free(&project_path);
- if (!root) continue;
- e->loaded = true;
+ json_t *title = json_object_get(root, "title");
+ if (title) {
+ al_wstr_from_cstr(&e->title, (char *)json_string_value(title));
+ } else {
+ al_wstr_from_cstr(&e->title, "<Title_Not_Set>");
+ }
- json_t *title = json_object_get(root, "title");
- if (title) {
- al_wstr_from_cstr(&e->title, (char *)json_string_value(title));
- }
+ json_t *preview = json_object_get(root, "preview");
+ if (preview) {
+ al_str_clone(&e->preview_path, &e->path);
+ al_str_cat(&e->preview_path, &al_str_c("/"));
+ al_str_cat(&e->preview_path, &al_str_cr(json_string_value(preview)));
+ e->has_preview = nn_file_exists(&e->preview_path);
+ } else {
+ e->has_preview = false;
+ }
- json_t *preview = json_object_get(root, "preview");
- if (preview) {
- al_str_clone(&e->preview_path, &e->path);
- al_str_cat(&e->preview_path, al_str_c("/"));
- al_str_cat(&e->preview_path, al_str_cr(json_string_value(preview)));
- e->has_preview = nn_file_exists(&e->preview_path);
- }
+ json_t *general = json_object_get(root, "general");
+ if (general) {
+ al_array_init(e->props);
+ json_t *properties = json_object_get(general, "properties");
+ if (properties) {
+ void *iter = json_object_iter(properties);
+ json_t *iter_obj;
+ json_t *text, *type;
+ while (iter) {
+ iter_obj = json_object_iter_value(iter);
+ text = json_object_get(iter_obj, "text");
+ if (!text || strcmp(json_string_value(text), "ui_browse_properties_scheme_color") == 0) {
+ iter = json_object_iter_next(properties, iter);
+ continue;
+ }
- json_t *general = json_object_get(root, "general");
- if (!general) {
- continue;
- }
+ struct prop p = { 0 };
+ p.type = PROP_TYPE_UNKNOWN;
+ al_wstr_from_cstr(&p.title, (char *)json_string_value(text));
+ type = json_object_get(iter_obj, "type");
+ if (!type) {
+ // Silently ignore for now. Is "Notes" a special case? see 1918273588.
+ //warn("improperly handled prop (%.*s)", al_str_x(&e->id));
+ iter = json_object_iter_next(properties, iter);
+ continue;
+ }
- al_array_init(e->props);
- json_t *properties = json_object_get(general, "properties");
- if (properties) {
- void *iter = json_object_iter(properties);
- json_t *iter_obj;
- json_t *text, *type;
- while (iter) {
- iter_obj = json_object_iter_value(iter);
- text = json_object_get(iter_obj, "text");
- if (!text || strcmp(json_string_value(text), "ui_browse_properties_scheme_color") == 0) {
+ str type_str = al_str_cr(json_string_value(type));
+ if (al_str_eq(&type_str, &al_str_c("bool"))) {
+ json_t *value = json_object_get(iter_obj, "value");
+ if (!value) {
+ warn("improperly handled bool (%.*s)", al_str_x(&e->id));
iter = json_object_iter_next(properties, iter);
continue;
}
-
- struct prop p;
- p.type = PROP_TYPE_UNKNOWN;
- al_wstr_from_cstr(&p.title, (char *)json_string_value(text));
- type = json_object_get(iter_obj, "type");
- if (!type) {
- // Silently ignore for now. Is "Notes" a special case? see 1918273588.
- //log_warn("improperly handled prop (%.*s)", AL_STR_PRINTF(&e->id));
+ p.value = al_malloc(sizeof(bool));
+ *((bool *)p.value) = json_boolean_value(value);
+ p.type = PROP_TYPE_BOOL;
+ } else if (al_str_eq(&type_str, &al_str_c("slider"))) {
+ json_t *value = json_object_get(iter_obj, "value");
+ json_t *min = json_object_get(iter_obj, "min");
+ json_t *max = json_object_get(iter_obj, "max");
+ if (!value || !min || !max) {
+ warn("improperly handled slider (%.*s)", al_str_x(&e->id));
iter = json_object_iter_next(properties, iter);
continue;
}
-
- str *type_str = al_str_cr(json_string_value(type));
- if (al_str_eq(type_str, al_str_c("bool"))) {
- json_t *value = json_object_get(iter_obj, "value");
- if (!value) {
- al_log_warn("db", "improperly handled bool (%.*s)", AL_STR_PRINTF(&e->id));
- iter = json_object_iter_next(properties, iter);
- continue;
- }
- p.value = al_malloc(sizeof(bool));
- *((bool *)p.value) = json_boolean_value(value);
- p.type = PROP_TYPE_BOOL;
- } else if (al_str_eq(type_str, al_str_c("slider"))) {
- json_t *value = json_object_get(iter_obj, "value");
- json_t *min = json_object_get(iter_obj, "min");
- json_t *max = json_object_get(iter_obj, "max");
- if (!value || !min || !max) {
- al_log_warn("db", "improperly handled slider (%.*s)", AL_STR_PRINTF(&e->id));
- iter = json_object_iter_next(properties, iter);
- continue;
- }
- json_t *step = json_object_get(iter_obj, "step");
- p.value = al_malloc(sizeof(struct slider_value));
- struct slider_value *slider = (struct slider_value *)p.value;
- slider->value = REAL_OR_INT(value);
- slider->min = REAL_OR_INT(min);
- slider->max = REAL_OR_INT(max);
- if (step) {
- slider->step = REAL_OR_INT(step);
- } else {
- // Assume the default step is 1.0.
- slider->step = 1.0;
- }
- p.type = PROP_TYPE_SLIDER;
+ json_t *step = json_object_get(iter_obj, "step");
+ p.value = al_malloc(sizeof(struct slider_value));
+ struct slider_value *slider = (struct slider_value *)p.value;
+ slider->value = REAL_OR_INT(value);
+ slider->min = REAL_OR_INT(min);
+ slider->max = REAL_OR_INT(max);
+ if (step) {
+ slider->step = REAL_OR_INT(step);
} else {
- //al_log_warn("db", "unhandled type. (%.*s)", AL_STR_PRINTF(type_str));
+ // Assume the default step is 1.0.
+ slider->step = 1.0;
}
+ p.type = PROP_TYPE_SLIDER;
+ } else {
+ //warn("unhandled type. (%.*s)", al_str_x(type_str));
+ }
- al_array_push(e->props, p);
+ al_array_push(e->props, p);
- iter = json_object_iter_next(properties, iter);
- }
+ iter = json_object_iter_next(properties, iter);
}
}
}
- nn_dir_close(&dir);
+ json_decref(root);
return e->loaded;
}
@@ -143,18 +141,24 @@ bool db_ensure_entry_loaded(struct db *db, struct entry *e)
break;
}
}
+ return false;
}
- return e->loaded;
+ al_assert(e->loaded);
+ return true;
+}
+
+void db_init(struct db *db)
+{
+ al_array_init(db->entries);
}
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;
- return 0;
+ s64 ac = ((struct entry *)a)->compare;
+ s64 bc = ((struct entry *)b)->compare;
+ if (ac > bc) return 1;
+ else if (bc > ac) return -1;
+ else return 0;
}
bool db_load_entries(struct db *db, str *path)
@@ -162,39 +166,50 @@ bool db_load_entries(struct db *db, str *path)
struct nn_dir dir;
if (!nn_dir_open(&dir, path)) return false;
- al_array_init(db->entries);
struct nn_dir_entry entry;
while (nn_dir_read(&dir, &entry)) {
- if (!(entry.entry->d_type == DT_DIR || entry.entry->d_type == DT_UNKNOWN)) {
- continue;
- }
- if (strcmp(entry.entry->d_name, ".") == 0 ||
- strcmp(entry.entry->d_name, "..") == 0) {
+ if (!(entry.entry->d_type == DT_DIR || entry.entry->d_type == DT_UNKNOWN) ||
+ (strcmp(entry.entry->d_name, ".") == 0) ||
+ (strcmp(entry.entry->d_name, "..") == 0)) {
continue;
}
struct entry e = { 0 };
- e.loaded = false;
- al_str_clone(&e.id, al_str_cr(entry.entry->d_name));
- al_str_clone(&e.path, path);
- al_str_cat(&e.path, al_str_c("/"));
- al_str_cat(&e.path, &e.id);
- if (!nn_dir_exists(&e.path)) {
+ nn_dir_entry_get_name(&entry, &e.id);
+ bool dupe = false;
+ struct entry *re;
+ al_array_foreach_ptr(db->entries, i, re) {
+ if (al_str_eq(&re->id, &e.id)) {
+ dupe = true;
+ break;
+ }
+ }
+ if (dupe) {
al_str_free(&e.id);
- al_str_free(&e.path);
continue;
}
+ nn_dir_entry_get_path(&entry, &dir, &e.path);
+ bool error;
+ e.compare = al_str_to_long(&e.id, 10, &error);
+ if (error) e.compare = -1;
+ e.loaded = false;
al_array_push(db->entries, e);
}
- al_array_sort(db->entries, struct entry, wallpaper_id_compare);
+ nn_dir_close(&dir);
return true;
}
+void db_sort(struct db *db)
+{
+ al_array_sort(db->entries, struct entry, wallpaper_id_compare);
+}
+
void db_unload(struct db *db)
{
struct entry *e;
al_array_foreach_ptr(db->entries, i, e) {
+ al_str_free(&e->id);
al_str_free(&e->path);
al_str_free(&e->preview_path);
al_wstr_free(&e->title);
diff --git a/taro/db.h b/taro/db.h
index 78f4c77..abc0d03 100644
--- a/taro/db.h
+++ b/taro/db.h
@@ -22,9 +22,10 @@ struct prop {
};
struct entry {
- bool loaded;
str id;
str path;
+ s64 compare;
+ bool loaded;
bool has_package;
str preview_path;
bool has_preview;
@@ -36,6 +37,8 @@ struct db {
array(struct entry) entries;
};
+void db_init(struct db *db);
bool db_load_entries(struct db *db, str *path);
+void db_sort(struct db *db);
bool db_ensure_entry_loaded(struct db *db, struct entry *e);
void db_unload(struct db *db);
diff --git a/taro/file_ext.c b/taro/file_ext.c
index 1d1bbdc..78fdba8 100644
--- a/taro/file_ext.c
+++ b/taro/file_ext.c
@@ -1,3 +1,4 @@
+#define AL_LOG_SECTION "file_ext"
#include <al/log.h>
#include "file_ext.h"
@@ -31,9 +32,9 @@ json_t *nn_file_open_as_json(str *path)
return NULL;
}
json_error_t error;
- json_t *root = json_loadb(s.data, s.len, 0, &error);
+ json_t *root = json_loadb(s.data, s.length, 0, &error);
if (!root) {
- al_log_error("file_ext", "json_loadb(%.*s, %u) failed (%s)", AL_STR_PRINTF(path), s.len, error.text);
+ error("json_loadb(%.*s, %u) failed (%s)", al_str_x(path), s.length, error.text);
}
al_str_free(&s);
return root;
@@ -44,8 +45,8 @@ bool nn_dump_json_and_decref(str *path, json_t *root)
str s;
size_t size = json_dumpb(root, NULL, 0, JSON_INDENT(4));
al_str_sized(&s, size);
- s.len = json_dumpb(root, s.data, size, JSON_INDENT(4));
- if (s.len == 0) {
+ s.length = json_dumpb(root, s.data, size, JSON_INDENT(4));
+ if (s.length == 0) {
json_decref(root);
return false;
}
diff --git a/taro/meson.build b/taro/meson.build
index 487c671..ffee872 100644
--- a/taro/meson.build
+++ b/taro/meson.build
@@ -4,9 +4,11 @@ if not notcurses.found() or not notcurses_core.found()
notcurses_opts = cmake.subproject_options()
notcurses_opts.add_cmake_defines({ 'CMAKE_BUILD_TYPE': is_debug ? 'Debug' : 'Release' })
notcurses_opts.add_cmake_defines({ 'USE_CXX': false })
+ notcurses_opts.add_cmake_defines({ 'USE_DOCTEST': false })
notcurses_opts.add_cmake_defines({ 'USE_PANDOC': false })
- notcurses_opts.add_cmake_defines({ 'USE_POC': false })
notcurses_opts.add_cmake_defines({ 'BUILD_EXECUTABLES': false })
+ notcurses_opts.add_cmake_defines({ 'BUILD_FFI_LIBRARY': false })
+ notcurses_opts.add_cmake_defines({ 'USE_POC': false })
notcurses_opts.add_cmake_defines({ 'USE_STATIC': false })
notcurses_opts.add_cmake_defines({ 'USE_MULTIMEDIA': 'ffmpeg' })
notcurses_proj = cmake.subproject('notcurses', options: notcurses_opts)
diff --git a/taro/ui.c b/taro/ui.c
index 95ce402..8df6370 100644
--- a/taro/ui.c
+++ b/taro/ui.c
@@ -1,3 +1,4 @@
+#define AL_LOG_SECTION "taro"
#include <al/log.h>
#include <al/lib.h>
#include <al/random.h>
@@ -22,7 +23,7 @@
#define UI_RESERVE_HEIGHT 2
#define GRID_TILE_WIDTH 18 // img = 16
-#define GRID_TILE_HEIGHT 10 // img = 8
+#define GRID_TILE_HEIGHT 9 // img = 8
#define GRID_TEXT_WIDTH (GRID_TILE_WIDTH - 4)
@@ -33,7 +34,7 @@
// INFO_HEIGHT = term_rows + UI_RESERVE_HEIGHT
#define INFO_IMG_WIDTH 24
-#define INFO_IMG_HEIGHT 12
+#define INFO_IMG_HEIGHT 11
#define INFO_TEXT_WIDTH (GRID_TEXT_WIDTH + 6)
@@ -141,12 +142,12 @@ static s32 get_entry_from_config(struct ui *u)
s32 ret = -1;
str config;
if (nn_file_open_and_read_wholly(&u->m.selection_path, &config, false)) {
- s32 index = al_str_find(&config, '|');
- if (index > 2) {
- str *id = al_str_substr(&config, 0, index - 2);
+ u32 index = al_str_find(&config, '|');
+ if (index != AL_STR_NPOS && index > 2) {
+ str id = al_str_substr(&config, 0, index - 2);
struct entry *e;
al_array_foreach_ptr(u->db.entries, i, e) {
- if (al_str_eq(&e->id, id)) {
+ if (al_str_eq(&e->id, &id)) {
ret = i;
break;
}
@@ -163,14 +164,11 @@ static void select_monitor(struct ui *u)
u->m.set_monitor = u->m.selected_monitor;
al_str_free(&u->m.selection_path);
al_str_clone(&u->m.selection_path, &u->conf.config_dir);
- al_str_cat(&u->m.selection_path, al_str_c("/"));
+ al_str_cat(&u->m.selection_path, &al_str_c("/"));
char *monitor = al_array_at(u->m.monitors, u->m.set_monitor);
- al_str_cat(&u->m.selection_path, al_str_cr(monitor));
- al_str_cat(&u->m.selection_path, al_str_c("/selection"));
+ al_str_cat(&u->m.selection_path, &al_str_cr(monitor));
+ al_str_cat(&u->m.selection_path, &al_str_c("/selection"));
u->g.set_index = get_entry_from_config(u);
- if (u->g.set_index >= 0) {
- u->g.selected_index = u->g.set_index;
- }
}
static void write_selected_entry_to_config(struct ui *u)
@@ -182,22 +180,24 @@ static void write_selected_entry_to_config(struct ui *u)
} else {
al_str_clone(&config, &u->n.e->id);
if (u->n.e->has_package) {
- al_str_cat(&config, al_str_c("_p"));
+ al_str_cat(&config, &al_str_c("_p"));
} else {
- al_str_cat(&config, al_str_c("_d"));
+ al_str_cat(&config, &al_str_c("_d"));
}
- al_str_cat(&config, al_str_c("|false,100|"));
+ al_str_cat(&config, &al_str_c("|false,100|"));
struct prop *p;
al_array_foreach_ptr(u->n.e->props, i, p) {
switch (p->type) {
- case PROP_TYPE_BOOL:
- al_str_cat(&config, *((bool *)p->value) ? al_str_c("true") : al_str_c("false"));
+ case PROP_TYPE_BOOL: {
+ bool value = *((bool *)p->value);
+ al_str_cat(&config, value ? &al_str_c("true") : &al_str_c("false"));
break;
}
- al_str_cat(&config, al_str_c(","));
+ }
+ al_str_cat(&config, &al_str_c(","));
}
- if (al_str_at(&config, config.len - 1) == ',') {
- config.len--;
+ if (al_str_at(&config, config.length - 1) == ',') {
+ config.length--;
}
}
nn_file_open_and_replace(&u->m.selection_path, &config, true);
@@ -207,69 +207,6 @@ static void write_selected_entry_to_config(struct ui *u)
// Interface
// ---------
-static bool wide_string_to_width(wstr *w, u32 index, s32 max_width, void (*func)(void *, wchar_t), void *userdata)
-{
- 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;
- }
- func(userdata, wc);
- } while (++i < w->len);
- return i == w->len;
-}
-
-static void set_scrolling_text(struct scrolling_text *text, wstr *s, s32 y, s32 x, s32 width)
-{
- 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 draw_func(void *userdata, wchar_t wc)
-{
- struct ncplane *p = (struct ncplane *)userdata;
- ncplane_putwc(p, wc);
-}
-
-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, 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) {
- 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);
- }
-}
-
static bool init_info(struct info *n)
{
al_array_init(n->props);
@@ -300,19 +237,19 @@ static bool init_monitors(struct ui *u, struct monitors *m)
al_array_init(m->monitors);
str monitors_path;
al_str_clone(&monitors_path, &u->conf.config_dir);
- al_str_cat(&monitors_path, al_str_c("/monitors"));
+ al_str_cat(&monitors_path, &al_str_c("/monitors"));
str monitors;
bool ret = nn_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, has `tarod` run?");
+ error("failed to open monitor list, has `tarod` run?");
return false;
}
str monitor_dir;
- str monitor = al_str_zero();
+ str monitor = al_str_null();
while (al_str_get_line(&monitors, '\n', &monitor)) {
al_str_clone(&monitor_dir, &u->conf.config_dir);
- al_str_cat(&monitor_dir, al_str_c("/"));
+ al_str_cat(&monitor_dir, &al_str_c("/"));
al_str_cat(&monitor_dir, &monitor);
if (nn_dir_exists(&monitor_dir)) {
char *c_str = al_str_to_c_str(&monitor);
@@ -320,23 +257,33 @@ static bool init_monitors(struct ui *u, struct monitors *m)
}
al_str_free(&monitor_dir);
}
- if (!u->m.monitors.size) {
+ if (!u->m.monitors.count) {
u->m.set_monitor = -1;
u->m.selected_monitor = -1;
}
al_array_sort(m->monitors, char *, monitor_cmp);
u->g.set_index = -1;
+ u->g.selected_index = -1;
if (m->selected_monitor >= 0) {
select_monitor(u);
}
- if (u->db.entries.size > 0) {
- u->g.selected_index = (u->g.set_index >= 0) ? u->g.set_index : 0;
+ if (u->db.entries.count > 0) {
+ // set_index will be set in set_grid_tiles() otherwise.
+ if (u->g.set_index == -1) u->g.selected_index = 0;
} else {
- u->g.selected_index = -1;
+ u->selected_menu = MENU_MONITORS;
}
return true;
}
+static s32 void_print_callback(void *userdata, u8 level, char *message)
+{
+ (void)userdata;
+ (void)level;
+ (void)message;
+ return 0;
+}
+
static bool init_ui(struct ui *u)
{
if (!init_monitors(u, &u->m)) return false;
@@ -344,9 +291,10 @@ static bool init_ui(struct ui *u)
if (!init_image_pool(&u->pool)) return false;
if (!init_info(&u->n)) return false;
if (!(u->nc = notcurses_init(NULL, stdin))) {
- al_log_error("taro", "failed to initialize notcurses");
+ error("failed to initialize notcurses");
return false;
}
+ al_set_print(void_print_callback, NULL);
return true;
}
@@ -360,21 +308,16 @@ struct image *get_image_from_pool(struct image_pool *pool, str *path)
}
}
- bool gif = false;
- s32 index = al_str_rfind(path, '.');
- if (index != -1) {
- if (al_str_eq(al_str_w(path->data, index, 4), al_str_c(".gif"))) {
- gif = true;
- }
- }
-
- if (pool->images.size >= pool->max) {
+ if (pool->images.count >= pool->max) {
for (u32 i = 0; i < pool->chunk_size; i++) {
ncvisual_destroy(al_array_at(pool->images, i).v);
}
al_array_remove_range(pool->images, 0, pool->chunk_size);
}
+ u32 index = al_str_rfind(path, '.');
+ bool gif = index != AL_STR_NPOS && al_str_cmp(path, &al_str_c(".gif"), index, 4) == 0;
+
char *c_str = al_str_to_c_str(path);
struct image img = {
.hash = hash,
@@ -422,6 +365,48 @@ static s32 resize_cb(struct ncplane *p)
return 0;
}
+static bool wide_string_to_width(wstr *w, u32 index, s32 max_width,
+ void (*func)(void *, wchar_t), void *userdata)
+{
+ 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;
+ }
+ func(userdata, wc);
+ } while (++i < w->length);
+ return i == w->length;
+}
+
+static void set_scrolling_text(struct scrolling_text *text, wstr *s, s32 y, s32 x, s32 width)
+{
+ 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 draw_func(void *userdata, wchar_t wc)
+{
+ struct ncplane *p = (struct ncplane *)userdata;
+ ncplane_putwc(p, wc);
+}
+
+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 maybe_destroy_info(struct info *n)
{
if (n->ip) {
@@ -430,6 +415,28 @@ static void maybe_destroy_info(struct info *n)
}
}
+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, INFO_IMG_HEIGHT + 1, tx, INFO_TEXT_WIDTH);
+
+ n->selected_prop = 0;
+ n->props.count = 0;
+ struct prop *p;
+ struct info_prop *ip;
+ al_array_foreach_ptr(u->n.e->props, i, p) {
+ 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);
+ }
+}
+
static bool layout_info(struct ui *u, struct info *n, struct ncplane *parent, s32 height)
{
maybe_destroy_info(n);
@@ -467,14 +474,15 @@ static void maybe_destroy_grid(struct grid *g)
ncplane_destroy(t->p);
t->p = NULL;
}
- g->tiles.size = 0;
+ g->tiles.count = 0;
ncplane_destroy(g->p);
g->p = NULL;
}
static void set_grid_tiles(struct ui *u, struct grid *g)
{
- u32 index = (g->selected_index - (g->selected_index % g->tiles_per_page));
+ u32 selected = (g->selected_index >= 0) ? g->selected_index : g->set_index;
+ u32 index = (selected - (selected % g->tiles_per_page));
g->min_index = index;
g->max_index = index;
@@ -486,16 +494,22 @@ static void set_grid_tiles(struct ui *u, struct grid *g)
t->blitted = false;
ncplane_erase(t->p);
- while (g->max_index < (s32)u->db.entries.size) {
+ while (g->max_index < (s32)u->db.entries.count) {
t->e = &al_array_at(u->db.entries, g->max_index);
if (db_ensure_entry_loaded(&u->db, t->e)) {
break;
}
+ // Entry failed to load.
+ if (g->set_index == g->max_index) {
+ g->set_index = -1;
+ } else if (g->set_index > g->max_index) {
+ g->set_index--;
+ }
}
- al_assert(g->max_index <= (s32)u->db.entries.size);
+ al_assert(g->max_index <= (s32)u->db.entries.count);
- if (g->max_index == (s32)u->db.entries.size) {
+ if (g->max_index == (s32)u->db.entries.count) {
t->e = NULL;
t->text.s = NULL;
ncplane_erase(t->ip);
@@ -503,18 +517,31 @@ static void set_grid_tiles(struct ui *u, struct grid *g)
}
s32 width = al_wstr_width(&t->e->title);
- s32 tx = 2 + (width < GRID_TEXT_WIDTH ? (GRID_TEXT_WIDTH - width) / 2 : 0);
+ 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++;
}}
+ if (g->selected_index == -1) {
+ if (g->set_index == -1) {
+ // set_index failed to load above.
+ g->selected_index = 0;
+ set_grid_tiles(u, g);
+ return;
+ } else {
+ g->selected_index = g->set_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;
+ // selected_index goes over max_index is undefined.
+ // Handling entry load failure in this way saves us from a lot of preloading.
+ 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)
@@ -588,7 +615,7 @@ static bool layout_grid(struct ui *u, struct grid *g, struct ncplane *parent, s3
al_array_push(g->tiles, t);
}}
- if (g->selected_index >= 0) {
+ if (u->db.entries.count > 0) {
set_grid_tiles(u, g);
}
@@ -622,8 +649,8 @@ bool layout_ui(struct ui *u)
{
bool even = u->term_cols % 2 == 0;
- u32 rwidth = (even) ? UI_RESERVE_WIDTH + 1 : UI_RESERVE_WIDTH;
- u32 rheight = (even) ? UI_RESERVE_HEIGHT + 1 : UI_RESERVE_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 < GRID_TILE_WIDTH + rwidth || u->term_rows < GRID_TILE_HEIGHT + rheight) {
return false;
@@ -699,30 +726,48 @@ void draw_monitors(struct ui *u, struct monitors *m)
{
if (!u->layed_out) return;
- s32 width = 0;
char *monitor;
- al_array_foreach(m->monitors, i, monitor) {
- width += strlen(monitor) + 1;
+ const char *no_monitors = "NO MONITORS";
+
+ s32 width = 0;
+ if (u->m.selected_monitor == -1) {
+ width = al_strlen(no_monitors);
+ } else {
+ al_array_foreach(m->monitors, i, monitor) {
+ width += strlen(monitor) + 1;
+ }
}
ncplane_set_styles(u->mp, NCSTYLE_BOLD);
s32 w = ncplane_dim_x(u->mp);
s32 x = ((w - 1) - width) / 2;
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) {
+
+ if (u->m.selected_monitor == -1) {
+ if (u->selected_menu == MENU_MONITORS) {
ncplane_set_bg_palindex(u->mp, 8);
ncplane_set_fg_palindex(u->mp, 0);
- } else if (i == (u32)m->set_monitor) {
+ } else {
ncplane_set_bg_palindex(u->mp, 7);
ncplane_set_fg_palindex(u->mp, 0);
- m->flash -= MAX(TIMER_UPDATE_BASE * (u->delay / 1000000.f), 0.001f);
- } else {
- 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);
+ ncplane_putstr(u->mp, no_monitors);
+ } else {
+ 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_palindex(u->mp, 8);
+ ncplane_set_fg_palindex(u->mp, 0);
+ } else if (i == (u32)m->set_monitor) {
+ ncplane_set_bg_palindex(u->mp, 7);
+ ncplane_set_fg_palindex(u->mp, 0);
+ m->flash -= MAX(TIMER_UPDATE_BASE * (u->delay / 1000000.f), 0.001f);
+ } else {
+ 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);
+ }
}
}
@@ -733,7 +778,7 @@ void draw_info(struct ui *u, struct info *n, bool update_text)
ncplane_erase(u->ip);
struct ncvisual_options vopts = { 0 };
- vopts.scaling = NCSCALE_STRETCH;
+ vopts.scaling = PIXEL_BLIT ? NCSCALE_SCALE_HIRES : 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) {
@@ -749,6 +794,8 @@ void draw_info(struct ui *u, struct info *n, bool update_text)
u->delay = 100000;
}
}
+ } else if (!img) {
+ ncplane_erase(n->ip);
}
ncplane_set_fg_default(u->ip);
@@ -759,7 +806,7 @@ void draw_info(struct ui *u, struct info *n, bool update_text)
u32 i, index;
for (i = 0; i < n->max_props; i++) {
index = i + selected_index;
- if (index >= n->props.size) {
+ if (index >= n->props.count) {
break;
}
ip = &al_array_at(n->props, index);
@@ -782,7 +829,7 @@ void draw_info(struct ui *u, struct info *n, bool update_text)
}
}
}
- if (ip && i == n->max_props && i != n->props.size) {
+ if (ip && i == n->max_props && i != n->props.count) {
ncplane_putstr_yx(u->ip, ip->text.y + 1, (INFO_IMG_WIDTH - 1) / 2, "V");
}
}
@@ -810,13 +857,15 @@ 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_STRETCH;
+ vopts.scaling = PIXEL_BLIT ? NCSCALE_SCALE_HIRES : 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 && t->e->has_preview) {
+ ncplane_erase(t->p);
+ if (!t->e) continue;
+ if (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;
@@ -824,7 +873,6 @@ void draw_grid_tiles(struct ui *u, struct grid *g, bool update_text)
t->blitted = true;
}
}
- 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) {
@@ -836,7 +884,7 @@ void draw_grid_tiles(struct ui *u, struct grid *g, bool update_text)
ncplane_set_bg_alpha(t->p, NCALPHA_TRANSPARENT);
ncplane_set_styles(t->p, NCSTYLE_NONE);
}
- if (t->text.s && t->text.s->len) {
+ if (t->text.s && !al_wstr_is_empty(t->text.s)) {
draw_scrolling_text(t->p, &t->text, update_text);
}
index++;
@@ -849,14 +897,6 @@ static bool handle_input_monitors(struct ui *u, struct monitors *m, struct ncinp
bool should_redraw_grid = false;
bool should_redraw_info = false;
switch (input->id) {
- case NCKEY_RIGHT:
- case 'l':
- if ((u32)(m->selected_monitor + 1) >= m->monitors.size) {
- return false;
- }
- m->selected_monitor++;
- should_redraw_monitors = true;
- break;
case NCKEY_LEFT:
case 'h':
if (m->selected_monitor - 1 < 0) {
@@ -865,7 +905,16 @@ static bool handle_input_monitors(struct ui *u, struct monitors *m, struct ncinp
m->selected_monitor--;
should_redraw_monitors = true;
break;
+ case NCKEY_RIGHT:
+ case 'l':
+ if ((u32)(m->selected_monitor + 1) >= m->monitors.count) {
+ return false;
+ }
+ m->selected_monitor++;
+ should_redraw_monitors = true;
+ break;
case NCKEY_RETURN:
+ case NCKEY_SPACE: {
select_monitor(u);
m->flash = FLASH_DURATION;
should_redraw_monitors = true;
@@ -873,6 +922,7 @@ static bool handle_input_monitors(struct ui *u, struct monitors *m, struct ncinp
should_redraw_info = true;
break;
}
+ }
if (should_redraw_monitors) {
draw_monitors(u, m);
}
@@ -891,14 +941,6 @@ static bool handle_input_info(struct ui *u, struct info *n, struct ncinput *inpu
{
bool should_redraw_info = false;
switch (input->id) {
- case NCKEY_DOWN:
- case 'j':
- if ((u32)(n->selected_prop + 1) >= n->props.size) {
- return false;
- }
- n->selected_prop++;
- should_redraw_info = true;
- break;
case NCKEY_UP:
case 'k':
if (n->selected_prop - 1 < 0) {
@@ -907,9 +949,17 @@ static bool handle_input_info(struct ui *u, struct info *n, struct ncinput *inpu
n->selected_prop--;
should_redraw_info = true;
break;
+ case NCKEY_DOWN:
+ case 'j':
+ if ((u32)(n->selected_prop + 1) >= n->props.count) {
+ return false;
+ }
+ n->selected_prop++;
+ should_redraw_info = true;
+ break;
case NCKEY_RETURN:
case NCKEY_SPACE: {
- if (!n->props.size) return false;
+ if (!n->props.count) return false;
struct prop *p = &al_array_at(n->e->props, n->selected_prop);
if (p->type == PROP_TYPE_BOOL) {
*((bool *)p->value) = !*((bool *)p->value);
@@ -922,7 +972,7 @@ static bool handle_input_info(struct ui *u, struct info *n, struct ncinput *inpu
case 'h':
case NCKEY_RIGHT:
case 'l':
- if (!n->props.size) return false;
+ if (!n->props.count) return false;
struct prop *p = &al_array_at(n->e->props, n->selected_prop);
if (p->type == PROP_TYPE_SLIDER) {
struct slider_value *value = (struct slider_value *)p->value;
@@ -956,8 +1006,10 @@ 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 (!u->db.entries.count) {
+ return false;
+ }
+ s32 max = (s32)u->db.entries.count - 1;
if (g->selected_index == max) {
return false;
}
@@ -986,19 +1038,6 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu
bool should_redraw_grid = false;
bool should_redraw_info = false;
switch (input->id) {
- case NCKEY_RIGHT:
- case 'l':
- if (g->selected_index + 1 >= (s32)u->db.entries.size) {
- return false;
- }
- if ((g->selected_index + 1) % g->tiles_x == 0) {
- should_update_grid = grid_next_page(u, &u->g, g->tiles_x - 1);
- } else {
- g->selected_index++;
- }
- should_redraw_info = true;
- should_redraw_grid = true;
- break;
case NCKEY_LEFT:
case 'h':
if (g->selected_index - 1 < 0) {
@@ -1012,12 +1051,16 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu
should_redraw_info = true;
should_redraw_grid = true;
break;
- case NCKEY_DOWN:
- case 'j':
- if (g->selected_index + g->tiles_x > g->max_index) {
+ case NCKEY_RIGHT:
+ case 'l':
+ if (g->selected_index + 1 >= (s32)u->db.entries.count) {
return false;
}
- g->selected_index += g->tiles_x;
+ if ((g->selected_index + 1) % g->tiles_x == 0) {
+ should_update_grid = grid_next_page(u, &u->g, g->tiles_x - 1);
+ } else {
+ g->selected_index++;
+ }
should_redraw_info = true;
should_redraw_grid = true;
break;
@@ -1030,15 +1073,24 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu
should_redraw_info = true;
should_redraw_grid = true;
break;
- case 'b':
- if (grid_previous_page(&u->g, 0)) {
+ case NCKEY_DOWN:
+ case 'j':
+ if (g->selected_index + g->tiles_x > g->max_index) {
+ return false;
+ }
+ g->selected_index += g->tiles_x;
+ should_redraw_info = true;
+ should_redraw_grid = true;
+ break;
+ case 'f':
+ if (grid_next_page(u, &u->g, 0)) {
should_update_grid = true;
should_redraw_info = true;
should_redraw_grid = true;
}
break;
- case 'f':
- if (grid_next_page(u, &u->g, 0)) {
+ case 'b':
+ if (grid_previous_page(&u->g, 0)) {
should_update_grid = true;
should_redraw_info = true;
should_redraw_grid = true;
@@ -1051,7 +1103,10 @@ static bool handle_input_grid(struct ui *u, struct grid *g, struct ncinput *inpu
should_redraw_info = true;
break;
case 's': {
- s32 max = (s32)u->db.entries.size - 1;
+ if (!u->db.entries.count) {
+ return false;
+ }
+ s32 max = (s32)u->db.entries.count - 1;
g->selected_index = al_rand() % max;
should_update_grid = true;
should_redraw_info = true;
@@ -1099,18 +1154,14 @@ static void input_poll_callback(void *userdata, s32 revents)
if (input.id == NCKEY_TAB) {
switch (u->selected_menu) {
case MENU_GRID:
- if (u->n.props.size > 0) {
+ if (u->n.props.count > 0) {
u->selected_menu = MENU_INFO;
} else {
u->selected_menu = MENU_MONITORS;
}
break;
case MENU_INFO:
- if (u->m.selected_monitor >= 0) {
- u->selected_menu = MENU_MONITORS;
- } else {
- u->selected_menu = MENU_GRID;
- }
+ u->selected_menu = MENU_MONITORS;
break;
case MENU_MONITORS:
u->selected_menu = MENU_GRID;
@@ -1174,20 +1225,23 @@ s32 main(void)
if (!nn_common_init()) return EXIT_FAILURE;
if (!open_config(&u.conf, false)) {
- al_log_error("taro", "failed to open config, run `tarod` at least once to generate needed config");
+ error("failed to open config, run `tarod` at least once to generate needed config");
return EXIT_FAILURE;
}
s32 ret = EXIT_FAILURE;
- if (!db_load_entries(&u.db, &u.conf.wallpaper_dir)) {
- goto out;
- }
+ db_init(&u.db);
- if (!init_ui(&u)) {
- goto out;
+ str *dir;
+ al_array_foreach_ptr(u.conf.wallpaper_dirs, i, dir) {
+ if (!db_load_entries(&u.db, dir)) goto out;
}
+ db_sort(&u.db);
+
+ if (!init_ui(&u)) goto out;
+
notcurses_stddim_yx(u.nc, &u.term_rows, &u.term_cols);
ncplane_set_resizecb(notcurses_stdplane(u.nc), resize_cb);
@@ -1195,7 +1249,7 @@ s32 main(void)
if (!layout_ui(&u)) {
notcurses_stop(u.nc);
- al_log_error("taro", "failed to layout ui, your terminal window is probably too small");
+ error("failed to layout ui, your terminal window is probably too small");
goto out;
}
u.layed_out = true;