feat: add screenshot app

This commit is contained in:
2026-03-22 13:28:49 +01:00
parent 9030485fcb
commit a805b06406
10 changed files with 2097 additions and 4 deletions
+168
View File
@@ -0,0 +1,168 @@
/*
* main.cpp
* MontaukOS Screenshot Tool
* Captures the screen framebuffer and saves as JPEG to the user's Pictures directory.
* Copyright (c) 2026 Daniel Hammer
*/
#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/heap.h>
#include <montauk/config.h>
extern "C" {
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define STBI_WRITE_NO_STDIO
#include <gui/stb_image_write.h>
}
// ============================================================================
// JPEG write callback - accumulates encoded bytes into a resizable buffer
// ============================================================================
struct JpegBuffer {
uint8_t* data;
uint64_t size;
uint64_t capacity;
};
static void jpeg_write_callback(void* context, void* data, int size) {
JpegBuffer* buf = (JpegBuffer*)context;
if (size <= 0) return;
uint64_t needed = buf->size + (uint64_t)size;
if (needed > buf->capacity) {
uint64_t new_cap = buf->capacity * 2;
if (new_cap < needed) new_cap = needed;
uint8_t* new_data = (uint8_t*)montauk::realloc(buf->data, new_cap);
if (!new_data) return;
buf->data = new_data;
buf->capacity = new_cap;
}
montauk::memcpy(buf->data + buf->size, data, size);
buf->size += (uint64_t)size;
}
// ============================================================================
// Entry point
// ============================================================================
extern "C" void _start() {
// Get framebuffer info before doing anything else
Montauk::FbInfo fb;
montauk::fb_info(&fb);
int width = (int)fb.width;
int height = (int)fb.height;
int pitch = (int)fb.pitch;
if (width <= 0 || height <= 0) {
montauk::print("screenshot: no framebuffer available\n");
montauk::exit(1);
}
// Map the hardware framebuffer (read-only access to screen pixels)
uint32_t* hw_fb = (uint32_t*)montauk::fb_map();
if (!hw_fb) {
montauk::print("screenshot: failed to map framebuffer\n");
montauk::exit(1);
}
// Copy framebuffer and convert from BGRA (0xAARRGGBB) to packed RGB
uint64_t rgb_size = (uint64_t)width * height * 3;
uint8_t* rgb = (uint8_t*)montauk::malloc(rgb_size);
if (!rgb) {
montauk::print("screenshot: out of memory\n");
montauk::exit(1);
}
for (int y = 0; y < height; y++) {
uint32_t* src_row = (uint32_t*)((uint8_t*)hw_fb + y * pitch);
uint8_t* dst_row = rgb + y * width * 3;
for (int x = 0; x < width; x++) {
uint32_t px = src_row[x];
dst_row[x * 3 + 0] = (px >> 16) & 0xFF; // R
dst_row[x * 3 + 1] = (px >> 8) & 0xFF; // G
dst_row[x * 3 + 2] = px & 0xFF; // B
}
}
// Encode as JPEG (quality 100 - maximum quality)
JpegBuffer jpeg;
jpeg.data = (uint8_t*)montauk::malloc(256 * 1024);
jpeg.size = 0;
jpeg.capacity = 256 * 1024;
if (!jpeg.data) {
montauk::mfree(rgb);
montauk::print("screenshot: out of memory for JPEG buffer\n");
montauk::exit(1);
}
int ok = stbi_write_jpg_to_func(jpeg_write_callback, &jpeg,
width, height, 3, rgb, 100);
montauk::mfree(rgb);
if (!ok || jpeg.size == 0) {
montauk::mfree(jpeg.data);
montauk::print("screenshot: JPEG encoding failed\n");
montauk::exit(1);
}
// Build output path: 0:/users/<username>/Pictures/screenshot_YYYYMMDD_HHMMSS.jpg
char username[32] = {};
{
auto doc = montauk::config::load("session");
const char* name = doc.get_string("session.username", "");
if (name[0] == '\0') {
doc.destroy();
montauk::mfree(jpeg.data);
montauk::print("screenshot: no user session\n");
montauk::exit(1);
}
montauk::strncpy(username, name, sizeof(username) - 1);
doc.destroy();
}
char home[128];
snprintf(home, sizeof(home), "0:/users/%s", username);
// Ensure Pictures directory exists
char pictures_dir[192];
snprintf(pictures_dir, sizeof(pictures_dir), "%s/Pictures", home);
montauk::fmkdir(pictures_dir);
// Build filename with timestamp
Montauk::DateTime dt;
montauk::gettime(&dt);
char filepath[256];
snprintf(filepath, sizeof(filepath),
"%s/screenshot_%04d%02d%02d_%02d%02d%02d.jpg",
pictures_dir,
(int)dt.Year, (int)dt.Month, (int)dt.Day,
(int)dt.Hour, (int)dt.Minute, (int)dt.Second);
// Write JPEG file
int fd = montauk::fcreate(filepath);
if (fd < 0) {
montauk::mfree(jpeg.data);
montauk::print("screenshot: failed to create file\n");
montauk::exit(1);
}
montauk::fwrite(fd, jpeg.data, 0, jpeg.size);
montauk::close(fd);
montauk::mfree(jpeg.data);
// Print confirmation
char msg[320];
snprintf(msg, sizeof(msg), "Screenshot saved: %s (%dx%d, %llu bytes)\n",
filepath, width, height, (unsigned long long)jpeg.size);
montauk::print(msg);
montauk::exit(0);
}