diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-06 18:11:36 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-09-07 13:59:12 -0300 |
| commit | 60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch) | |
| tree | 310fc734173cf771881f4691c71909135fadde97 /examples/acmefs/clock.py | |
| parent | fa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff) | |
| download | pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.tar.gz pardes-60367d8fe23f6af98ec28e3cf6c2094dfe332df0.zip | |
Refactor panes and filesystem; replace FUSE with 9P
Consolidate pane, layout, memory and host code. Serve 9P by default over Unix sockets, with runtime mounts and optional TCP/QUIC transports. Remove FUSE and obsolete proof-of-concept examples.
Fix highlighting and terminal-history performance, expand differential and stress-test infrastructure, sort navigation results while preserving the next occurrence, add syntax-colored Braille minimaps, remove SPC-k, and document 9P interaction as a repository skill.
Diffstat (limited to 'examples/acmefs/clock.py')
| -rwxr-xr-x | examples/acmefs/clock.py | 163 |
1 files changed, 0 insertions, 163 deletions
diff --git a/examples/acmefs/clock.py b/examples/acmefs/clock.py deleted file mode 100755 index d976544b..00000000 --- a/examples/acmefs/clock.py +++ /dev/null @@ -1,163 +0,0 @@ -#!/usr/bin/env python3 -"""A pardes pane that becomes a live clock, driven only through the acme -control filesystem. python3 stdlib, nothing else. - -WHAT IT DEMONSTRATES - - * Creating a pane is a LOOKUP, not a write: naming any file under `new/` - makes a pane and resolves to that pane's copy of the file. Opening - `new/ctl` is therefore the whole creation handshake, because the ctl read - hands back the new pane's id as its first field. Nothing else in the tree - can create a pane, and READDIR of `new/` creates nothing. - - * The `ctl` verb stream: `name` and `clean` go out in ONE write, newline - separated. ctl writes are all-or-nothing, so a batch either applies whole - or leaves the pane untouched -- which is why sending the pair together is - safer than two writes that could half-fail. - - * `addr` + `data` as a whole-body REPLACE. A `body` write always appends - (the offset is ignored), so redrawing a frame in place needs the address - machinery: write `,` to `addr` to select the entire body, then write the - frame to `data`, which substitutes the addressed text. After that write - `addr` is the null string just past the insertion, so if the kernel splits - a big frame across several write(2) calls the pieces still land in order: - the first replaces, the rest append at the growing end. - -FILES TOUCHED - - new/ctl create the pane, read its id back - <id>/ctl `name /+clock`, `clean` - <id>/addr `,` (whole body) before each frame - <id>/data the frame itself - <id>/ctl `clean` again after each frame, see below - -Every frame ends with `clean` because a data write marks the pane dirty, and a -generated clock face is not user data: a dirty pane refuses `del` and nags on -exit. One extra ctl round trip per second is not a cost worth optimising. - -USAGE - - clock.py [mountdir] default: $PARDES_FS (set in every pane shell) - -Ctrl-C removes the pane and exits. So does the pane being deleted from the -editor: the next addr/data write fails with an OSError, which is the only -"the other end is gone" signal the filesystem gives us, and it is enough. -""" - -import os -import sys -import time - -# 3x5 cells per glyph, doubled horizontally below so the face is legible in a -# character grid, where cells are about twice as tall as they are wide. -FONT = { - "0": ("###", "# #", "# #", "# #", "###"), - "1": (" #", " #", " #", " #", " #"), - "2": ("###", " #", "###", "# ", "###"), - "3": ("###", " #", "###", " #", "###"), - "4": ("# #", "# #", "###", " #", " #"), - "5": ("###", "# ", "###", " #", "###"), - "6": ("###", "# ", "###", "# #", "###"), - "7": ("###", " #", " #", " #", " #"), - "8": ("###", "# #", "###", "# #", "###"), - "9": ("###", "# #", "###", " #", "###"), - ":": (" ", " # ", " ", " # ", " "), -} -BLANK = (" ",) * 5 -XSCALE = 2 - - -def art(text): - """Render `text` as five rows of doubled-width block characters.""" - rows = [] - for row in range(5): - line = " ".join(FONT.get(ch, BLANK)[row] for ch in text) - rows.append("".join(ch * XSCALE for ch in line).rstrip()) - return rows - - -def write_all(fd, data): - """One logical fs write. Short writes are looped over rather than trusted - away: see the addr/data note in the module comment for why the tail of a - split frame still lands in the right place.""" - view = memoryview(data) - while view: - view = view[os.write(fd, view) :] - - -def main(argv): - mount = argv[1] if len(argv) > 1 else os.environ.get("PARDES_FS", "") - if not mount: - sys.stderr.write( - "clock.py: no mount point. Pass one, or run inside a pardes pane\n" - " shell where $PARDES_FS is set (start pardes with --fs).\n" - ) - return 1 - if not os.path.isdir(mount): - sys.stderr.write("clock.py: %s is not a directory\n" % mount) - return 1 - - # The lookup of `new/ctl` is the creation. Read it back for the id, which - # is the first of the five index numbers (id, tag len, body len, isdir, - # dirty) that a ctl read starts with. acme's ctl read has no trailing - # newline, so read the lot and split on whitespace rather than a line. - try: - with open(os.path.join(mount, "new", "ctl"), "rb") as f: - fields = f.read(256).split() - except OSError as e: - sys.stderr.write("clock.py: cannot create a pane: %s\n" % e) - return 1 - if not fields or not fields[0].isdigit(): - sys.stderr.write("clock.py: unexpected new/ctl contents: %r\n" % fields[:1]) - return 1 - pane = fields[0].decode() - - d = os.path.join(mount, pane) - ctl = addr = data = None - try: - # O_WRONLY, never O_TRUNC: truncating a control file is a setattr the - # server has no reason to honour, and `open(..., "wb")` would send one. - ctl = os.open(os.path.join(d, "ctl"), os.O_WRONLY) - write_all(ctl, b"name /+clock\nclean\n") - addr = os.open(os.path.join(d, "addr"), os.O_WRONLY) - data = os.open(os.path.join(d, "data"), os.O_WRONLY) - - while True: - now = time.localtime() - frame = art(time.strftime("%H:%M:%S", now)) - frame.append("") - frame.append(time.strftime("%A %d %B %Y", now)) - payload = ("\n".join(frame) + "\n").encode() - write_all(addr, b",") - write_all(data, payload) - write_all(ctl, b"clean\n") - # Sleep to the next second boundary so the face never skips or - # stutters, and so this loop is never a spin. - time.sleep(1.0 - (time.time() % 1.0)) - except KeyboardInterrupt: - pass - except OSError: - # The pane (or the whole mount) went away. That is a normal ending for - # a script that lives inside someone else's editor, not a crash. - return 0 - finally: - for fd in (addr, data): - if fd is not None: - try: - os.close(fd) - except OSError: - pass - if ctl is not None: - try: - write_all(ctl, b"clean\ndel\n") - except OSError: - pass - try: - os.close(ctl) - except OSError: - pass - return 0 - - -if __name__ == "__main__": - sys.exit(main(sys.argv)) |
