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
+93
View File
@@ -0,0 +1,93 @@
# Makefile for screenshot tool on MontaukOS
# Copyright (c) 2026 Daniel Hammer
MAKEFLAGS += -rR
.SUFFIXES:
# ---- Toolchain ----
TOOLCHAIN_PREFIX := $(shell cd ../../.. && pwd)/toolchain/local/bin/x86_64-elf-
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
CXX := $(TOOLCHAIN_PREFIX)g++
else
CXX := g++
endif
# ---- Paths ----
PROJECT_ROOT := $(shell cd ../../.. && pwd)
PROGRAMS := $(PROJECT_ROOT)/programs
JPEGWRITE_LIB := $(PROGRAMS)/lib/libjpegwrite
LIBC_LIB := $(PROGRAMS)/lib/libc
PROG_INC := $(PROGRAMS)/include
LINK_LD := $(PROGRAMS)/link.ld
BINDIR := $(PROGRAMS)/bin
OBJDIR := obj
# ---- Compiler flags ----
CXXFLAGS := \
-std=gnu++20 \
-g -O2 -pipe \
-Wall \
-Wextra \
-Wno-unused-parameter \
-Wno-unused-function \
-nostdinc \
-ffreestanding \
-fno-stack-protector \
-fno-stack-check \
-fno-PIC \
-fno-rtti \
-fno-exceptions \
-ffunction-sections \
-fdata-sections \
-m64 \
-march=x86-64 \
-msse \
-msse2 \
-mno-red-zone \
-mcmodel=small \
-I $(PROG_INC) \
-isystem $(PROG_INC)/libc \
-isystem $(PROJECT_ROOT)/kernel/freestnd-c-hdrs/x86_64/include \
-isystem $(PROJECT_ROOT)/kernel/freestnd-cxx-hdrs/x86_64/include
# ---- Linker flags ----
LDFLAGS := \
-nostdlib \
-static \
-Wl,--build-id=none \
-Wl,--gc-sections \
-Wl,-m,elf_x86_64 \
-z max-page-size=0x1000 \
-T $(LINK_LD)
# ---- Libraries ----
LIBS := $(JPEGWRITE_LIB)/libjpegwrite.a $(LIBC_LIB)/liblibc.a
# ---- Source files ----
SRCS := main.cpp
OBJS := $(addprefix $(OBJDIR)/,$(SRCS:.cpp=.o))
# ---- Target ----
TARGET := $(BINDIR)/apps/screenshot/screenshot.elf
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS) $(LIBS) $(LINK_LD) Makefile
mkdir -p $(BINDIR)/apps/screenshot
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@
$(OBJDIR)/%.o: %.cpp Makefile
mkdir -p $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -rf $(OBJDIR) $(TARGET)
+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);
}
+8
View File
@@ -0,0 +1,8 @@
[app]
name = "Screenshot"
binary = "screenshot.elf"
icon = "screenshot.svg"
[menu]
category = "System"
visible = true