Skip to content

API Reference

This document provides a comprehensive reference for gSlapper's public APIs, internal functions, and data structures.

Note: This is a developer-facing API reference. For user-facing command-line options, see Command Line Options.


Table of Contents


IPC API

The IPC (Inter-Process Communication) API allows runtime control of gSlapper via Unix domain sockets.

Overview

The IPC system uses a client-server model: - Server: Runs within gSlapper main process - Clients: External processes (scripts, user tools) - Protocol: Text-based commands over Unix socket

Initialization

ipc_init(const char *socket_path)

Initialize IPC server with specified socket path.

Parameters: - socket_path - Path to Unix socket (e.g., /tmp/gslapper.sock)

Returns: - true on success - false on failure

Thread Safety: Thread-safe (creates dedicated thread)

Example:

#include "ipc.h"

bool success = ipc_init("/tmp/gslapper.sock");
if (!success) {
    fprintf(stderr, "Failed to initialize IPC server\n");
}


ipc_shutdown(void)

Shutdown IPC server and cleanup resources.

Parameters: None

Returns: void

Thread Safety: Thread-safe (uses internal locks)

Behavior: - Closes all client connections - Unlinks socket file - Joins server thread - Frees all allocated resources

Example:

// At program exit or on signal
ipc_shutdown();


Wakeup Mechanism

ipc_get_wakeup_fd(void)

Get file descriptor for the wakeup pipe (used by main event loop).

Returns: - File descriptor (int) for wakeup pipe - -1 if IPC not initialized

Purpose: - Allows main loop to monitor IPC activity via poll() or select() - Writing to this pipe wakes up the main loop when new commands arrive

Example:

#include <poll.h>

// In main event loop
struct pollfd fds[2];
fds[0].fd = wl_display_get_fd(display);
fds[0].events = POLLIN;
fds[1].fd = ipc_get_wakeup_fd();
fds[1].events = POLLIN;

while (running) {
    poll(fds, 2, -1);
    // Handle events...
}


ipc_drain_wakeup(void)

Drain the wakeup pipe (call before processing commands).

Parameters: None

Returns: void

Purpose: - Clear pending data from wakeup pipe - Prevents repeated wakeups for same event

Example:

// After poll() returns with IPC event
if (fds[1].revents & POLLIN) {
    ipc_drain_wakeup();  // Clear pipe

    // Now process commands
    ipc_command_t *cmd = ipc_dequeue_command();
    // ...
}


Command Queue

ipc_dequeue_command(void)

Dequeue next command from internal queue.

Returns: - Pointer to ipc_command_t structure - NULL if queue empty

Responsibility: Caller must free: - cmd->cmd_line (string) - Command structure itself (free(cmd))

Thread Safety: Thread-safe (uses mutex-protected queue)

Example:

while ((cmd = ipc_dequeue_command()) != NULL) {
    // Process cmd->cmd_line
    // Send response to cmd->client_fd
    ipc_send_response(cmd->client_fd, "OK\n");

    // Cleanup
    free(cmd->cmd_line);
    free(cmd);
}


ipc_send_response(int client_fd, const char *response)

Send response to IPC client (thread-safe).

Parameters: - client_fd - File descriptor of client socket - response - Response string to send

Returns: void

Thread Safety: Thread-safe

Format: Responses follow this format: - Success: OK or OK: <message> - Error: ERROR: <message> - Query: STATUS: <state> <type> <path> - Transition: TRANSITION: <type> <enabled|disabled> <duration>

Example:

ipc_send_response(client_fd, "OK: Wallpaper changed\n");
ipc_send_response(client_fd, "ERROR: File not found\n");
ipc_send_response(client_fd, "STATUS: playing video /path/to/video.mp4\n");


IPC Commands

Available Commands

Command Parameters Response Description
pause None OK Pause video playback
resume None OK Resume paused playback
stop None OK Stop gSlapper (exits)
query None STATUS: <state> <type> <path> Get current wallpaper state
change <path> File path OK or OK: transition started Change wallpaper
set-transition <type> none or fade OK or ERROR Set transition effect
set-transition-duration <secs> 0.0-5.0 seconds OK or ERROR Set transition duration
get-transition None TRANSITION: <type> <enabled> <duration> Query transition settings

Example Client Code

#include <sys/socket.h>
#include <sys/un.h>

int connect_to_ipc(const char *socket_path) {
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        return -1;
    }
    return fd;
}

void send_ipc_command(int fd, const char *cmd) {
    write(fd, cmd, strlen(cmd));

    char response[1024];
    ssize_t n = read(fd, response, sizeof(response) - 1);
    if (n > 0) {
        response[n] = '\0';
        printf("Response: %s\n", response);
    }
}

int main() {
    int fd = connect_to_ipc("/tmp/gslapper.sock");
    if (fd < 0) {
        perror("Failed to connect");
        return 1;
    }

    send_ipc_command(fd, "pause\n");
    send_ipc_command(fd, "query\n");
    send_ipc_command(fd, "change /path/to/new/video.mp4\n");

    close(fd);
    return 0;
}

Data Structures

ipc_command_t

typedef struct ipc_command {
    char *cmd_line;          // Full command string from client
    int client_fd;            // Client socket file descriptor
    struct ipc_command *next;  // Next command in queue (linked list)
} ipc_command_t;

Usage: - Internal to IPC system (queued by server thread) - Dequeued by main thread for processing - Must be freed by caller after processing

preload_entry_t

typedef struct preload_entry {
    char *path;              // File path of preloaded image
    gpointer frame_data;       // GLib pointer to frame data
    gsize frame_size;         // Size of frame data in bytes
    gint width;               // Image width in pixels
    gint height;              // Image height in pixels
    struct preload_entry *next; // Next entry in cache (linked list)
} preload_entry_t;

Note: Currently reserved for future implementation (see TO_IMPLEMENT.md).


State Management API

The state management API provides persistent storage and retrieval of wallpaper configuration.

Overview

State is saved as simple key=value text files (not JSON): - Location: ~/.local/state/gslapper/state-<output>.txt - Format: Human-readable text - Atomic writes: Uses temp file + rename - Thread safety: File locking prevents corruption

Data Structures

wallpaper_state

struct wallpaper_state {
    char *output;        // Monitor name (e.g., "DP-1", "DP-3")
    char *path;         // Full path to video or image file
    bool is_image;      // true = static image, false = video
    char *options;       // GStreamer options string (e.g., "loop panscan=0.8")
    double position;    // Video position in seconds (0.0 for images)
    bool paused;        // Pause state for videos (false by default)
};

Example State File (state-DP-1.txt):

version=1
output=DP-1
path=/home/user/Videos/wallpaper.mp4
is_image=false
options=loop panscan=0.8
position=123.45
paused=0


Path Resolution

get_state_file_path(const char *output_name)

Generate state file path for given output.

Parameters: - output_name - Monitor name (e.g., "DP-1"), or NULL for default

Returns: - Dynamically allocated string (must be freed by caller) - NULL on failure

Path Resolution Logic: 1. Check XDG_STATE_HOME environment variable 2. If not set, use $HOME/.local/state/gslapper/ 3. Append state-<output_name>.txt (or state.txt if NULL)

Example:

#include "state.h"

char *path = get_state_file_path("DP-1");
if (path) {
    printf("State file: %s\n", path);
    free(path);
}


State File Operations

save_state_file(const char *path, const struct wallpaper_state *state)

Save wallpaper state to file.

Parameters: - path - Full path to state file - state - Pointer to wallpaper_state structure

Returns: - 0 on success - -1 on failure

Behavior: 1. Create backup of existing file (if any) 2. Write to temporary file (.tmp suffix) 3. Verify write completed 4. Atomic rename (temp → actual file) 5. Set permissions to 0600 (user-only) 6. Use file locking to prevent concurrent writes

Example:

struct wallpaper_state state = {
    .output = "DP-1",
    .path = "/home/user/Videos/wallpaper.mp4",
    .is_image = false,
    .options = "loop panscan=0.8",
    .position = 123.45,
    .paused = false
};

char *state_path = get_state_file_path("DP-1");
if (state_path && save_state_file(state_path, &state) == 0) {
    printf("State saved successfully\n");
}
free(state_path);


load_state_file(const char *path, struct wallpaper_state *state)

Load wallpaper state from file.

Parameters: - path - Full path to state file - state - Pointer to wallpaper_state structure to fill

Returns: - 0 on success - -1 on failure (file not found, parse error, etc.)

Behavior: 1. Open and lock file (shared lock) 2. Read key=value pairs line by line 3. Parse and populate state structure 4. Validate file paths exist 5. Unlock and close file

Parsed Keys: - version - State file version (currently 1) - output - Monitor name - path - Video/image file path - is_image - Boolean (0 or 1) - options - GStreamer options string - position - Video position in seconds - paused - Pause state (0 or 1)

Example:

struct wallpaper_state state;
char *state_path = get_state_file_path("DP-1");

if (state_path && load_state_file(state_path, &state) == 0) {
    printf("Restoring: %s\n", state.path);
    printf("Options: %s\n", state.options);
    printf("Position: %.2f\n", state.position);
}


free_wallpaper_state(struct wallpaper_state *state)

Free all dynamically allocated memory in wallpaper_state structure.

Parameters: - state - Pointer to wallpaper_state structure

Returns: void

Behavior: - Frees state->output - Frees state->path - Frees state->options - Does not free the structure itself (caller's responsibility)

Example:

struct wallpaper_state *state = malloc(sizeof(struct wallpaper_state));
// ... fill state ...

// Free allocated strings
free_wallpaper_state(state);

// Free structure itself
free(state);


Logging API

The logging API provides formatted output with color-coded severity levels.

Functions

cflp_success(char *msg, ...)

Log success message in green.

Parameters: - msg - Format string (printf-style) - ... - Variable arguments

Color: Green (\033[0;32m)

Example:

#include "cflogprinter.h"

cflp_success("State saved to %s", state_path);
// Output (green): ✓ State saved to /home/user/.local/state/gslapper/state.txt


cflp_error(char *msg, ...)

Log error message in red.

Parameters: - msg - Format string (printf-style) - ... - Variable arguments

Color: Red (\033[1;31m, bold)

Example:

cflp_error("Failed to open file: %s", path);
// Output (red bold): ✗ Failed to open file: /path/to/file.txt


cflp_warning(char *msg, ...)

Log warning message in yellow.

Parameters: - msg - Format string (printf-style) - ... - Variable arguments

Color: Yellow (\033[0;33m)

Example:

cflp_warning("Video codec not found, falling back to software decoding");
// Output (yellow): ⚠ Video codec not found, falling back to software decoding


cflp_info(char *msg, ...)

Log informational message (no color by default).

Parameters: - msg - Format string (printf-style) - ... - Variable arguments

Color: None (default terminal color)

Example:

if (VERBOSE) {
    cflp_info("Initializing GStreamer pipeline...");
}
// Output (if VERBOSE): ℹ Initializing GStreamer pipeline...


cflp_custom(char *color, char *msg, ...)

Log message with custom color.

Parameters: - color - ANSI color code string - msg - Format string (printf-style) - ... - Variable arguments

Available Colors: - RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN (from cflogprinter.h) - RESET - Reset to default terminal color

Example:

cflp_custom(CYAN, "Loading image: %s", path);
cflp_custom(MAGENTA, "Status: %s", status);
cflp_custom(RESET, "Back to normal color");


Color Constants

#define RED         "\033[1;31m"    // Bold red
#define GREEN       "\033[0;32m"    // Green
#define YELLOW      "\033[0;33m"    // Yellow
#define BLUE        "\033[0;34m"    // Blue
#define MAGENTA     "\033[0;35m"    // Magenta
#define CYAN        "\033[0;36m"    // Cyan
#define RESET       "\033[0m"       // Reset to default

Internal APIs

This section documents internal functions and structures used by gSlapper's main implementation.

Wayland Integration

Global State Structure

struct wl_state {
    struct wl_display *display;           // Wayland display connection
    struct wl_compositor *compositor;     // Compositor interface
    struct zwlr_layer_shell_v1 *layer_shell; // Layer shell protocol
    struct wl_list outputs;               // List of display_output structures
    char *monitor;                       // User-selected output name (or '*')
    int surface_layer;                    // Layer level (background, bottom, top, overlay)
};

Display Output Structure

struct display_output {
    uint32_t wl_name;                  // Wayland output name (numeric)
    struct wl_output *wl_output;         // Output interface
    char *name;                         // Output name string (e.g., "DP-1")
    char *identifier;                    // Output model/serial string

    struct wl_state *state;              // Parent state reference
    struct wl_surface *surface;           // Wayland surface
    struct zwlr_layer_surface_v1 *layer_surface; // Layer surface
    struct wl_egl_window *egl_window;    // EGL window
    EGLSurface *egl_surface;             // EGL surface

    uint32_t width, height;             // Output dimensions
    uint32_t scale;                     // Output scale factor (DPI)

    struct wl_list link;                 // Link in state.outputs list

    struct wl_callback *frame_callback;   // Frame callback
    bool redraw_needed;                  // Flag for frame request
};

GStreamer Pipeline

Global GStreamer Elements

static GstElement *pipeline;             // Main GStreamer pipeline
static GstBus *bus;                   // Pipeline bus for events/messages
static char *allocated_uri;            // URI for current media (for cleanup)
static bool using_waylandsink;        // Whether using waylandsink for direct rendering

Video Texture Management

static GLuint video_texture = 0;       // OpenGL texture for video frames
static GLuint shader_program = 0;       // Default shader program
static GLuint transition_shader_program = 0; // Transition effect shader
static GLuint vao = 0, vbo = 0;    // Vertex array/buffer objects

// Smart texture management to reduce reallocations
static struct {
    GLuint texture;
    int current_width;
    int current_height;
    gboolean initialized;
} texture_manager = {0};

// Video frame data for thread-safe texture updates
static struct {
    gboolean has_new_frame;
    gint width, height;
    gpointer data;
    gsize size;
    gint last_width, last_height;     // Track dimension changes
} video_frame_data = {0};

Frame Rate Limiting

static struct timespec last_render_time = {0};
static long long target_frame_time_ns = 33333333;  // Default ~30 FPS
static int frame_rate_cap = 30;                   // Default 30 FPS
static int frames_skipped = 0;                     // Adaptive skipping

Purpose: Reduce GPU usage by limiting rendering rate.

Default: 30 FPS (adjustable via -r CLI flag)


State Management Globals

static char *state_file_path = NULL;
static bool systemd_mode = false;
static bool save_state_on_exit = true;
static bool restore_flag = false;
static bool save_state_flag = false;

static double restore_position = 0.0;
static bool restore_paused = false;

Transition Effects

typedef enum {
    TRANSITION_NONE = 0,
    TRANSITION_FADE
} transition_type_t;

typedef struct {
    transition_type_t type;       // Transition effect type
    bool active;                 // Currently in transition
    bool enabled;                // Transitions globally enabled
    float duration;              // Transition duration in seconds
    float elapsed;               // Time since transition started
    float progress;              // 0.0 to 1.0 (0% to 100%)

    GLuint old_texture;          // Previous wallpaper texture
    GLuint new_texture;          // New wallpaper texture
    int old_width, old_height;  // Old wallpaper dimensions
    int new_width, new_height;  // New wallpaper dimensions
    float old_scale_x, old_scale_y; // Old scaling factors
    float alpha_old, alpha_new;   // Blend factors

    struct timespec start_time;    // Transition start time
} transition_state_t;

Note: Transitions only work between static images, not videos.


Configuration Management

static struct {
    char **pauselist;          // List of app names for auto-pause
    char **stoplist;           // List of app names for auto-stop

    int argc;                   // Saved argc for restart
    char **argv_copy;           // Saved argv for restart
    char *save_info;            // Saved command info

    bool auto_pause;             // Auto-pause when wallpaper hidden
    bool auto_stop;              // Auto-stop when wallpaper hidden

    int is_paused;              // Current pause state
    bool frame_ready;            // New frame available flag
    bool stop_render_loop;        // Flag to stop rendering loop
} app_state = {0};

Command-Line Parsing

Supported Options

Short Long Type Description
-h --help flag Display help message
-v --verbose flag Be more verbose (-vv for higher verbosity)
-f --fork flag Fork to background after starting
-d --help-output flag Display all available outputs and quit
-p --auto-pause flag Auto-pause when wallpaper hidden
-s --auto-stop flag Auto-stop when wallpaper hidden
-n --slideshow seconds Slideshow mode (TODO: not fully implemented)
-l --layer string Shell surface layer (background, bottom, top, overlay)
-o --gst-options string GStreamer options (see below)
-r --fps-cap int Frame rate cap (30, 60, or 100 FPS)
-I --ipc-socket path Enable IPC control via Unix socket
--transition-type string Transition effect (none, fade)
--transition-duration float Transition duration in seconds (default: 0.5)
-S --systemd flag Enable systemd service mode
-R --restore flag Restore wallpaper from saved state
--save-state flag Save current state and exit
--state-file path Use custom state file path
--no-save-state flag Disable automatic state saving on exit

GStreamer Options (passed via -o)

Option Description Default
loop Seamless video looping Off
fill Fill screen maintaining aspect ratio (crop excess) Default for images
panscan=X Fit inside screen (0.0-1.0 scale factor) 1.0 (full)
stretch Fill screen ignoring aspect ratio Off
original Display at native resolution Off
no-audio Disable audio playback Off

Helper Programs

holder.c

Purpose: Gatekeeper process that checks stoplist conditions before main program runs.

Behavior: 1. Reads stoplist from ~/.config/mpvpaper/stoplist 2. Monitors running applications 3. Allows main program to run only if no stoplist app is active 4. Manages process lifecycle transitions

Usage: Automatically invoked by main gSlapper binary (not user-facing).


Thread Safety

Mutexes

Mutex Purpose
video_mutex Protects video_frame_data and texture updates
state_mutex Protects state file operations
IPC queue lock Protects command queue (internal to ipc.c)

Thread Model

gSlapper uses multi-threading: - Main Thread: Wayland event loop, rendering, IPC command processing - GStreamer Bus Thread: Handles pipeline events (EOM, errors, state changes) - Pause/Stop Monitor Thread: Checks stoplist/pauselist conditions - IPC Server Thread: Accepts client connections and enqueues commands


Error Handling

Common Return Values

Value Meaning
0 Success
-1 Failure (check errno)
NULL Failure (check logs)
false Failure
true Success

Error Logging

All errors are logged via cflp_error() with descriptive messages.

Common Error Scenarios: - Wayland connection failed: Check WAYLAND_DISPLAY environment variable - File not found: Check path in state file - GStreamer pipeline error: Check codec support, install gst-plugins-ugly - IPC socket error: Check socket path, permissions


Building with these APIs

Example: Custom Wallpaper Manager

#include "ipc.h"
#include <stdio.h>

int main() {
    // Start gSlapper with IPC
    system("gslapper -I /tmp/gslapper.sock -o 'loop' DP-1 /path/to/video.mp4 &");
    sleep(2);  // Wait for startup

    // Connect to IPC
    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, "/tmp/gslapper.sock", sizeof(addr.sun_path) - 1);

    if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("Connect failed");
        return 1;
    }

    // Interactive control
    char input[256];
    while (1) {
        printf("> ");
        fgets(input, sizeof(input), stdin);

        if (strcmp(input, "pause\n") == 0) {
            write(fd, "pause\n", 6);
        } else if (strcmp(input, "resume\n") == 0) {
            write(fd, "resume\n", 7);
        } else if (strcmp(input, "quit\n") == 0) {
            write(fd, "stop\n", 5);
            break;
        }

        // Read response
        char response[1024];
        ssize_t n = read(fd, response, sizeof(response) - 1);
        if (n > 0) {
            response[n] = '\0';
            printf("%s", response);
        }
    }

    close(fd);
    return 0;
}

Example: State-Based Wallpaper Switcher

#include "state.h"
#include <dirent.h>

void switch_wallpaper(const char *new_path) {
    struct wallpaper_state state;
    char *state_path = get_state_file_path("DP-1");

    // Load current state
    if (state_path && load_state_file(state_path, &state) == 0) {
        // Update path
        free(state.path);
        state.path = strdup(new_path);

        // Save state
        if (save_state_file(state_path, &state) == 0) {
            printf("State updated: %s\n", new_path);
        }
    }

    free(state_path);
}

See Also