screenshot util

This commit is contained in:
boreddevnl 2026-03-12 12:16:01 +01:00
parent dbaff43c6f
commit 071f8339bf
20 changed files with 1992 additions and 21 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
disk.img

Binary file not shown.

View file

@ -818,6 +818,7 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size)
uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset];
*(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF
vol->disk->write_sector(vol->disk, fat_sector, fat_buf); vol->disk->write_sector(vol->disk, fat_sector, fat_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
} }
if (fat_buf) kfree(fat_buf); if (fat_buf) kfree(fat_buf);
@ -894,6 +895,7 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size)
uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset];
*(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | (new_cluster & 0x0FFFFFFF); *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fat_sector, fat_buf); vol->disk->write_sector(vol->disk, fat_sector, fat_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
} }
if (fat_buf) kfree(fat_buf); if (fat_buf) kfree(fat_buf);
@ -905,6 +907,7 @@ static int realfs_write(FAT32_FileHandle *handle, const void *buffer, int size)
uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset]; uint32_t old_val = *(uint32_t*)&fat_buf[fat_offset];
*(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF *(uint32_t*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF
vol->disk->write_sector(vol->disk, fat_sector, fat_buf); vol->disk->write_sector(vol->disk, fat_sector, fat_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
} }
if (fat_buf) kfree(fat_buf); if (fat_buf) kfree(fat_buf);
@ -1074,6 +1077,7 @@ static bool realfs_delete(char drive, const char *path) {
if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) { if (vol->disk->read_sector(vol->disk, fat_sector, fat_buf) == 0) {
*(uint32_t*)&fat_buf[fat_offset] = 0; // Free *(uint32_t*)&fat_buf[fat_offset] = 0; // Free
vol->disk->write_sector(vol->disk, fat_sector, fat_buf); vol->disk->write_sector(vol->disk, fat_sector, fat_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
} }
kfree(fat_buf); kfree(fat_buf);
@ -1449,6 +1453,7 @@ static bool realfs_mkdir(char drive, const char *path) {
uint32_t old_val = *(uint32_t*)&sect_buf[fat_offset]; uint32_t old_val = *(uint32_t*)&sect_buf[fat_offset];
*(uint32_t*)&sect_buf[fat_offset] = (old_val & 0xF0000000) | (expanded & 0x0FFFFFFF); *(uint32_t*)&sect_buf[fat_offset] = (old_val & 0xF0000000) | (expanded & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fat_sector, sect_buf); vol->disk->write_sector(vol->disk, fat_sector, sect_buf);
if (vol->cached_fat_sector == fat_sector) vol->cached_fat_sector = 0xFFFFFFFF;
} }
// Zero out new cluster // Zero out new cluster
for(uint32_t k=0; k<cluster_size; k++) dir_cluster_buf[k] = 0; for(uint32_t k=0; k<cluster_size; k++) dir_cluster_buf[k] = 0;

View file

@ -583,6 +583,24 @@ void graphics_flip_buffer(void) {
} }
} }
void graphics_copy_screenbuffer(uint32_t *dest) {
if (!g_fb || !dest) return;
uint64_t rflags;
asm volatile("pushfq; pop %0; cli" : "=r"(rflags));
int sw = g_fb->width;
int sh = g_fb->height;
// Copy the internal back object to the dest directly
for (int y = 0; y < sh; y++) {
uint32_t *src_row = &g_back_buffer[y * sw];
for (int x = 0; x < sw; x++) {
dest[y * sw + x] = src_row[x];
}
}
asm volatile("push %0; popfq" : : "r"(rflags));
}
void graphics_set_clipping(int x, int y, int w, int h) { void graphics_set_clipping(int x, int y, int w, int h) {
if (x < 0) { w += x; x = 0; } if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; } if (y < 0) { h += y; y = 0; }

View file

@ -31,6 +31,7 @@ void graphics_set_bg_pattern(const uint32_t *pattern); // 128x128 pattern
void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image void graphics_set_bg_image(uint32_t *pixels, int w, int h); // Full-screen wallpaper image
void graphics_set_render_target(uint32_t *buffer, int w, int h); void graphics_set_render_target(uint32_t *buffer, int w, int h);
void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h); void graphics_blit_buffer(uint32_t *src, int dst_x, int dst_y, int w, int h);
void graphics_copy_screenbuffer(uint32_t *dest);
void draw_boredos_logo(int x, int y, int scale); void draw_boredos_logo(int x, int y, int scale);

View file

@ -14,6 +14,10 @@
#define GUI_CMD_GET_STRING_WIDTH 8 #define GUI_CMD_GET_STRING_WIDTH 8
#define GUI_CMD_GET_FONT_HEIGHT 9 #define GUI_CMD_GET_FONT_HEIGHT 9
#define GUI_CMD_WINDOW_SET_RESIZABLE 14 #define GUI_CMD_WINDOW_SET_RESIZABLE 14
#define GUI_CMD_GET_SCREEN_SIZE 17
#define GUI_CMD_GET_SCREENBUFFER 18
#define GUI_CMD_SHOW_NOTIFICATION 19
#define GUI_CMD_GET_DATETIME 20
#define GUI_EVENT_NONE 0 #define GUI_EVENT_NONE 0
#define GUI_EVENT_PAINT 1 #define GUI_EVENT_PAINT 1

View file

@ -25,7 +25,7 @@ uint64_t timer_handler(registers_t *regs) {
// --- Keyboard --- // --- Keyboard ---
static bool shift_pressed = false; static bool shift_pressed = false;
static bool ctrl_pressed = false; bool ps2_ctrl_pressed = false;
static bool extended_scancode = false; static bool extended_scancode = false;
static char scancode_map[128] = { static char scancode_map[128] = {
@ -54,14 +54,14 @@ uint64_t keyboard_handler(registers_t *regs) {
} }
if (scancode == 0x1D) { if (scancode == 0x1D) {
ctrl_pressed = true; ps2_ctrl_pressed = true;
extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug) extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug)
} else if (scancode == 0x9D) { } else if (scancode == 0x9D) {
ctrl_pressed = false; ps2_ctrl_pressed = false;
extended_scancode = false; extended_scancode = false;
} }
if (ctrl_pressed && scancode == 0x2E) { if (ps2_ctrl_pressed && scancode == 0x2E) {
extern process_t* process_get_current(void); extern process_t* process_get_current(void);
process_t* proc = process_get_current(); process_t* proc = process_get_current();
if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) { if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) {

View file

@ -680,6 +680,51 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
} }
} }
return 0; return 0;
} else if (cmd == GUI_CMD_GET_SCREEN_SIZE) {
uint64_t *out_w = (uint64_t *)arg2;
uint64_t *out_h = (uint64_t *)arg3;
if (out_w && out_h) {
extern int get_screen_width(void);
extern int get_screen_height(void);
*out_w = (uint64_t)get_screen_width();
*out_h = (uint64_t)get_screen_height();
}
return 0;
} else if (cmd == GUI_CMD_GET_SCREENBUFFER) {
uint32_t *dest = (uint32_t *)arg2;
if (dest) {
extern void graphics_copy_screenbuffer(uint32_t *dest);
graphics_copy_screenbuffer(dest);
}
return 0;
} else if (cmd == GUI_CMD_SHOW_NOTIFICATION) {
const char *user_msg = (const char *)arg2;
if (user_msg) {
char kernel_msg[256];
int i = 0;
while (i < 255 && user_msg[i]) {
kernel_msg[i] = user_msg[i];
i++;
}
kernel_msg[i] = 0;
extern void wm_show_notification(const char *msg);
wm_show_notification(kernel_msg);
}
return 0;
} else if (cmd == GUI_CMD_GET_DATETIME) {
uint64_t *out_arr = (uint64_t *)arg2;
if (out_arr) {
extern void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, int *second);
int y, m, d, h, min, s;
rtc_get_datetime(&y, &m, &d, &h, &min, &s);
out_arr[0] = y;
out_arr[1] = m;
out_arr[2] = d;
out_arr[3] = h;
out_arr[4] = min;
out_arr[5] = s;
}
return 0;
} }
} else if (syscall_num == SYS_FS) { } else if (syscall_num == SYS_FS) {
int cmd = (int)arg1; int cmd = (int)arg1;
@ -1090,9 +1135,6 @@ static uint64_t syscall_handler_inner(registers_t *regs) {
mem += (processes[i].heap_end - processes[i].heap_start); mem += (processes[i].heap_end - processes[i].heap_start);
if (processes[i].pid == 0) { if (processes[i].pid == 0) {
// For kernel, we can report a more realistic figure if we want,
// but 32KB is specifically its stack. Let's keep it but maybe
// add a note in documentation.
mem = 32768; mem = 32768;
} else { } else {
if (processes[i].is_user) mem += 262144; // User stack if (processes[i].is_user) mem += 262144; // User stack

View file

@ -5,7 +5,7 @@ CC = x86_64-elf-gcc
AS = nasm AS = nasm
LD = x86_64-elf-ld LD = x86_64-elf-ld
CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-80387 -mno-mmx -mno-sse -mno-sse2 -mno-red-zone -Ilibc CFLAGS = -Wall -Wextra -std=gnu11 -ffreestanding -O2 -fno-stack-protector -fno-stack-check -fno-lto -fno-pie -m64 -march=x86-64 -mno-red-zone -Ilibc
LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic-linker -z text -z max-page-size=0x1000 -e _start
BIN_DIR = bin BIN_DIR = bin
@ -45,6 +45,9 @@ $(BIN_DIR)/settings.elf: $(LIBC_OBJS) $(BIN_DIR)/settings.o $(BIN_DIR)/stb_image
$(BIN_DIR)/browser.elf: $(LIBC_OBJS) $(BIN_DIR)/browser.o $(BIN_DIR)/stb_image.o $(BIN_DIR)/browser.elf: $(LIBC_OBJS) $(BIN_DIR)/browser.o $(BIN_DIR)/stb_image.o
$(LD) $(LDFLAGS) $^ -o $@ $(LD) $(LDFLAGS) $^ -o $@
$(BIN_DIR)/screenshot.elf: $(LIBC_OBJS) $(BIN_DIR)/screenshot.o $(BIN_DIR)/stb_image.o
$(LD) $(LDFLAGS) $^ -o $@
$(BIN_DIR)/%.o: doom/%.c $(BIN_DIR)/%.o: doom/%.c
$(CC) $(CFLAGS) -Wno-error -Idoom -c $< -o $@ $(CC) $(CFLAGS) -Wno-error -Idoom -c $< -o $@

View file

@ -185,17 +185,7 @@ void _exit(int status) {
exit(status); exit(status);
} }
void* memmove(void *dest, const void *src, size_t n) {
unsigned char *d = dest;
const unsigned char *s = src;
if (d < s) {
while (n--) *d++ = *s++;
} else {
d += n; s += n;
while (n--) *--d = *--s;
}
return dest;
}
int fflush(FILE *stream) { return 0; } int fflush(FILE *stream) { return 0; }
int abs(int x) { return x < 0 ? -x : x; } int abs(int x) { return x < 0 ? -x : x; }

View file

@ -148,6 +148,30 @@ void *memcpy(void *dest, const void *src, size_t n) {
return dest; return dest;
} }
void *memmove(void *dest, const void *src, size_t n) {
unsigned char *d = (unsigned char *)dest;
const unsigned char *s = (const unsigned char *)src;
if (d < s) {
while (n--) *d++ = *s++;
} else {
d += n;
s += n;
while (n--) *--d = *--s;
}
return dest;
}
int memcmp(const void *s1, const void *s2, size_t n) {
const unsigned char *p1 = (const unsigned char *)s1;
const unsigned char *p2 = (const unsigned char *)s2;
while (n--) {
if (*p1 != *p2) return *p1 - *p2;
p1++;
p2++;
}
return 0;
}
// String functions // String functions
size_t strlen(const char *s) { size_t strlen(const char *s) {
size_t len = 0; size_t len = 0;

View file

@ -1,6 +1,11 @@
#ifndef BOREDOS_LIBC_STRING_H #ifndef BOREDOS_LIBC_STRING_H
#define BOREDOS_LIBC_STRING_H #define BOREDOS_LIBC_STRING_H
#include <stdlib.h> #include <stddef.h>
void *memmove(void *dest, const void *src, size_t n);
int memcmp(const void *s1, const void *s2, size_t n);
void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *s, int c, size_t n);
#endif #endif

View file

@ -0,0 +1,122 @@
#include <stdint.h>
#include <stdbool.h>
#include "stdlib.h"
#include "libui.h"
#include "syscall_user.h"
#define STBI_WRITE_NO_STDIO
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#include <string.h>
#define GUI_CMD_GET_SCREEN_SIZE 17
#define GUI_CMD_GET_SCREENBUFFER 18
#define GUI_CMD_SHOW_NOTIFICATION 19
#define GUI_CMD_GET_DATETIME 20
void png_write_func(void *context, void *data, int size) {
int fd = *(int*)context;
sys_write_fs(fd, data, size);
}
int main(int argc, char **argv) {
(void)argc;
(void)argv;
// 1. Get screen size
uint64_t w = 0, h = 0;
syscall3(SYS_GUI, GUI_CMD_GET_SCREEN_SIZE, (uint64_t)&w, (uint64_t)&h);
if (w == 0 || h == 0 || w > 4096 || h > 4096) {
printf("Failed to get screen size %d x %d\n", (int)w, (int)h);
return 1;
}
// 2. Allocate buffer for 0xAARRGGBB
uint32_t *pixels = (uint32_t *)malloc(w * h * sizeof(uint32_t));
if (!pixels) {
printf("Failed to allocate memory for %d x %d pixels\n", (int)w, (int)h);
return 1;
}
// 3. Request screenbuffer
syscall2(SYS_GUI, GUI_CMD_GET_SCREENBUFFER, (uint64_t)pixels);
// 4. Convert 0xAARRGGBB to RGB for stb_image_write
uint8_t *rgb_pixels = (uint8_t *)malloc(w * h * 3);
if (!rgb_pixels) {
printf("Failed to allocate RGB buffer\n");
free(pixels);
return 1;
}
for (int y = 0; y < (int)h; y++) {
for (int x = 0; x < (int)w; x++) {
uint32_t px = pixels[y * w + x];
int idx = (y * w + x) * 3;
rgb_pixels[idx + 0] = (px >> 16) & 0xFF; // R
rgb_pixels[idx + 1] = (px >> 8) & 0xFF; // G
rgb_pixels[idx + 2] = (px) & 0xFF; // B
}
}
// 5. Get Datetime for filename
uint64_t dt[6] = {0};
syscall2(SYS_GUI, GUI_CMD_GET_DATETIME, (uint64_t)dt);
char filename[128] = "A:/Desktop/screenshot-";
// Quick helper to append 4-digit and 2-digit numbers
auto void append_num(int num, int digits);
void append_num(int num, int digits) {
int len = 0; while (filename[len]) len++;
if (digits == 4) {
filename[len++] = '0' + (num / 1000) % 10;
filename[len++] = '0' + (num / 100) % 10;
}
filename[len++] = '0' + (num / 10) % 10;
filename[len++] = '0' + (num % 10);
filename[len] = '\0';
}
append_num((int)dt[0], 4);
append_num((int)dt[1], 2);
append_num((int)dt[2], 2);
int len = 0; while (filename[len]) len++;
filename[len++] = '-'; filename[len] = '\0';
append_num((int)dt[3], 2);
append_num((int)dt[4], 2);
append_num((int)dt[5], 2);
len = 0; while (filename[len]) len++;
filename[len++] = '.'; filename[len++] = 'p'; filename[len++] = 'n'; filename[len++] = 'g'; filename[len] = '\0';
// 6. Write to PNG
int fd = sys_open(filename, "w"); // Open file
int res = 0;
if (fd >= 0) {
res = stbi_write_png_to_func(png_write_func, &fd, (int)w, (int)h, 3, rgb_pixels, (int)w * 3);
sys_close(fd); // Close file
}
free(rgb_pixels);
free(pixels);
if (res) {
char notif[256] = "Saved ";
int nlen = 6;
int flen = 0;
while (filename[11 + flen]) {
notif[nlen + flen] = filename[11 + flen];
flen++;
}
notif[nlen + flen] = '\0';
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)notif);
} else {
syscall2(SYS_GUI, GUI_CMD_SHOW_NOTIFICATION, (uint64_t)"Failed to save screenshot");
return 1;
}
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -66,6 +66,13 @@ static char msg_box_text[64];
// Hook definition // Hook definition
void (*wm_custom_paint_hook)(void) = NULL; void (*wm_custom_paint_hook)(void) = NULL;
// Notification state
static char notif_text[256] = {0};
static int notif_timer = 0;
static int notif_x_offset = 300; // Starts offscreen
static bool notif_active = false;
extern bool ps2_ctrl_pressed;
// Dragging State // Dragging State
static bool is_dragging = false; static bool is_dragging = false;
static bool is_resizing = false; static bool is_resizing = false;
@ -1411,7 +1418,18 @@ void wm_paint(void) {
draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT); draw_string(mx + 15, my + 10, msg_box_title, COLOR_DARK_TEXT);
draw_string(mx + 10, my + 40, msg_box_text, COLOR_DARK_TEXT); draw_string(mx + 10, my + 40, msg_box_text, COLOR_DARK_TEXT);
draw_rounded_rect_filled(mx + mw/2 - 30, my + 70, 60, 20, 4, COLOR_DARK_BORDER); draw_rounded_rect_filled(mx + mw/2 - 30, my + 70, 60, 20, 4, COLOR_DARK_BORDER);
draw_string(mx + mw/2 - 18, my + 75, "OK", COLOR_DARK_TEXT); }
// Notification (dark mode)
if (notif_active) {
int nx = sw - 280 + notif_x_offset;
int ny = 40;
int nw = 260;
int nh = 50;
draw_rounded_rect_filled(nx, ny, nw, nh, 8, COLOR_DARK_PANEL);
draw_string(nx + 15, ny + 10, "Screenshot", COLOR_DARK_TEXT);
draw_string(nx + 15, ny + 30, notif_text, COLOR_DKGRAY);
} }
// Custom Overlay (VM Graphics) // Custom Overlay (VM Graphics)
@ -2492,6 +2510,7 @@ static void wm_dispatch_key(char c, bool pressed) {
if (!target) return; if (!target) return;
if (target->handle_key) { if (target->handle_key) {
target->handle_key(target, c, pressed); target->handle_key(target, c, pressed);
} }
@ -2500,7 +2519,26 @@ static void wm_dispatch_key(char c, bool pressed) {
wm_mark_dirty(target->x, target->y, target->w, target->h); wm_mark_dirty(target->x, target->y, target->w, target->h);
} }
void wm_show_notification(const char *msg) {
int i = 0;
while (msg[i] && i < 255) {
notif_text[i] = msg[i];
i++;
}
notif_text[i] = 0;
notif_timer = 180; // ~3 seconds at 60Hz
notif_x_offset = 300;
notif_active = true;
force_redraw = true;
}
void wm_handle_key(char c, bool pressed) { void wm_handle_key(char c, bool pressed) {
if (pressed && c == 'p' && ps2_ctrl_pressed) {
process_create_elf("/bin/screenshot.elf", NULL);
return;
}
int next = (key_head + 1) % INPUT_QUEUE_SIZE; int next = (key_head + 1) % INPUT_QUEUE_SIZE;
if (next != key_tail) { if (next != key_tail) {
key_queue[key_head].c = c; key_queue[key_head].c = c;
@ -2607,6 +2645,26 @@ void wm_timer_tick(void) {
wm_mark_dirty(sw - 110, 6, 110, 24); wm_mark_dirty(sw - 110, 6, 110, 24);
} }
if (notif_active) {
if (notif_timer > 0) {
notif_timer--;
// Slide in
if (notif_timer > 165 && notif_x_offset > 0) { // First 15 ticks (1/4 sec) slide in
notif_x_offset -= 20;
if (notif_x_offset < 0) notif_x_offset = 0;
}
// Slide out
else if (notif_timer < 15 && notif_x_offset < 300) { // Last 15 ticks slide out
notif_x_offset += 20;
}
} else {
notif_active = false;
}
int sw = get_screen_width();
wm_mark_dirty(sw - 280, 40, 275, 60);
}
if (force_redraw) { if (force_redraw) {
graphics_mark_screen_dirty(); graphics_mark_screen_dirty();
force_redraw = false; force_redraw = false;