summaryrefslogtreecommitdiff
path: root/examples/acmefs/clock.py
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-06 18:11:36 -0300
committerGabriel Schneider <[email protected]>2026-09-07 13:59:12 -0300
commit60367d8fe23f6af98ec28e3cf6c2094dfe332df0 (patch)
tree310fc734173cf771881f4691c71909135fadde97 /examples/acmefs/clock.py
parentfa82cac885cb4738fe36d1e49b4749b5a3e31a4a (diff)
downloadpardes-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-xexamples/acmefs/clock.py163
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))