refactor: code cleaning
4
Makefile
@@ -1,10 +1,10 @@
|
||||
all: wallpaper distance_field_generator
|
||||
|
||||
wallpaper: src/wallpaper.c
|
||||
gcc -O3 -Werror -Wall -Wextra -I./vendor/raylib-5.5_linux_amd64/include/ -o wallpaper src/wallpaper.c -L./vendor/raylib-5.5_linux_amd64/lib -l:libraylib.a -lm
|
||||
gcc -O3 -Werror -Wall -Wextra -pedantic -I./vendor/raylib-5.5_linux_amd64/include/ -o wallpaper src/wallpaper.c -L./vendor/raylib-5.5_linux_amd64/lib -l:libraylib.a -lm
|
||||
|
||||
distance_field_generator: src/distance_field_generator.c
|
||||
gcc -Werror -Wall -Wextra -fopenmp -I./vendor/raylib-5.5_linux_amd64/include/ -o distance_field_generator src/distance_field_generator.c -L./vendor/raylib-5.5_linux_amd64/lib -l:libraylib.a -lm
|
||||
gcc -Werror -Wall -Wextra -pedantic -fopenmp -I./vendor/raylib-5.5_linux_amd64/include/ -o distance_field_generator src/distance_field_generator.c -L./vendor/raylib-5.5_linux_amd64/lib -l:libraylib.a -lm
|
||||
|
||||
install: wallpaper
|
||||
sudo install -D -m 755 wallpaper /usr/bin/wallpaper
|
||||
|
||||
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.9 MiB After Width: | Height: | Size: 7.9 MiB |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 7.9 MiB |
|
Before Width: | Height: | Size: 25 KiB |
@@ -3,13 +3,11 @@
|
||||
#include <math.h>
|
||||
#include <omp.h>
|
||||
|
||||
bool is_inside_shape(Color pixel)
|
||||
{
|
||||
static bool is_inside_shape(Color pixel) {
|
||||
return pixel.r == 255 && pixel.g == 255 && pixel.b == 255;
|
||||
}
|
||||
|
||||
Image generate_distance_field(Image input_texture, int search_radius)
|
||||
{
|
||||
static Image generate_distance_field(Image input_texture, int search_radius) {
|
||||
int width = input_texture.width;
|
||||
int height = input_texture.height;
|
||||
|
||||
@@ -20,25 +18,20 @@ Image generate_distance_field(Image input_texture, int search_radius)
|
||||
TraceLog(LOG_INFO, "Using %d threads for parallel processing", omp_get_max_threads());
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 64)
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
if (omp_get_thread_num() == 0 && y % (height / 10) == 0)
|
||||
{
|
||||
for (int y = 0; y < height; y += 1) {
|
||||
if (omp_get_thread_num() == 0 && y % (height / 10) == 0) {
|
||||
TraceLog(LOG_INFO, "Progress: %d%%", (y * 100) / height);
|
||||
}
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int x = 0; x < width; x += 1) {
|
||||
int index = y * width + x;
|
||||
bool is_inside = is_inside_shape(input_pixels[index]);
|
||||
|
||||
float min_distance_sq = search_radius * search_radius;
|
||||
bool found_boundary = false;
|
||||
|
||||
for (int dy = -search_radius; dy <= search_radius; dy += 1)
|
||||
{
|
||||
for (int dx = -search_radius; dx <= search_radius; dx += 1)
|
||||
{
|
||||
for (int dy = -search_radius; dy <= search_radius; dy += 1) {
|
||||
for (int dx = -search_radius; dx <= search_radius; dx += 1) {
|
||||
int sample_x = x + dx;
|
||||
int sample_y = y + dy;
|
||||
|
||||
@@ -47,11 +40,9 @@ Image generate_distance_field(Image input_texture, int search_radius)
|
||||
int sample_index = sample_y * width + sample_x;
|
||||
bool sample_is_inside = is_inside_shape(input_pixels[sample_index]);
|
||||
|
||||
if (sample_is_inside != is_inside)
|
||||
{
|
||||
if (sample_is_inside != is_inside) {
|
||||
float distance_sq = dx * dx + dy * dy;
|
||||
if (distance_sq < min_distance_sq)
|
||||
{
|
||||
if (distance_sq < min_distance_sq) {
|
||||
min_distance_sq = distance_sq;
|
||||
found_boundary = true;
|
||||
}
|
||||
@@ -62,22 +53,15 @@ Image generate_distance_field(Image input_texture, int search_radius)
|
||||
float distance = found_boundary ? sqrtf(min_distance_sq) : (float)search_radius;
|
||||
float normalized_distance = distance / (float)search_radius;
|
||||
|
||||
unsigned char distance_value;
|
||||
if (is_inside)
|
||||
{
|
||||
distance_value = (unsigned char)(127.0f - 127.0f * normalized_distance);
|
||||
}
|
||||
else
|
||||
{
|
||||
distance_value = (unsigned char)(128.0f + 127.0f * normalized_distance);
|
||||
}
|
||||
unsigned char distance_value = is_inside ?
|
||||
(unsigned char)(127.0f - 127.0f * normalized_distance) :
|
||||
(unsigned char)(128.0f + 127.0f * normalized_distance);
|
||||
|
||||
output_pixels[index] = (Color){distance_value, distance_value, distance_value, 255};
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < width * height; i++)
|
||||
{
|
||||
for (int i = 0; i < width * height; i += 1) {
|
||||
ImageDrawPixel(&distance_field, i % width, i / width, output_pixels[i]);
|
||||
}
|
||||
|
||||
@@ -89,8 +73,7 @@ Image generate_distance_field(Image input_texture, int search_radius)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
{
|
||||
if (argc < 3) {
|
||||
TraceLog(LOG_ERROR, "Usage: %s <input_image> <output_image> [search_radius]", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
@@ -100,15 +83,13 @@ int main(int argc, char *argv[])
|
||||
int search_radius = (argc >= 4) ? atoi(argv[3]) : 100;
|
||||
|
||||
Image input_image = LoadImage(input_path);
|
||||
if (input_image.data == NULL)
|
||||
{
|
||||
if (input_image.data == NULL) {
|
||||
TraceLog(LOG_ERROR, "Error: Failed to load input image '%s'", input_path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Image distance_field_image = generate_distance_field(input_image, search_radius);
|
||||
if (!ExportImage(distance_field_image, output_path))
|
||||
{
|
||||
if (!ExportImage(distance_field_image, output_path)) {
|
||||
TraceLog(LOG_ERROR, "Failed to save output image '%s'", output_path);
|
||||
UnloadImage(input_image);
|
||||
UnloadImage(distance_field_image);
|
||||
|
||||
111
src/wallpaper.c
@@ -6,41 +6,41 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
||||
#define WIDTH 1920
|
||||
#define HEIGHT 1080
|
||||
|
||||
#define BALL_RADIUS 100.0f
|
||||
#define INFLUENCE 200.0f
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
int x;
|
||||
int y;
|
||||
int cx;
|
||||
int cy;
|
||||
Vector2 pos;
|
||||
Vector2 origin;
|
||||
float angle;
|
||||
float angle_step;
|
||||
} Star;
|
||||
|
||||
static Star stars[] = {
|
||||
{ .name = "star_1.png", .x = 4, .y = 528, .cx = 165, .cy = 166, .angle = 90.0, .angle_step = -0.025 },
|
||||
{ .name = "star_2.png", .x = 215, .y = 809, .cx = 82, .cy = 83, .angle = 90.0, .angle_step = 0.05 },
|
||||
{ .name = "star_3.png", .x = 333, .y = 781, .cx = 25, .cy = 31, .angle = 90.0, .angle_step = 0.15 },
|
||||
{ .name = "star_4.png", .x = 956, .y = 405, .cx = 166, .cy = 167, .angle = 90.0, .angle_step = -0.1 },
|
||||
{ .name = "star_5.png", .x = 1164, .y = 570, .cx = 66, .cy = 70, .angle = 90.0, .angle_step = 0.075 },
|
||||
{ .name = "star_1.png", .pos = { 4, 528 }, .origin = {165, 166}, .angle = 90.0, .angle_step = -0.025 },
|
||||
{ .name = "star_2.png", .pos = { 215, 809 }, .origin = {82, 83}, .angle = 90.0, .angle_step = 0.05 },
|
||||
{ .name = "star_3.png", .pos = { 333, 781 }, .origin = {25, 31}, .angle = 90.0, .angle_step = 0.15 },
|
||||
{ .name = "star_4.png", .pos = { 956, 405 }, .origin = {166, 167}, .angle = 90.0, .angle_step = -0.1 },
|
||||
{ .name = "star_5.png", .pos = { 1164, 570 }, .origin = {66, 70}, .angle = 90.0, .angle_step = 0.075 },
|
||||
};
|
||||
|
||||
#define STARS_COUNT (int)(sizeof(stars) / sizeof(stars[0]))
|
||||
|
||||
char *hyprland_socket_path;
|
||||
static char *hyprland_socket_path;
|
||||
|
||||
Vector2 hyprland_get_cursor_position() {
|
||||
Vector2 pos = {0};
|
||||
static Vector2 hyprland_get_cursor_position() {
|
||||
Vector2 pos = {0, 0};
|
||||
|
||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd == -1) {
|
||||
perror("socket");
|
||||
TraceLog(LOG_ERROR, "socket: %s", strerror(errno));
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -49,15 +49,14 @@ Vector2 hyprland_get_cursor_position() {
|
||||
strncpy(addr.sun_path, hyprland_socket_path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
|
||||
printf("Using hyprland socket: %s\n", hyprland_socket_path);
|
||||
perror("connect");
|
||||
TraceLog(LOG_ERROR, "connect %s: %s", hyprland_socket_path, strerror(errno));
|
||||
close(sock_fd);
|
||||
return pos;
|
||||
}
|
||||
|
||||
const char *message = "/cursorpos";
|
||||
if (send(sock_fd, message, strlen(message), 0) == -1) {
|
||||
perror("send");
|
||||
TraceLog(LOG_ERROR, "send: %s", strerror(errno));
|
||||
close(sock_fd);
|
||||
return pos;
|
||||
}
|
||||
@@ -65,7 +64,7 @@ Vector2 hyprland_get_cursor_position() {
|
||||
char buffer[256] = {0};
|
||||
ssize_t bytes_received = recv(sock_fd, buffer, sizeof(buffer) - 1, 0);
|
||||
if (bytes_received == -1) {
|
||||
perror("recv");
|
||||
TraceLog(LOG_ERROR, "recv: %s", strerror(errno));
|
||||
close(sock_fd);
|
||||
return pos;
|
||||
}
|
||||
@@ -74,76 +73,66 @@ Vector2 hyprland_get_cursor_position() {
|
||||
buffer[bytes_received] = '\0';
|
||||
|
||||
if (sscanf(buffer, "%f, %f", &pos.x, &pos.y) != 2) {
|
||||
fprintf(stderr, "Failed to parse cursor position\n");
|
||||
TraceLog(LOG_ERROR, "Failed to parse cursor position");
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
return pos;
|
||||
}
|
||||
|
||||
Texture2D load_texture_from_file(const char *file_path) {
|
||||
Texture2D texture = {0};
|
||||
|
||||
if (FileExists(file_path)) {
|
||||
texture = LoadTexture(file_path);
|
||||
}
|
||||
else {
|
||||
const char *system_path = TextFormat("/usr/share/wallpaper/%s", file_path);
|
||||
if (FileExists(system_path)) {
|
||||
texture = LoadTexture(system_path);
|
||||
}
|
||||
}
|
||||
static const char *resolve_asset_path(const char *path) {
|
||||
if (FileExists(path)) return path;
|
||||
const char *system_path = TextFormat("/usr/share/wallpaper/%s", path);
|
||||
if (FileExists(system_path)) return system_path;
|
||||
return path;
|
||||
}
|
||||
|
||||
static Texture2D load_texture_from_file(const char *file_path) {
|
||||
Texture2D texture = LoadTexture(resolve_asset_path(file_path));
|
||||
if (texture.id == 0) {
|
||||
TraceLog(LOG_ERROR, "Failed to load texture from file: %s", file_path);
|
||||
abort();
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
Shader load_shader_from_file(const char *vs_path, const char *fs_path) {
|
||||
Shader shader = {0};
|
||||
|
||||
if (FileExists(vs_path) && FileExists(fs_path)) {
|
||||
shader = LoadShader(vs_path, fs_path);
|
||||
}
|
||||
else {
|
||||
const char *system_vs_path = TextFormat("/usr/share/wallpaper/%s", vs_path);
|
||||
const char *system_fs_path = TextFormat("/usr/share/wallpaper/%s", fs_path);
|
||||
if (FileExists(system_vs_path) && FileExists(system_fs_path)) {
|
||||
shader = LoadShader(system_vs_path, system_fs_path);
|
||||
}
|
||||
}
|
||||
|
||||
static Shader load_shader_from_file(const char *vs_path, const char *fs_path) {
|
||||
Shader shader = LoadShader(resolve_asset_path(vs_path), resolve_asset_path(fs_path));
|
||||
if (shader.id == 0) {
|
||||
TraceLog(LOG_ERROR, "Failed to load shader from files: %s, %s", vs_path, fs_path);
|
||||
abort();
|
||||
}
|
||||
return shader;
|
||||
}
|
||||
|
||||
const char* get_window_title() {
|
||||
static const char* get_window_title() {
|
||||
return getenv("DEBUG") != NULL ? "pihkaal-wallpaper-debug" : "pihkaal-wallpaper";
|
||||
}
|
||||
|
||||
static char* get_hyprland_socket_path() {
|
||||
return strdup(TextFormat("%s/hypr/%s/.socket.sock", getenv("XDG_RUNTIME_DIR"), getenv("HYPRLAND_INSTANCE_SIGNATURE")));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
InitWindow(WIDTH, HEIGHT, get_window_title());
|
||||
SetWindowState(FLAG_WINDOW_RESIZABLE);
|
||||
SetTargetFPS(60);
|
||||
|
||||
// initialize hyprland socket path
|
||||
hyprland_socket_path = strdup(TextFormat("%s/hypr/%s/.socket.sock", getenv("XDG_RUNTIME_DIR"), getenv("HYPRLAND_INSTANCE_SIGNATURE")));
|
||||
hyprland_socket_path = get_hyprland_socket_path();
|
||||
|
||||
// initialize textures
|
||||
Texture2D distanceFieldTex = load_texture_from_file("resources/textures/background_distance_field.png");
|
||||
Texture2D backgroundTex = load_texture_from_file("resources/textures/background_no_stars.png");
|
||||
RenderTexture2D target = LoadRenderTexture(WIDTH, HEIGHT);
|
||||
|
||||
Texture2D starTextures[STARS_COUNT];
|
||||
Texture2D distanceFieldTex = load_texture_from_file("assets/textures/background_distance_field.png");
|
||||
Texture2D backgroundTex = load_texture_from_file("assets/textures/background_transparent.png");
|
||||
Texture2D starTextures[STARS_COUNT] = {0};
|
||||
for (int i = 0; i < STARS_COUNT; i++) {
|
||||
starTextures[i] = load_texture_from_file(TextFormat("resources/textures/%s", stars[i].name));
|
||||
starTextures[i] = load_texture_from_file(TextFormat("assets/textures/%s", stars[i].name));
|
||||
}
|
||||
|
||||
RenderTexture2D target = LoadRenderTexture(WIDTH, HEIGHT);
|
||||
|
||||
// initialize shader
|
||||
Shader shader = load_shader_from_file("resources/shaders/basic.vs", "resources/shaders/distance_field.fs");
|
||||
Shader shader = load_shader_from_file("assets/shaders/basic.vs", "assets/shaders/distance_field.fs");
|
||||
|
||||
int resolutionLoc = GetShaderLocation(shader, "resolution");
|
||||
int mousePosLoc = GetShaderLocation(shader, "mousePos");
|
||||
@@ -154,10 +143,10 @@ int main(void) {
|
||||
Vector2 resolution = {WIDTH, HEIGHT};
|
||||
SetShaderValue(shader, resolutionLoc, &resolution, SHADER_UNIFORM_VEC2);
|
||||
|
||||
float ballRadius = 100.0f;
|
||||
float ballRadius = BALL_RADIUS;
|
||||
SetShaderValue(shader, ballRadiusLoc, &ballRadius, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
float shapeInfluence = 200.0f;
|
||||
float shapeInfluence = INFLUENCE;
|
||||
SetShaderValue(shader, shapeInfluenceLoc, &shapeInfluence, SHADER_UNIFORM_FLOAT);
|
||||
|
||||
int textureUnit = 1;
|
||||
@@ -192,13 +181,13 @@ int main(void) {
|
||||
DrawTextureRec(backgroundTex, (Rectangle){0, 0, screenWidth, screenHeight}, (Vector2){0, 0}, WHITE);
|
||||
|
||||
for (int i = 0; i < STARS_COUNT; i++) {
|
||||
stars[i].angle = fmodf(stars[i].angle + stars[i].angle_step, 360.0f);
|
||||
Star* star = &stars[i];
|
||||
star->angle = fmodf(star->angle + star->angle_step, 360.0f);
|
||||
|
||||
Texture2D tex = starTextures[i];
|
||||
Rectangle source = {0, 0, (float)tex.width, (float)tex.height};
|
||||
Rectangle dest = {stars[i].x, stars[i].y, (float)tex.width, (float)tex.height};
|
||||
Vector2 origin = {stars[i].cx, stars[i].cy};
|
||||
DrawTexturePro(tex, source, dest, origin, stars[i].angle, WHITE);
|
||||
Rectangle dest = {star->pos.x, star->pos.y, (float)tex.width, (float)tex.height};
|
||||
DrawTexturePro(tex, source, dest, star->origin, star->angle, WHITE);
|
||||
}
|
||||
|
||||
EndDrawing();
|
||||
|
||||