SchOS/cmd/schmode/main.c
Nexoniarz cb3d28e3d6 docs: README, plan, guides and the site describe what is built now
README: rewritten around what runs (Linux software, Xorg, apk, Linux's drivers), the honest gaps, measured numbers.
plan/: 1.0.md is the finish line, where each clause of the acceptance test stands, and the register (one failing
clause: a root that survives a reboot); built plans (foundations, hybrid, alpine, xorg) and the decided-against one
(wayland) are gone, their content lives in architecture/; roadmap.md is only what is still open.
guides: building/layout/testing/adding-a-driver (Linux code may be used). history: four entries (ls -lR, TLB
shootdowns, MDIO 0x02460246, netlink MSG_TRUNC). Website: cards, stack, download page, docs index.
Code comments that pointed at deleted plan docs point at the architecture docs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EVRhxi9zpdW2uDUN4FVcao
2026-10-26 08:08:05 +01:00

109 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// schmode -- change the display mode.
//
// docs/architecture/devices.md milestone H3. The interesting thing about this program
// is how little it is: it opens a socket to `vgpud` and writes a line. No
// syscall it makes has heard of a GPU, no kernel code runs that knows what a
// scanout is, and if the driver on the other end gets the sequence wrong the
// worst that happens is that one process dies and schinit starts another.
//
// That is the whole argument for moving the driver out, stated as a program
// rather than as a paragraph. An in-kernel driver could do the same modeset,
// and the same mistake would take the machine.
//
// schmode what the display is doing now
// schmode modes what it will accept
// schmode 1024 768 set it
// schmode 1024x768 the same
#include "core/types.h"
#include "uapi/user.h"
#include "net/socket.h"
#define VGPUD_SOCKET "/run/vgpud"
static void
puts_(const char *s)
{
int n = 0;
while (s[n]) n++;
write(1, (void *)s, n);
}
static int
dial(void)
{
struct sockaddr_un sa;
int fd, i;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
return -1;
for (i = 0; i < (int)sizeof(sa.sun_path); i++)
sa.sun_path[i] = 0;
sa.sun_family = AF_UNIX;
for (i = 0; VGPUD_SOCKET[i] && i < (int)sizeof(sa.sun_path) - 1; i++)
sa.sun_path[i] = VGPUD_SOCKET[i];
if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
close(fd);
return -1;
}
return fd;
}
int
main(int argc, char *argv[])
{
char buf[256];
char line[128];
int fd, n = 0;
fd = dial();
if (fd < 0) {
// Naming the likely cause rather than the errno. On a machine with no
// virtio-gpu, vgpud reports that and exits on purpose -- see
// SCHINIT_EXIT_NORESTART -- so "not running" is the ordinary case and
// not a fault.
puts_("schmode: cannot reach vgpud on " VGPUD_SOCKET "\n");
puts_(" it is not running -- check: cat /proc/schinit\n");
return 1;
}
// Everything after the program name, rejoined. "1024 768" and "1024x768"
// both arrive as one line either way, because the server parses digits and
// skips whatever separates them.
if (argc < 2) {
line[n++] = 'i';
} else {
if (argv[1][0] >= '0' && argv[1][0] <= '9') {
// A bare size is a set, not a subcommand.
line[n++] = 'm'; line[n++] = 'o'; line[n++] = 'd';
line[n++] = 'e'; line[n++] = ' ';
}
for (int i = 1; i < argc; i++) {
if (i > 1 && n < (int)sizeof(line) - 1)
line[n++] = ' ';
for (int j = 0; argv[i][j] && n < (int)sizeof(line) - 1; j++)
line[n++] = argv[i][j];
}
}
line[n++] = '\n';
if (write(fd, line, n) != n) {
puts_("schmode: vgpud would not take that\n");
close(fd);
return 1;
}
// One answer, then leave. The server writes its reply and waits for the
// next line; it does not close, so reading to EOF would wait for ever.
n = read(fd, buf, sizeof(buf));
if (n > 0)
write(1, buf, n);
close(fd);
return 0;
}