commit 2e890fb5b7b4fdbbf2a7cc7d97bf62a54b42f6c2 Author: veclavtlica Date: Wed Aug 12 13:28:18 2026 +0300 player: x11 bitmap window diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17f0620 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +**/*.o +*.sublime-* + +Player/player diff --git a/Player/Display/display.h b/Player/Display/display.h new file mode 100644 index 0000000..a805693 --- /dev/null +++ b/Player/Display/display.h @@ -0,0 +1,2 @@ + +extern int plr_display_x11_main(int argc, char *argv[]); diff --git a/Player/Display/x11.c b/Player/Display/x11.c new file mode 100644 index 0000000..10db5d5 --- /dev/null +++ b/Player/Display/x11.c @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include "display.h" + +bool quited = false; + +#define WIDTH 640 +#define HEIGHT 480 + +// TODO: correct error handling. +extern int plr_display_x11_main(int argc, char *argv[]) +{ + Display *display = XOpenDisplay(NULL); + if (NULL == display) { + fprintf(stderr, "Failed to initialize display"); + return EXIT_FAILURE; + } + + Window root = DefaultRootWindow(display); + if (None == root) { + fprintf(stderr, "No root window found"); + XCloseDisplay(display); + return EXIT_FAILURE; + } + + int screen = DefaultScreen(display); + Visual *visual = DefaultVisual(display, screen); + int depth = DefaultDepth(display, screen); + + Window window = XCreateSimpleWindow(display, root, 0, 0, WIDTH, HEIGHT, 0, 0, 0xffffffff); + if (None == window) { + fprintf(stderr, "Failed to create window"); + XCloseDisplay(display); + return EXIT_FAILURE; + } + + XSizeHints *hints = XAllocSizeHints(); + if (hints == NULL) return EXIT_FAILURE; + + // Pinning min and max to the same values disables resizing + hints->flags = PMinSize | PMaxSize; + hints->min_width = hints->max_width = WIDTH; + hints->min_height = hints->max_height = HEIGHT; + + XSetWMNormalHints(display, window, hints); + XFree(hints); + + XSelectInput(display, window, ExposureMask | KeyPressMask); + XMapWindow(display, window); + + GC gc = XCreateGC(display, window, 0, NULL); + + Atom wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False); + XSetWMProtocols(display, window, & wm_delete_window, 1); + + // TODO: can't be not true + int bytes_per_pixel = 4; + char *pixel_buffer = (char *)malloc(WIDTH * HEIGHT * bytes_per_pixel); + + XImage *ximage = XCreateImage( + display, visual, depth, ZPixmap, 0, + pixel_buffer, WIDTH, HEIGHT, 32, 0 + ); + + if (!ximage) { + fprintf(stderr, "Failed to create XImage\n"); + free(pixel_buffer); + XCloseDisplay(display); + return EXIT_FAILURE; + } + + int frame = 0; + + XEvent event; + while (!quited) { + XNextEvent(display, &event); + + switch(event.type) { + case ClientMessage: + if(event.xclient.data.l[0] == wm_delete_window) { + XDestroyWindow(display, window); + quited = true; + } + break; + case Expose: + frame += 2; + // Fill buffer with a simple color gradient + for (int y = 0; y < HEIGHT; y++) { + for (int x = 0; x < WIDTH; x++) { + int pixel_index = (y * WIDTH + x) * bytes_per_pixel; + + // Assuming standard Little-Endian 32-bit BGRA format + pixel_buffer[pixel_index + 0] = (x + frame) % 256; // Blue + pixel_buffer[pixel_index + 1] = (y + frame) % 256; // Green + pixel_buffer[pixel_index + 2] = (x + y - frame) % 256; // Red + pixel_buffer[pixel_index + 3] = 0; // Alpha/Padding + } + } + // Draw the complete image onto the window when exposed + XPutImage( + display, + window, // Target drawable + gc, // Graphics Context + ximage, // Source XImage + 0, 0, // Source coordinates (x, y) + 0, 0, // Destination coordinates (x, y) + WIDTH, HEIGHT// Dimensions to copy + ); + break; + } + } + + XCloseDisplay(display); + + return 0; +} diff --git a/Player/Makefile b/Player/Makefile new file mode 100644 index 0000000..2ef300f --- /dev/null +++ b/Player/Makefile @@ -0,0 +1,10 @@ +CC=clang +CFLAGS=-lX11 +DEPS = +OBJ = main.o Display/x11.o + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +player: $(OBJ) + $(CC) -o $@ $^ $(CFLAGS) diff --git a/Player/docs.txt b/Player/docs.txt new file mode 100644 index 0000000..aa75442 --- /dev/null +++ b/Player/docs.txt @@ -0,0 +1,13 @@ +Pustina -- Player +BYOND-inspired optimized web-driven content player. +All logic is processed on the server, whereas player only does display changes and sends unreliable inputs. +This allows for no-update and hidden content strategies, allowing for mystic and truly seamlessly progressing world. +We do not use abstractions, as the simplicity of the required set of features should allow for platform-specific implementations. + +Restrictions: +-- Fixed sized frambuffer, default is 640x480, but platforms can implement their own. Scaling by 2 is possible. +-- GIF image format for all drawing purposes. Delta Frame Optimiztion is encouraged. +-- Images are 32x32 or multiples of it, aligning to the grid. +-- 20 FPS display rate. +-- Fonts are in ASCII bitmaps. +-- Audio samples are in vorbis format. Music pieces are written in our own simple tracker format, using the same vorbis samples. diff --git a/Player/main.c b/Player/main.c new file mode 100644 index 0000000..d6e9441 --- /dev/null +++ b/Player/main.c @@ -0,0 +1,5 @@ +#include "Display/display.h" + +extern int main(int argc, char *argv[]) { + return plr_display_x11_main(argc, argv); +}