Created
May 6, 2026 04:43
-
-
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 <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