Last active
July 16, 2026 23:08
-
-
Save klange/420525baf857eb7b1cace47fdd9cf311 to your computer and use it in GitHub Desktop.
Sortix's wallpaper generator, as an app for ToaruOS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @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 <errno.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}, | |
| {"file", required_argument, 0, 5}, | |
| {0,0,0,0}, | |
| }; | |
| int opt; | |
| char * resolution = NULL; | |
| uint32_t seed = time(NULL); | |
| int is_background = 0; | |
| FILE * out_file = stdout; | |
| 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 5: | |
| out_file = fopen(optarg, "w"); | |
| if (!out_file) return fprintf(stderr, "%s: %s: %s\n", argv[0], optarg, strerror(errno)); | |
| 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); | |
| return gfx_buffer_write(out_file, ctx, GFX_WRITE_FORMAT_TARGA | GFX_WRITE_FLAG_BACKBUF); | |
| } | |
| 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