From 7404c6374ff31f9b7332f253fbf2e22e40cfe53c Mon Sep 17 00:00:00 2001 From: zsuper Date: Mon, 12 Jan 2026 01:15:37 -0800 Subject: [PATCH 1/4] feat: recursive traversal for image loading Adds the `--recursive` flag, which simply keeps traversing directories (excluding `.` and `..`) and adding their images to the loader. Additionally modified `.gitignore` to ignore the output `hellpaper` binary, as it is platform-specific. --- .gitignore | 1 + hellpaper.c | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f2271c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +hellpaper diff --git a/hellpaper.c b/hellpaper.c index 1043336..839ab06 100644 --- a/hellpaper.c +++ b/hellpaper.c @@ -163,6 +163,7 @@ static Particle particles[MAX_POSSIBLE_PARTICLES]; static int wallpaper_count = 0; static bool print_filename_only = false; +static bool recursive_traversal = false; static atomic_int next_load_index = 0; static atomic_bool loader_running = true; @@ -387,9 +388,14 @@ static void LoadWallpapers(const char *dir) struct dirent *entry; while ((entry = readdir(dp)) != NULL && wallpaper_count < g_max_wallpapers) { - if (entry->d_type != DT_REG) + bool is_dot_dir = (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0); + if (entry->d_type == DT_DIR && recursive_traversal && !is_dot_dir) { - continue; + char *fullpath; + if (asprintf(&fullpath, "%s/%s", dir, entry->d_name) == -1) continue; + LoadWallpapers(fullpath); + } else if (entry->d_type != DT_REG) { + continue; } const char *ext = strrchr(entry->d_name, '.'); if (!ext || (strcasecmp(ext, ".jpg") != 0 && strcasecmp(ext, ".jpeg") != 0 && strcasecmp(ext, ".png") != 0)) @@ -516,6 +522,7 @@ void print_help() printf("OPTIONS:\n"); printf(" --help Show this help message and exit.\n"); printf(" --filename Print only the filename of the selected wallpaper to stdout.\n"); + printf(" --recursive Recursively traverse the input directory for wallpapers.\n"); printf(" --width Set the initial window width.\n"); printf(" --height Set the initial window height.\n"); printf(" --startup-effect \n"); @@ -743,6 +750,10 @@ int main(int argc, char **argv) { print_filename_only = true; } + else if (strcmp(argv[i], "--recursive") == 0) + { + recursive_traversal = true; + } else if (strcmp(argv[i], "--width") == 0 && i + 1 < argc) { g_win_width = atoi(argv[++i]); From df0882540a62d21686ee1af61b94037298e7c4e6 Mon Sep 17 00:00:00 2001 From: zsuper Date: Mon, 12 Jan 2026 02:22:27 -0800 Subject: [PATCH 2/4] add: flake.nix & -O3 compile flag --- Makefile | 2 +- flake.lock | 26 ++++++++++++++++++++++++++ flake.nix | 23 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/Makefile b/Makefile index abeee07..85ac909 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ CC = gcc SRC = hellpaper.c TARGET = hellpaper -CFLAGS = -Wall -O2 +CFLAGS = -Wall -O3 LIBS = -lraylib -lm #-lGL -lpthread -ldl -lrt PREFIX ?= /usr/local diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..686857c --- /dev/null +++ b/flake.lock @@ -0,0 +1,26 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1768200421, + "narHash": "sha256-2wqgHF4f6MoDBOAZCbEGmenPDTiPCVJv2/Np8YZ3C3s=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "aca8e67997d3195aeb603b8808da6f464741c13e", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..ea46113 --- /dev/null +++ b/flake.nix @@ -0,0 +1,23 @@ +{ + inputs.nixpkgs.url = "github:nixos/nixpkgs"; + + outputs = + { nixpkgs, ... }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in + { + devShells.${system}.default = pkgs.mkShell { + packages = with pkgs; [ + pkg-config + (raylib.overrideAttrs ( + self: { + cmakeFlags = self.cmakeFlags ++ [ "-DSUPPORT_FILEFORMAT_JPG=ON" ]; + } + )) + bear + ]; + }; + }; +} From 2921fa4d62c20a54e45bf5d66ff13fd9e8672d5c Mon Sep 17 00:00:00 2001 From: zsuper Date: Mon, 12 Jan 2026 02:56:01 -0800 Subject: [PATCH 3/4] update flake.nix --- flake.nix | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index ea46113..836b9c6 100644 --- a/flake.nix +++ b/flake.nix @@ -6,18 +6,40 @@ let system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; + raylibWithJPG = pkgs.raylib.overrideAttrs (finalAttrs: { + cmakeFlags = finalAttrs.cmakeFlags ++ [ "-DSUPPORT_FILEFORMAT_JPG=ON" ]; + }); in { devShells.${system}.default = pkgs.mkShell { - packages = with pkgs; [ - pkg-config - (raylib.overrideAttrs ( - self: { - cmakeFlags = self.cmakeFlags ++ [ "-DSUPPORT_FILEFORMAT_JPG=ON" ]; - } - )) - bear + packages = [ + raylibWithJPG + pkgs.pkg-config # For local compilation + linking with raylib + pkgs.bear # For generating compile_commands.json (used by LSPs) ]; }; + + packages.${system} = rec { + hellpaper = pkgs.stdenv.mkDerivation { + pname = "hellpaper"; + version = "1.0.0"; + src = ./.; + + nativeBuildInputs = [ raylibWithJPG ]; + + buildPhase = '' + make + ''; + + installPhase = '' + mkdir -p $out/bin + mv ./hellpaper $out/bin/ + ''; + + meta.mainProgram = "hellpaper"; + }; + + default = hellpaper; + }; }; } From 1ea5d02c4d7178d17c08438ca09aab00e28e6fe2 Mon Sep 17 00:00:00 2001 From: zsuper Date: Mon, 12 Jan 2026 23:50:28 -0800 Subject: [PATCH 4/4] feat + bugfix: HIDPI & keybind remaps - set HIDPI flag (since I was getting inconsistent behavior across my laptop and monitor) - added/modified the following keybinds: - q: quits application (just like Escape) - Esc: modified how app reads the keycode slightly, so hellpaper now reacts to virtual/remapped keys (i.e. my CAPS key is mapped to Esc. Clicking either now works) - Left Shift: toggles fullscreen preview rather than just enables it (very helpful for keyboard warriors) - Reformatted help message (idk why we had 40,000 printfs, const C strings can be concatenated into 1 printf) - Increased key repeat delay from `0.1f -> 0.15f`. Subtle change, but make keyboard navigation a lot more manageable - Hellpaper now deselects AND clears its full search buffer when entering Search Mode (fixed a LOT of bugs surrounding searching multiple times) --- hellpaper.c | 105 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 43 deletions(-) diff --git a/hellpaper.c b/hellpaper.c index 839ab06..b492c72 100644 --- a/hellpaper.c +++ b/hellpaper.c @@ -513,49 +513,54 @@ void ParseConfigFile() void print_help() { - printf("Hellpaper - wallpaper picker for Linux.\n\n"); - printf("USAGE:\n"); - printf(" hellpaper [OPTIONS] [PATH]\n\n"); - printf("ARGUMENTS:\n"); - printf(" [PATH] Optional path to the directory containing wallpapers.\n"); - printf(" Defaults to '~/Pictures/'.\n\n"); - printf("OPTIONS:\n"); - printf(" --help Show this help message and exit.\n"); - printf(" --filename Print only the filename of the selected wallpaper to stdout.\n"); - printf(" --recursive Recursively traverse the input directory for wallpapers.\n"); - printf(" --width Set the initial window width.\n"); - printf(" --height Set the initial window height.\n"); - printf(" --startup-effect \n"); - printf(" --keypress-effect \n"); - printf(" --exit-effect \n"); - printf(" Override the configured visual effects on certain events.\n"); - printf(" Available effects: none, glitch, blur, pixelate, shake, collapse, reveal\n\n"); - printf("KEYBINDINGS:\n"); - printf(" NAVIGATION:\n"); - printf(" h, j, k, l / Arrows Move selection. Keys repeat when held.\n"); - printf(" Mouse Wheel Scroll through wallpapers.\n"); - printf(" Ctrl + Mouse Wheel Zoom thumbnail scaling.\n\n"); - printf(" ACTIONS:\n"); - printf(" Enter / LMB Select the highlighted wallpaper and exit.\n"); - printf(" L-Shift / RMB Show a full-screen preview of the highlighted wallpaper.\n"); - printf(" / Enter search mode. Type to filter wallpapers by name.\n"); - printf(" ESC Closes Preview, then Search, then the App.\n\n"); - printf(" VIEW MODES:\n"); - printf(" 1, 2, 3, 4 Switch between different layout modes:\n"); - printf(" 1: Grid\n"); - printf(" 2: Horizontal River\n"); - printf(" 3: Vertical River\n"); - printf(" 4: Wave\n\n"); - printf("CONFIGURATION:\n"); - printf(" Hellpaper can be fully customized by editing the configuration file located at:\n"); - printf(" ~/.config/hellpaper/hellpaper.conf\n"); + printf("Hellpaper - wallpaper picker for Linux.\n\n" + "USAGE:\n" + " hellpaper [OPTIONS] [PATH]\n\n" + "ARGUMENTS:\n" + " [PATH] Optional path to the directory containing wallpapers.\n" + " Defaults to '~/Pictures/'.\n\n" + "OPTIONS:\n" + " --help Show this help message and exit.\n" + " --filename Print only the filename of the selected wallpaper to stdout.\n" + " --recursive Recursively traverse the input directory for wallpapers.\n" + " --width Set the initial window width.\n" + " --height Set the initial window height.\n" + " --startup-effect \n" + " --keypress-effect \n" + " --exit-effect \n" + " Override the configured visual effects on certain events.\n" + " Available effects: none, glitch, blur, pixelate, shake, collapse, reveal\n\n" + "KEYBINDINGS:\n" + " NAVIGATION:\n" + " h, j, k, l / Arrows Move selection. Keys repeat when held.\n" + " q Quit application.\n" + " Mouse Wheel Scroll through wallpapers.\n" + " Ctrl + Mouse Wheel Zoom thumbnail scaling.\n\n" + " ACTIONS:\n" + " Enter / LMB Select the highlighted wallpaper and exit.\n" + " L-Shift / RMB Toggle a full-screen preview of the highlighted wallpaper.\n" + " / Enter search mode. Type to filter wallpapers by name.\n" + " ESC Closes Preview, then Search, then the App.\n\n" + " VIEW MODES:\n" + " 1, 2, 3, 4 Switch between different layout modes:\n" + " 1: Grid\n" + " 2: Horizontal River\n" + " 3: Vertical River\n" + " 4: Wave\n\n" + "CONFIGURATION:\n" + " Hellpaper can be fully customized by editing the configuration file located at:\n" + " ~/.config/hellpaper/hellpaper.conf\n"); } void UpdateAndDrawScene(int filteredCount, int* filteredIndices, float delta, bool isPreviewing, bool isSearching) { int sw = GetScreenWidth(); int sh = GetScreenHeight(); - Vector2 mouse = GetMousePosition(); + + Vector2 mouse_raw = GetMousePosition(); + float scaleX = (float)GetRenderWidth() / (float)GetScreenWidth(); + float scaleY = (float)GetRenderHeight() / (float)GetScreenHeight(); + Vector2 mouse = (Vector2){ mouse_raw.x * scaleX, mouse_raw.y * scaleY }; if (g_modeTransitionTimer > 0) { @@ -774,7 +779,7 @@ int main(int argc, char **argv) wallpaper_path = default_path; } LoadWallpapers(wallpaper_path); - + SetConfigFlags(FLAG_WINDOW_HIGHDPI); InitWindow(g_win_width, g_win_height, "Hellpaper"); SetExitKey(KEY_NULL); SetTargetFPS(g_max_fps); @@ -805,7 +810,7 @@ int main(int argc, char **argv) TriggerEffect(g_startupEffect, 1.0f); float keyRepeatTimer = 0.0f; - const float KEY_REPEAT_DELAY = 0.1f; + const float KEY_REPEAT_DELAY = 0.15f; while (!WindowShouldClose()) { @@ -892,15 +897,23 @@ int main(int argc, char **argv) if (!blockActions) { int key = GetKeyPressed(); - if (key != 0 && !isSearching) + + // Process exit keycodes first + if ((key == KEY_Q || key == KEY_ESCAPE)) { - TriggerEffect(g_keypressEffect, 0.4f); + break; } + if (key >= KEY_ONE && key < KEY_ONE + NUM_MODES) { g_targetMode = key - KEY_ONE; g_modeTransitionTimer = g_modeTransitionDuration; } + + if (!isSearching) + { + TriggerEffect(g_keypressEffect, 0.4f); + } } bool ateEscKey = false; @@ -920,7 +933,13 @@ int main(int argc, char **argv) } else { - if (!blockActions && IsKeyPressed(KEY_SLASH)) { isSearching = true; searchBufferCount = 0; searchBuffer[0] = '\0'; } + if (!blockActions && IsKeyPressed(KEY_SLASH)) + { + isSearching = true; + g_hoveredIndex = -1; + searchBufferCount = 0; + memset(searchBuffer, 0, 256); + } } @@ -1023,7 +1042,7 @@ int main(int argc, char **argv) } } - if (isPreviewing && (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsKeyPressed(KEY_ESCAPE) || IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))) + if (isPreviewing && (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsKeyPressed(KEY_ESCAPE) || IsKeyPressed(KEY_LEFT_SHIFT) || IsMouseButtonPressed(MOUSE_BUTTON_RIGHT))) { if(IsKeyPressed(KEY_ESCAPE)) ateEscKey = true; if (atomic_load(&isFullTextureReady)) UnloadTexture(fullPreviewTexture);