This commit is contained in:
veclavtlica
2026-09-11 02:21:21 +03:00
parent 2e890fb5b7
commit adfae10938
17 changed files with 949 additions and 119 deletions
+5
View File
@@ -1,2 +1,7 @@
#ifndef PLR_DISPLAY_H
#define PLR_DISPLAY_H
extern int plr_display_x11_main(int argc, char *argv[]);
extern int plr_display_dos_main(int argc, char *argv[]);
#endif
+152
View File
@@ -0,0 +1,152 @@
#include "display.h"
#include <stdint.h>
#include <string.h>
#define WIDTH 640
#define HEIGHT 480
#define MODE_VGA_320_200_8 0x13
#define MODE_VESA_640_480_8 0x101
// 8-color VGA palette gradient, lighter shades of lavender to pink
#define COLOR_OFFSET 0x50
#define COLOR_NUM 8
#define COLOR_STEP (HEIGHT / COLOR_NUM)
/* https://wiki.osdev.org/VESA_Video_Modes */
_Packed struct VbeInfoBlock {
char VbeSignature[4]; // == "VESA"
uint16_t VbeVersion; // == 0x0300 for VBE 3.0
uint16_t OemStringPtr[2]; // isa vbeFarPtr
uint8_t Capabilities[4];
uint16_t VideoModePtr[2]; // isa vbeFarPtr
uint16_t TotalMemory; // as # of 64KB blocks
uint8_t Reserved[492];
};
_Packed struct ModeInfoBlock {
uint16_t
attributes; // deprecated, only bit 7 should be of interest to you, and it
// indicates the mode supports a linear frame buffer.
uint8_t window_a; // deprecated
uint8_t window_b; // deprecated
uint16_t granularity; // deprecated; used while calculating bank numbers
uint16_t window_size;
uint16_t segment_a;
uint16_t segment_b;
uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode
// without returning to real mode
uint16_t pitch; // number of bytes per horizontal line
uint16_t width; // width in pixels
uint16_t height; // height in pixels
uint8_t w_char; // unused...
uint8_t y_char; // ...
uint8_t planes;
uint8_t bpp; // bits per pixel in this mode
uint8_t banks; // deprecated; total number of banks in this mode
uint8_t memory_model;
uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may
// be 16 KB...
uint8_t image_pages;
uint8_t reserved0;
uint8_t red_mask;
uint8_t red_position;
uint8_t green_mask;
uint8_t green_position;
uint8_t blue_mask;
uint8_t blue_position;
uint8_t reserved_mask;
uint8_t reserved_position;
uint8_t direct_color_attributes;
uint32_t framebuffer; // physical address of the linear frame buffer; write
// here to draw to the screen
uint32_t off_screen_mem_off;
uint16_t off_screen_mem_size; // size of memory in the framebuffer but not
// being displayed on the screen
uint8_t reserved1[206];
};
static char get_video_mode();
#pragma aux get_video_mode = "mov ah, 0x0f" \
"int 0x10" value[al] modify[ah];
static void set_video_mode(unsigned char);
#pragma aux set_video_mode = "mov ah, 0x00" \
"int 0x10" parm[al] modify[ah];
static void set_vesa_video_mode(unsigned short);
#pragma aux set_vesa_video_mode = "mov ax, 0x4f02" \
"int 0x10" parm[bx] modify[ah];
static void wait_for_key();
#pragma aux wait_for_key = "mov ah, 0x00" \
"int 0x16" modify[ah];
static short get_vesa_controller_info(struct VbeInfoBlock *);
#pragma aux get_vesa_controller_info = "mov ax, 0x4f00" \
"int 0x10" parm[es di] value[ax];
static short get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *);
#pragma aux get_vesa_mode_info = "mov ax, 0x4F01" \
"int 0x10" parm[cx][es di] value[ax];
/* https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial*/
static char *find_display_memory(void) {
struct VbeInfoBlock *ctrl = (struct VbeInfoBlock *)0x2000;
struct ModeInfoBlock *inf = (struct ModeInfoBlock *)0x3000;
uint16_t *modes;
int i;
strncpy(ctrl->VbeSignature, "VBE2", 4);
if (get_vesa_controller_info(ctrl) != 0x004F)
return NULL;
modes = (uint16_t *)(ctrl->VideoModePtr);
for (i = 0; modes[i] != 0xFFFF; ++i) {
if (get_vesa_mode_info(modes[i], inf) != 0x004F)
continue;
// Check if this is a graphics mode with linear frame buffer support
if ((inf->attributes & 0x80) != 0x80)
continue;
// Check if this is a packed pixel or direct color mode
if (inf->memory_model != 4 && inf->memory_model != 6)
continue;
// Check if this is exactly the mode we're looking for
if (WIDTH == inf->width && HEIGHT == inf->height && 256 == inf->pitch)
return (char *)inf->framebuffer;
}
return NULL;
}
extern int plr_display_dos_main(int argc, char *argv[]) {
char far *buf = (char far *)0xA0000; // VGA memory address
int x, y;
unsigned char saved_mode = get_video_mode(); // Save original mode
set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode
buf = find_display_memory();
/* TODO: Fetch info about the mode, to know the availability */
/* as well as base address in memory */
/* https://wiki.osdev.org/VESA_Video_Modes */
// Now draw. We draw pixel-by-pixel by directly setting the video memory like
// it was an array. We step the color every 25 pixels so that we have 8 bands
// of even height.
for (y = 0; y < HEIGHT; y++) {
for (x = 0; x < WIDTH; x++) {
buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP;
}
}
wait_for_key();
set_video_mode(saved_mode); // Restore original mode
return 0;
}
+127 -103
View File
@@ -1,120 +1,144 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "../../Common/lzw.h"
#include "../../Common/mat.h"
#include "display.h"
bool quited = false;
#define WIDTH 640
#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;
}
extern int plr_display_x11_main(int argc, char *argv[]) {
char test_string[] = "what is this?";
char *compressed_string = NULL;
uint32_t compressed_string_sz = 0;
struct com_lzw_table table =
com_lzw_infer_table(test_string, sizeof(test_string));
bool test = com_lzw_compress(&table, test_string, sizeof(test_string),
&compressed_string, &compressed_string_sz);
com_lzw_free_table(&table);
assert(test);
Window root = DefaultRootWindow(display);
if (None == root) {
fprintf(stderr, "No root window found");
XCloseDisplay(display);
return EXIT_FAILURE;
}
com_mat_t m0 = com_mat_identity();
com_mat_t m1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
for (int i = 0; i < 16; ++i)
m1.a[i] *= COM_FIXED_FRACUNIT;
int screen = DefaultScreen(display);
Visual *visual = DefaultVisual(display, screen);
int depth = DefaultDepth(display, screen);
com_mat_t t0 = com_mat_mul(m1, m0);
com_mat_t t1 = com_mat_mul_reodered(com_mat_reoder(m1), m0);
com_fixed_print(t0.a[4]);
com_fixed_print(t1.a[4]);
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;
}
}
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;
}
return 0;
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;
}