SchOS/cmd/schmode/main.c
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

109 lines
3.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
//
// schmode -- change the display mode.
//
// docs/plan/hybrid.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;
}