Skip to content

Instantly share code, notes, and snippets.

@klange
Created May 6, 2026 04:43
Show Gist options
  • Select an option

  • Save klange/420525baf857eb7b1cace47fdd9cf311 to your computer and use it in GitHub Desktop.

Select an option

Save klange/420525baf857eb7b1cace47fdd9cf311 to your computer and use it in GitHub Desktop.
Sortix's wallpaper generator, as an app for ToaruOS
/**
* @brief Wallpaper generator from Sortix.
*
* Image generator:
*
* Copyright (c) 2014-2016, 2018, 2022-2024 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* This utility:
*
* Copyrigh 2026 K. Lange <klange@toaruos.org>
* NCSA/University of Illinois license.
*/
#include <stdlib.h>
#include <time.h>
#include <getopt.h>
#include <toaru/yutani.h>
#include <toaru/graphics.h>
#include <toaru/decorations.h>
#include <toaru/menu.h>
#define APP_TITLE "Sortix Wallpaper"
union c {
uint32_t v;
struct {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
};
};
static bool seeded = false; /* exposed so we can reset it */
static void wallpaper(int is_background, yutani_window_t * window, gfx_context_t * _ctx) {
static uint32_t s;
static uint32_t t;
if ( !seeded )
{
s = rand();
t = rand();
seeded = true;
}
gfx_context_t * ctx = _ctx;
if (!is_background) {
struct decor_bounds bounds;
decor_get_bounds(window, &bounds);
ctx = init_graphics_subregion(_ctx, bounds.left_width, bounds.top_height, _ctx->width - bounds.width, _ctx->height - bounds.height);
render_decorations(window, _ctx, APP_TITLE);
}
for ( size_t y = 0; y < ctx->height; y++ )
{
for ( size_t x = 0; x < ctx->width; x++ )
{
uint32_t r = 3793 * x + 6959 * y + 1889 * t + 7901 * s;
r ^= (5717 * x * 2953 * y) ^ s ^ t;
r = (r >> 24) ^ (r >> 16) ^ (r >> 8) ^ r;
union c c;
if ( x && (r & 0x3) == 2 )
c.v = GFX(ctx, x - 1, y);
else if ( y && (r & 0x3) == 1 )
c.v = GFX(ctx, x, y - 1);
else if ( x && y )
c.v = GFX(ctx, x - 1, y - 1);
else
{
c.v = t;
c.r = (c.r & 0xc0) | (r >> 0 & 0x3f);
c.g = (c.g & 0xc0) | (r >> 4 & 0x3f);
c.b = (c.b & 0xc0) | (r >> 8 & 0x3f);
}
if ( (r & 0xf0) == 0x10 && c.r ) c.r--;
if ( (r & 0xf0) == 0x20 && c.g ) c.g--;
if ( (r & 0xf0) == 0x30 && c.b ) c.b--;
if ( (r & 0xf0) == 0x40 && c.r != 255 ) c.r++;
if ( (r & 0xf0) == 0x50 && c.g != 255 ) c.g++;
if ( (r & 0xf0) == 0x60 && c.b != 255 ) c.b++;
union c tc = {.v = t};
if ( c.r && c.r - tc.r > (int8_t) (r >> 0) + 64 ) c.r--;
if ( c.r != 255 && tc.r - c.r > (int8_t) (r >> 4) + 240 ) c.r++;
if ( c.g && c.g - tc.g > (int8_t) (r >> 8) + 64) c.g--;
if ( c.g != 255 && tc.g - c.g > (int8_t) (r >> 12) + 240 ) c.g++;
if ( c.b && c.b - tc.b > (int8_t) (r >> 16) + 64 ) c.b--;
if ( c.b != 255 && tc.b - c.b > (int8_t) (r >> 20) + 240 ) c.b++;
c.a = 0xFF;
GFX(ctx, x, y) = c.v;
}
}
if (!is_background) free(ctx);
flip(_ctx);
}
static int help_text(char * argv[]) {
fprintf(stderr,
"usage: %s [--bitmap WxY] [--seed seed]\n",
argv[0]);
return 1;
}
int main (int argc, char * argv[]) {
struct option long_opts[] = {
{"bitmap", required_argument, 0, 1},
{"seed", required_argument, 0, 2},
{"help", no_argument, 0, 3},
{"background", no_argument, 0, 4},
{0,0,0,0},
};
int opt;
char * resolution = NULL;
uint32_t seed = time(NULL);
int is_background = 0;
while ((opt = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
switch (opt) {
case 1:
resolution = optarg;
break;
case 2:
seed = strtoul(optarg, NULL, 0);
break;
case 4:
is_background = 1;
break;
case 3:
default:
return help_text(argv);
}
}
srand(seed);
if (resolution) {
char * x = strchr(resolution, 'x');
if (!x) return help_text(argv);
*x = '\0';
x++;
size_t w = strtoul(resolution,NULL,10);
size_t h = strtoul(x,NULL,10);
sprite_t * buffer = create_sprite(w,h,ALPHA_OPAQUE);
gfx_context_t * ctx = init_graphics_sprite(buffer);
wallpaper(1, NULL, ctx);
struct {
uint8_t id_length;
uint8_t color_map_type;
uint8_t image_type;
uint16_t color_map_first_entry;
uint16_t color_map_length;
uint8_t color_map_entry_size;
uint16_t x_origin;
uint16_t y_origin;
uint16_t width;
uint16_t height;
uint8_t depth;
uint8_t descriptor;
} __attribute__((packed)) header = {
0, /* No image ID field */
0, /* No color map */
2, /* Uncompressed truecolor */
0, 0, 0, /* No color map */
0, 0, /* Don't care about origin */
ctx->width, ctx->height, 24,
0,
};
fwrite(&header, 1, sizeof(header), stdout);
for (int y = ctx->height-1; y>=0; y--) {
for (int x = 0; x < ctx->width; ++x) {
union c c;
c.v = GFX(ctx, x, y);
uint8_t buf[3] = { c.b, c.g, c.r };
fwrite(buf, 1, 3, stdout);
}
}
return 0;
}
yutani_t * yctx = yutani_init();
yutani_window_t * window;
if (is_background) {
window = yutani_window_create_flags(yctx, yctx->display_width, yctx->display_height, YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS);
yutani_window_move(yctx, window, 0, 0);
yutani_set_stack(yctx, window, YUTANI_ZORDER_BOTTOM);
} else {
window = yutani_window_create(yctx, 640, 480);
yutani_window_move(yctx, window, 200, 200);
yutani_window_advertise_icon(yctx, window, APP_TITLE, "sortix");
init_decorations();
}
gfx_context_t * ctx = init_graphics_yutani_double_buffer(window);
wallpaper(is_background, window, ctx);
yutani_flip(yctx, window);
while (1) {
yutani_msg_t * m = yutani_poll(yctx);
while (m) {
switch (decor_handle_event_flags(yctx, m, DECOR_HANDLE_CONTEXT_MENU | DECOR_HANDLE_WINDOW_FOCUS | DECOR_HANDLE_MENU_EVENTS)) {
case DECOR_REDRAW:
wallpaper(is_background, window, ctx);
yutani_flip(yctx, window);
break;
case DECOR_CLOSE:
goto _exit;
}
switch (m->type) {
case YUTANI_MSG_WELCOME:
if (is_background) yutani_window_resize_offer(yctx, window, yctx->display_width, yctx->display_height);
break;
case YUTANI_MSG_KEY_EVENT: {
struct yutani_msg_key_event * ke = (void*)m->data;
if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') goto _exit;
if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'r') {
seeded = false;
wallpaper(is_background, window, ctx);
yutani_flip(yctx, window);
}
break;
}
case YUTANI_MSG_RESIZE_OFFER: {
struct yutani_msg_window_resize * wr = (void*)m->data;
if (wr->wid == window->wid) {
yutani_window_resize_accept(yctx, window, wr->width, wr->height);
reinit_graphics_yutani(ctx, window);
wallpaper(is_background, window, ctx);
yutani_window_resize_done(yctx, window);
yutani_flip(yctx, window);
}
break;
}
case YUTANI_MSG_WINDOW_CLOSE:
case YUTANI_MSG_SESSION_END:
goto _exit;
default:
break;
}
m = yutani_poll_async(yctx);
}
}
_exit:
yutani_close(yctx, window);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment