CHANGELOG 0.3.4.md — Notepad

Added

  • The userland malloc no longer lives in a fixed BSS arena: it grows by asking the kernel for sections. The single BSS arena was resident physical RAM per process from exec onwards even if the app never touched a byte. A 256 KiB bootstrap stays in the BSS and the rest comes from section_create + map_view. External apps stop forcing -DSX_HEAP_SIZE. A normal malloc can now return addresses above 4 GiB.

  • The whole VRAM aperture is mapped write-combining. The firmware maps only the visible mode (4000 KiB of 16 MiB), and that was the ceiling for everything. fb_gpu now asks dispi how much VRAM there is and maps the complete aperture, choosing the WC index from IA32_PAT instead of assuming the layout. New in vm::: kPagePat, map_kernel_device_memory, kernel_page_cache_flags, write_combining_page_flags.

  • Double buffering by panning on the flat framebuffer (no tearing). With a virtual height twice the visible one, dispi's Y_OFFSET picks which frame is shown; the compositor always composes onto the one not on screen. Every present flips, partial damage included, by reapplying the previous frame's damage on top of the current one. boot::FramebufferInfo gains mapped_bytes, which decides whether both buffers fit.

  • The flat framebuffer backend can change modes (Bochs VBE). It detects the dispi interface (ports 0x1CE/0x1CF, implemented by QEMU's standard VGA and VBoxVGA) and advertises MUTABLE_MODE_SETTING. The ceiling is the native mode; changing modes needs the graphics session and no live imported surfaces, and releasing the session returns to native. New boot log: fb_gpu: <W>x<H> native, mode-setting ....

  • The SXE format: executables carry their identity inside. A <name>.sxres manifest is stamped into non-alloc ELF sections (.sxmeta/.sxicon), so the binary declares its own title, version, icons, accent and the extensions it opens. Program Manager, windowd and Files read from there instead of per-path tables that had to be recompiled. Format in docs/userland/SXE_FORMAT.md; new build.ps1 sxe-smoke and filesapp-smoke.

  • Default button in dialogs (default_button in struct sxgui_dialog). Enter triggers it and it is drawn with the period double border. Declared as Save in the notepad, OK in the About boxes and No in the shutdown one.

  • The notepad warns before losing unsaved changes. Exit, New and Open ask Save / Discard / Cancel and resume the action after saving. Closing via the window's X still kills the process: the WM sends SIGKILL instead of requesting a close.

  • Notepad (/bin/notepad). A Win95-shaped text editor: File/Edit/Search/Help menu, full text area and a status bar with the open file and a modified marker. New, Open..., Save, Save As...; F2 saves, F3 opens. 32 KB per document. It is the OS's first app that writes files.

  • Multiline editor in sxgui (sxgui_textedit). Caret, insertion and deletion, Enter splitting the line, arrows preserving the column, Home/End/PageUp/PageDown, click positioning and vertical scrolling. No word wrap: long lines follow the caret horizontally.

  • A launch can carry an argument. savanxp_desktop_launch_request gains an argument field the WM passes as argv[1], exposed as gfx_desktop_launch_arg(). It is what enables "open this file".

  • Dialogs can start with focus on a widget (initial_focus in struct sxgui_dialog). Without it a text input dialog opened with no focus.

  • Listbox with columns in sxgui (details view). Setting columns draws a fixed header and splits each item by TAB, with optional right alignment (SXGUI_COLUMN_RIGHT) and per-cell clipping. Opt-in.

  • Size hint: every window starts at the size of its content. A new client->WM channel (SAVANXP_WM_FD_SIZE_HINT, fd 11) the WM applies once at startup, clamped to the surface capacity. SDK: gfx_request_content_size() / gfx_wait_content_size(); in sxgui, sxgui_app_autosize() or sxgui_app_set_content_size().

  • svfs-cli rm: something can be taken out of an SVFS2 image. Files and empty directories only. Previously the sync was purely additive and removing a binary meant recreating the whole image.

  • Resizing windows by their edges. Edges and corners drag and the cursor anticipates them (RESIZE_H/RESIZE_V), with the opposite edge anchored. Not for frameless, maximized or fullscreen windows.

  • Multi-shape Win9x-style cursors. Eight shapes (arrow, wait, text, move, resize-h, resize-v, unavailable, link). Apps request theirs over savanxp_desktop_cursor_hint and the WM resolves by priority.

  • Factory wallpaper in /disk/wallpaper.bmp. Committed in diskfs/ and generated by tools/GenerateDefaultWallpaper.py with a fixed seed. It is changed from Program Manager's Options, which rewrites /disk/desktop.cfg.

  • build.ps1 net-smoke: automated NIC coverage. PCI presence, a non-zero MAC, address and gateway, and ARP + ICMP against slirp's gateway requiring the tx/rx counters to advance. The network had no harness before.

  • build.ps1 build -NoTestApps: an image without the diagnostic apps. Test binaries stay out of the rootfs (59 -> 32) and the launcher is built without their entries. The automation commands always include them.

Changed

  • Window frames now use the Windows 95 caption treatment. Active and inactive title bars use the period gradients, classic 16x14 caption buttons, and the shared system 3D palette; the Task List follows the same scheme.

  • Application accents tint the active title gradient. The declared accent is blended more strongly into the dark end and more gently into the light end; inactive captions and the Task List keep the shared system colors.

  • Client controls sit closer to the window edge. The shared inset is now 4 px for window content and 8 px for dialogs, keeping controls attached without losing their breathing room.

  • F11 lowers the scanout resolution instead of scaling in software. The shell used to stretch a fullscreen app's 640x400 buffer every frame, at two integer divisions per output pixel. It now asks the compositor for the client surface's mode (new SAVANXP_COMPOSITOR_MSG_SET_MODE) and the pixels go 1:1. The mode belongs to the scanout, so it is restored on leaving fullscreen, on the app dying and on the compositor dying. Without mode setting, scaled as before.

  • The kernel's cached geometry is restored on every mode change. GPU_IOC_SET_MODE now calls ui::sync_framebuffer_geometry(), which reprograms the absolute pointer's extent. With the old extent the cursor pointed elsewhere after a mode change.

  • memcpy/memset stop moving memory one byte at a time. The tree's three implementations were C loops of one byte per iteration, and the tree is built without -O. They now use rep movsq / rep stosq plus the byte tail; 8 bytes is the maximum with -mgeneral-regs-only. windowd-smoke drops from ~37 s to ~30 s end to end.

  • cld on every kernel entry and at the start of every process. DF is part of the process's RFLAGS, so a program could make the kernel's string instructions walk backwards. It is the precondition for the memory routines above, and for turning on -O2 later.

  • Full-rectangle blits are copied in a single pass. fb_gpu::blit_rect and sx_painter_blit_bitmap issued one memcpy per row even when the rows were already contiguous in source and destination. Small dirty rects still go row by row.

  • Files opens files in the notepad. Activating something that is not a program launches /bin/notepad with the path instead of refusing.

  • Files takes the shape of the Win95-era file explorer. Address bar with "Up One Level", a details list with a Name/Size/Type header, and a two-pane status bar with the object count and total. The menu becomes File/View/Help.

  • Files no longer shows file contents. The preview pane is gone: reading a file is an editor's job. Opening something unlaunchable now says so in the status bar instead of dumping the first bytes.

  • Display, audio, block and NIC pick drivers through a registry, not branches in kernel_main. Each driver self-describes with register_driver and the HAL binds by priority: bind_best for display, audio and NIC; probe_all for block, whose devices coexist. API: block::initialize -> block::probe_all, block::register_ramdisk -> ramdisk::attach_image. New boot logs: display:, audio:, nic:, block: N device(s).

  • Desktop assets: from System.Drawing (GDI+) to Pillow. The three generators move to Python + Pillow, with the same generated C header and no ABI change. New build requirement on every platform: python3 + Pillow.

  • Build migration to Linux: host paths and tools. The separate builds use / instead of a literal \, and Build-Iso compiles the limine deployer with make when there is no prebuilt binary. New requirement on Linux/macOS: make

    • cc.
  • Program Manager no longer lists programs that are not installed. New progman_registry_prune_missing(exists) discards items whose path cannot be opened and the groups left empty. It can leave the registry empty, which is the honest answer.

  • The build no longer regenerates the desktop art if nothing changed. With a Pillow version different from the one that produced the committed PNGs, every build left spurious binary diffs in assets/desktop/. The generators now compare mtimes.

  • QEMU is assembled with "base" hardware by default. Standard VGA, PS/2 mouse and AC'97 audio, so the fallback backends get exercised without leaving QEMU. -Virtio goes back to the paravirtualized path and applies to every command that launches QEMU.

  • The window manager was separated from the shell (NT 3.5 model). The old desktop process managed windows and drew the Win95 chrome at once. The WM is now windowd and the shell is client processes: shellui for the background and progman for the launcher, with /disk/progman.ini editable without recompiling. Taskbar, start menu and desktop icons give way to the Task List (Ctrl+Esc). Contract in savanxp/wm_protocol.h.

  • SVFS2 has a single implementation, shared kernel<->host. The on-disk format lives in include/svfs/svfs_format.h and is compiled by both the kernel and the host tool. libsvfs/ adds the portable core and svfs-cli, which does all writes to build/disk.img; the legacy PowerShell writer was deleted. That double implementation was the historical source of the desync bugs.

Removed

  • The Haxe variants of About and Files (aboutapp-hx, filesapp-hx) were retired. They were AOT-chain validation demos, not official apps. They go with their harnesses, the native-about/native-files targets and the Native launcher group, plus the haxe-toolkit/ widgets only they used. The sx_sysinfo.c/sx_fs.c runtimes stay: they are declared native ABI surface.

  • Deleted subsystems/posix/userland/busybox.c. The hand-written multicall was built by nothing any more: the installed applets come from vendor/busybox-port.

Fixed

  • realloc could hang while growing a block. The in-place growth loop called sx_merge_with_next on an occupied block, and that helper does nothing unless the block is free, so the loop never advanced. sx_absorb_next now reports whether it absorbed anything.

  • The AC'97 driver truncated the userland buffer address to 32 bits. copy_period took it as a uint32_t. It was latent while every audio buffer came from the ELF image or the BSS, but a section view is mapped at 64 GiB, and there audio_write returns EINVAL — a client that mutes on the first write error (Doom) stays mute for the session. audiotest now allocates its buffer with section_create so the 64-bit path is exercised.

  • A smoke left the automation spec stuck forever. The build wrote SMOKE into the rootfs but never deleted it, so init started that runner instead of the desktop on every later build, until the next clean.

  • Opening an app briefly showed the empty window at the generic size. The WM composed the window from the fork, without waiting for the first frame. While the client starts, the feedback is now the WAIT cursor.

  • Resize: the window ended up half black. resize_overlay_client_surface cleared the surface after publishing the new dimensions, erasing the frame the client had just copied. It surfaced with the size hint, but the bug was in edge resizing all along.

  • The power button did not shut the machine down: the SCI was routed twice. acpi::start_sci() and the uACPI glue routed the same GSI, overwriting each other. An interrupt line now admits several owners, which also covers two PCI links resolving to the same GSI. New acpi::enable_power_button(), called again after uacpi_initialize turns off every fixed event.

  • VirtualBox with I/O APIC enabled did not finish booting: xAPIC MMIO support in the local APIC. initialize_local_apic only spoke x2APIC over MSR, which VBox never exposes, so with the I/O APIC active the firmware masked LINT0, the PIT fallback died and the boot froze on the splash. It now maps the MMIO window from IA32_APIC_BASE and restores LINT0 as ExtINT. New log: cpu: local APIC in xAPIC|x2APIC mode (id N).

  • Key Test overwrote the second help line with the first event, and Mouse Test clipped the last line of its panel.

  • The build did not compile on native Linux. svfs-cli needs _POSIX_C_SOURCE for fseeko/off_t under clang -std=c11, and New-SvfsManifest built relative paths assuming a \ separator, leaving a leading / that svfs-cli apply rejected. Neither affects Windows.

  • exec/spawn reported ENOENT for any load failure. A perfectly installed binary failed with no such file or directory when the real problem was memory. Each step now reports its own reason: ENOMEM, ENOEXEC, EIO, EACCES, and the kernel logs the failing step and the free page count.

  • The WM hung entirely because of a client that did not drain its input. Opening a second instance of a program froze the whole session: the WM writes input non-blocking, but the kernel ignored O_NONBLOCK on partial pipe writes.

  • The cursor froze with the Task List open. Its handler consumed the pointer event and skipped the cursor repaint.

  • apply on build/disk.img failed with "no contiguous space" without being fragmented. The buffer where ensure_capacity preserves the inode was on the stack and capped at 20400 bytes. The image also self-compacts now and apply retries.

  • ps printed the literal %-13s in the STATE column. The busybox applet's printf did not support the - left-justification flag.

Version 0.3.42026-08-2849 changes