aboutsummaryrefslogtreecommitdiffstats
path: root/src/compat_win.c (follow)
Commit message (Collapse)AuthorAge
* shaders: remove the .so plugin loaderuser2026-09-02
| | | | | | | | | | | | The static musl release builds cannot dlopen -- musl compiles it out -- so loadable shader plugins were a feature most shipped binaries could not use. Nothing we ship used it either: every shader in contrib/shaders is an expression. Gone: the dlopen/glob loader in shader.c, shader_dir in the config, the plugin ABI table and entry macro, the Windows dl*/glob shims that existed only for it, the contrib/shader-plugin skeleton, its tests, and every doc mention. shader_abi.h stays as the shader model (cell_t, shade_ctx_t, shader_t) with a note about what it used to be.
* windows watcher: rebuild the watch set instead of freezing itPeter Geissler2026-08-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | watch_config() rebuilds the set of watched directories after every reload, because a config that just changed may include different files than it did a second ago. The inotify and kqueue branches unwatch and re-add. The Windows branch did neither: unwatching was a comment saying there was nothing to do, and sl_watch_add() appended to an array the pump thread had already finished reading. The pump captured `int n = w->ndirs` once, at the top, and armed one ReadDirectoryChangesW per directory it found. Everything added after that was stored and never watched. So an include added to a running session loaded fine and then sat there: editing it reloaded nothing, while editing the main config still worked, which is the shape of bug that gets reported as "sometimes". It was worse than deterministic. `w->ndirs` was written by the server thread and read by the pump with nothing between them, so whether a directory made it in depended on which won the start-up race -- _beginthreadex returns before the new thread runs, so the includes present at startup usually did, and the ones added later never could. And because nothing was ever closed, each reload re-added the same directories: handles accumulated until the cap of 8, after which sl_watch_add() failed quietly and no new directory could be watched at all for the life of the session. So the pump owns the handles and the server owns the wish. An overlapped read can only be cancelled by the thread that issued it, which settles where the boundary goes: the server writes a directory list under a lock, bumps a generation and pokes an event; the pump notices the generation moved, disarms what it had and arms what is wanted now. Cancelling waits for the cancellation to land before the slot is reused, because the OVERLAPPED and its buffer live in that slot. Duplicate directories are dropped on the way in -- a config and the theme beside it are usually in the same one -- and the compare is case-insensitive, as the filesystem is. sl_watch_close() also stopped every watcher in the process rather than the one it was given, and freed nothing. It now finds its own, joins the thread, and releases what that leaves behind. Verified on the Windows box, with a fully backslashed SLOSH_CONFIG: editing the config reloads; adding an include and then editing it reloads; repointing the include at an unrelated directory reloads; editing the file the include used to name does *not*, which is the rebuild working rather than an append; and after twelve reloads in a row it is still watching, where the old cap would have given up after eight.
* windows: a native port, on ConPTY and AF_UNIXPeter Geissler2026-08-28
slosh builds and runs on Windows 10/11, x86-64 and ARM64, as one exe with no runtime dependency beyond the OS: panes, splits, tabs, detachable sessions, the config file and its live reload, the control socket, the mouse. The port is a compatibility layer rather than a fork. compat/win holds stub <poll.h>, <sys/socket.h>, <termios.h> and friends that redirect to include/compat_win.h, which is force-included ahead of every translation unit -- so the existing sources keep their POSIX includes and their POSIX call sites, and the platform differences are answered in one place. Two decisions carry most of it. Everything the event loop waits on is a socket. On POSIX a pty master, an inotify descriptor and a unix socket are all file descriptors, so one poll() covers them; Windows has no such union, and WSAPoll waits on SOCKETs and nothing else. Rather than rewrite server.c's loop around WaitForMultipleObjects -- which caps at 64 objects and cannot express POLLOUT -- each non-socket source gets a pump thread that copies it into one end of a loopback socketpair. The loop is then the POSIX one unchanged, and only what sits behind the descriptor differs. Socket descriptors are tagged rather than guessed: a wrapped SOCKET comes back as SL_SOCK_BASE + slot, so read, write, close and poll can tell a socket from a CRT descriptor with certainty instead of inferring it from the numeric range. That is why socket() and accept() are shimmed too -- an untagged descriptor escaping into the loop is the one failure mode this scheme has, and it is worth closing completely. Three things bit hard enough to be worth naming. A pseudoconsole does not stop the child inheriting your stdio. CreateProcess copies the parent's standard handles into the child when STARTF_USESTDHANDLES is absent, and those win. With slosh's own stdout redirected -- a pipe under ssh, a file under --script, a log under the daemon -- the shell wrote its banner into slosh's output instead of into the pane, and the pane had a correct title (ConPTY emits that itself) over an empty body. The three handles are cleared for the duration of the call, leaving the child nothing to inherit. A ConPTY never reports EOF. The output pipe belongs to the pseudoconsole, not to the child, so it stays open after the child exits and pane_pump()'s read() -- the entire mechanism by which a pane learns its program is gone -- never returns 0. A pane whose shell had been told `exit` sat there looking alive forever. So the exit is waited for on the process handle, and ClosePseudoConsole is what then produces the EOF. That third thread is also why the per-pty state is reference counted: a pump blocked on a dead pipe must not be waited for, or pty_close would hang the session on the one program that misbehaves, so whoever finishes last frees. cmd.exe does not use CommandLineToArgvW's quoting. A literal quote is \" to an ordinary program and a backslash-then-end-of-quoted-section to cmd, so building `cmd /c notepad "C:\...\config.kdl"` the first way handed the editor \"C:\...\config.kdl\", backslashes and all, and notepad answered "Not a valid file name" about a path that existed. cmd /c now gets the rest of the line verbatim -- the caller composed a complete command with its own quoting, exactly as it does for sh -c. Paths are mixed, and only some programs mind. slosh joins with '/' while the environment supplies '\', so the config landed at C:\Users\you/.config/slosh/config.kdl. The CRT opens that without complaint, which is why --check reported it fine, but a GUI file dialog rejects it outright. Anything handed to another program now goes through sl_path_native() first; the internal representation is left alone. The rest is small. Panes run %ComSpec%, sessions live in %LOCALAPPDATA%\slosh rather than $XDG_RUNTIME_DIR because a session socket must not follow a roaming profile to another host, there is no SIGWINCH so a resize is noticed by comparing the console size on a bounded poll, no fork so the daemon is a fresh detached process that reconstructs its arguments on the command line, and no process group on a pty so the foreground program is the youngest descendant of the pane's shell. C-a e prefers whichever console editor is actually installed -- notepad is a GUI program, so a pane running it draws nothing and looks broken -- and says so when it has to fall back. make -f Makefile.windows ARCH=x86_64 (or aarch64) cross-compiles from a mac or Linux, and builds natively on the box with zig on PATH.