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

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*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF
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);
@@ -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*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
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);
@@ -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*)&fat_buf[fat_offset] = (old_val & 0xF0000000) | 0x0FFFFFF8; // EOF
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);
@@ -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) {
*(uint32_t*)&fat_buf[fat_offset] = 0; // Free
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);
@@ -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*)&sect_buf[fat_offset] = (old_val & 0xF0000000) | (expanded & 0x0FFFFFFF);
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
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) {
if (x < 0) { w += x; x = 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_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_copy_screenbuffer(uint32_t *dest);
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_FONT_HEIGHT 9
#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_PAINT 1

View File

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

View File

@@ -5,7 +5,7 @@ CC = x86_64-elf-gcc
AS = nasm
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
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
$(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
$(CC) $(CFLAGS) -Wno-error -Idoom -c $< -o $@

View File

@@ -185,17 +185,7 @@ void _exit(int 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 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;
}
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
size_t strlen(const char *s) {
size_t len = 0;

View File

@@ -1,6 +1,11 @@
#ifndef 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

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
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
static bool is_dragging = 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 + 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_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)
@@ -2492,6 +2510,7 @@ static void wm_dispatch_key(char c, bool pressed) {
if (!target) return;
if (target->handle_key) {
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);
}
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) {
if (pressed && c == 'p' && ps2_ctrl_pressed) {
process_create_elf("/bin/screenshot.elf", NULL);
return;
}
int next = (key_head + 1) % INPUT_QUEUE_SIZE;
if (next != key_tail) {
key_queue[key_head].c = c;
@@ -2607,6 +2645,26 @@ void wm_timer_tick(void) {
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) {
graphics_mark_screen_dirty();
force_redraw = false;