diff --git a/cli_interface.c b/cli_interface.c new file mode 100644 index 0000000..0cdee5c --- /dev/null +++ b/cli_interface.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + printf("INFO: interface\n"); + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_un addr = {0}; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/tmp/my_wallpaper.sock"); + + connect(fd, (struct sockaddr*)&addr, sizeof(addr)); + + if(argc < 2) { + printf("command required\n"); + printf("usage %s command [options]\n", argv[0]); + return 1; + } + + if(strcmp(argv[1], "pause") == 0){ + char command = 'p'; + write(fd, &command, 1); + } else if(strcmp(argv[1], "next") == 0){ + char command = 'n'; + write(fd, &command, 1); + } else if(strcmp(argv[1], "back") == 0){ + char command = 'b'; + write(fd, &command, 1); + } else if(strcmp(argv[1], "interval") == 0){ + char command = 't'; + write(fd, &command, 1); + if(argc < 3) { + printf("interval requires time as argument\n"); + printf("usage %s interval time\n", argv[0]); + return 0; + } + char *endptr; + long number = strtol(argv[2], &endptr, 10); + + if (*endptr != '\0') { + printf("Conversion failed: %s is not a valid number.\n", argv[2]); + return 1; + } + char value1 = number >> 8; + char value2 = number; + write(fd, &value1, 1); + write(fd, &value2, 1); + } else if(strcmp(argv[1], "info") == 0){ + char command = 'i'; + write(fd, &command, 1); + } else if(strcmp(argv[1], "static") == 0){ + char command = 'c'; + write(fd, &command, 1); + } else { + printf("unknown command\n"); + printf("available commands [pause, next, back, interval, info, static]"); + + } + + + return 0; +} diff --git a/daemon.c b/daemon.c new file mode 100644 index 0000000..8441e69 --- /dev/null +++ b/daemon.c @@ -0,0 +1,247 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +int initServerSockServer() { + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un addr = {0}; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, "/tmp/my_wallpaper.sock"); + unlink(addr.sun_path); + if (server_fd < 0) perror("socket"); + if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) perror("bind"); + if (listen(server_fd, 5) < 0) perror("listen"); + return server_fd; +} + + +int last_pid = -1; +void setBackgroundImage(char* path){ + printf("changing wallpaper to %s", path); + + int pid = fork(); + + if(pid < 0){ + perror("fork error"); + return; + } + + if(pid == 0) { + // child + char *args[] = {"/usr/bin/swaybg", "-m", "fit", "-i", path, "-c", "#000000", NULL}; + execvp("swaybg", args); + } + + if(last_pid > 0){ + usleep(250000); + printf("INFO: killing %d", last_pid); + kill(last_pid, SIGTERM); + } + + last_pid = pid; +} + +int image_index = 0; +void runBgSwapNext(char** images, size_t count){ + image_index++; + setBackgroundImage(images[image_index%count]); +} + +void runBgSwapPrevious(char** images, size_t count){ + image_index--; + if(image_index < 0) image_index = 0; + setBackgroundImage(images[image_index%count]); +} + +int isImageFile(const char* filename) { + const char* validExtensions[] = { ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff" }; + size_t numExtensions = sizeof(validExtensions) / sizeof(validExtensions[0]); + for (size_t i = 0; i < numExtensions; i++) { + if (strstr(filename, validExtensions[i])) { + return 1; + } + } + return 0; +} + +#define INITIAL_SIZE 10 + +void collectImages(const char* path, char*** images, size_t* count) { + DIR* dir = opendir(path); + if (dir == NULL) { + perror("opendir failed"); + return; + } + + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + char fullPath[1024]; + snprintf(fullPath, sizeof(fullPath), "%s/%s", path, entry->d_name); + + if (entry->d_type == DT_DIR) { + collectImages(fullPath, images, count); + } else if (isImageFile(entry->d_name)) { + if (*count % INITIAL_SIZE == 0) { + *images = realloc(*images, (*count + INITIAL_SIZE) * sizeof(char*)); + } + (*images)[*count] = malloc(strlen(fullPath) + 1); + strcpy((*images)[*count], fullPath); + (*count)++; + } + } + closedir(dir); +} + +void freeImages(char** images, size_t count) { + for (size_t i = 0; i < count; i++) { + free(images[i]); + } + free(images); +} + +void shuffleImages(char** images, size_t count) { + srand(time(NULL)); // Seed for random number generation + for (size_t i = 0; i < count; i++) { + size_t j = rand() % count; + // Swap images[i] and images[j] + char* temp = images[i]; + images[i] = images[j]; + images[j] = temp; + } +} + +int paused_wallpaper = 0; +int static_wallpaper = 0; +char* static_wallpaper_path = NULL; +int timeout_interval = 5; +void handleCommand(char *buff, int n, char** images, size_t image_count){ + if(n < 1){ + perror("ERROR: command missing"); + return; + } + printf("INFO: command accepted %c\n", buff[0]); + + switch(buff[0]){ + case 't': { + printf("INFO: time changed to %d\n", buff[1]); + timeout_interval = (buff[1] << 8) + buff[2]; + + } break; + case 'p': { + paused_wallpaper = 1 - paused_wallpaper; + printf("INFO: wallpaper is now %s\n", paused_wallpaper ? "paused" : "unpaused"); + } break; + case 'c': { + static_wallpaper = 1 - static_wallpaper; + paused_wallpaper = static_wallpaper; + if(static_wallpaper && static_wallpaper_path){ + setBackgroundImage(static_wallpaper_path); + } + printf("INFO: wallpaper is now %s\n", static_wallpaper ? "static" : "dynamic"); + } break; + case 'n': { + printf("INFO: wallpaper is now next"); + runBgSwapNext(images, image_count); + } break; + case 'b': { + printf("INFO: wallpaper is now next"); + runBgSwapPrevious(images, image_count); + } break; + case 'i': { + printf("current wallpaper is: \n%s\n", images[image_index]); + } break; + } +} + +int main(int argc, char** argv) { + printf("INFO: daemon started\n"); + + char** images = NULL; + size_t image_count = 0; + + if(argc < 3){ + printf("usage: %s dirpath filepath\n", argv[0]); + return 1; + } + + struct stat stat_buffer; + + if(stat(argv[1], &stat_buffer) != 0){ + printf("dirpath is invalid\n"); + return 1; + } + + if(stat(argv[2], &stat_buffer) != 0){ + printf("filepath is invalid\n"); + return 1; + } + static_wallpaper_path = argv[2]; + collectImages(argv[1], &images, &image_count); + printf("collected %ld suitable images\n", image_count); + shuffleImages(images, image_count); + printf("images shuffled\n"); + + printf("starting server\n"); + int server_fd = initServerSockServer(); + + setBackgroundImage(images[0]); + + while(1){ + fd_set fds; + FD_ZERO(&fds); + FD_SET(server_fd, &fds); + + struct timeval tv; + tv.tv_sec = timeout_interval; // N seconds + tv.tv_usec = 0; + int ret = select(server_fd + 1, &fds, NULL, NULL, &tv); + + if (ret < 0) { + perror("select\n"); + break; + } + + if (ret == 0) { + if(!paused_wallpaper){ + runBgSwapNext(images, image_count); + } + printf("INFO: periodic task\n"); + continue; + } + if (FD_ISSET(server_fd, &fds)) { + int client = accept(server_fd, NULL, NULL); + if (client < 0) continue; + + printf("INFO: client connected\n"); + + char buf[256]; + int n = read(client, buf, sizeof(buf)-1); + if (n > 0) { + buf[n] = 0; + handleCommand(buf, n, images, image_count); + } + + close(client); + printf("INFO: client disconnected\n"); + } + } + + + freeImages(images, image_count); + return 0; +}