Repository navigation
Add native Mac and Windows apps with a shared desktop client - #18
Draft
willibrandon wants to merge 27 commits into
Draft
willibrandon wants to merge 27 commits into
willibrandon wants to merge 27 commits into
Conversation
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
A Windows child inherits its creator's standard handles unless they belong to a console. The detached server inherited the launching command's pipes, so a script reading that command's output waited for the server to exit, and shells started with redirected handles wrote to those pipes instead of their pseudo console. The launcher now creates the server with no inherited handles, a windowless console, and its own process group, and the server points any redirected standard handle at that console before hosting blocks. The tests drove /bin/sh, printf, stty, dd, and od. They now run a small file-based C# shell built next to the test assembly, so the same scripts exercise real pseudo-terminals on every operating system.
The Windows app is a C# Reactor project that references the shared client core directly. Its terminal surface is a native control drawn with Win2D: frames invalidate only the rows that changed, glyphs found in the terminal font are batched into one run per row and style at exact cell advances, and anything else is shaped with system fallback inside its own cells. A hidden text box at the caret gives Windows text services a real editing target, so input methods, dead keys, and AltGr produce text through it. Window chrome renders the shared action catalog as a session menu, tabs, and a menu of every command, with Windows shortcut defaults that leave plain Ctrl and Alt keys to the terminal. The app publishes with Native AOT. Two measures keep that working with the current preview toolchain: the entry point constructs an array type the .NET 11 RC1 compiler's scanner does not predict, and publishing copies the resource index the unpackaged layout leaves behind.
A new test project hosts the Reactor app on one UI thread inside the test process and drives real windows against private servers and the test shell. Tests reach the same key, text, composition, pointer, and wheel entry points real input does, operate menus, tabs, dialogs, and the scroll bar through UI Automation, and check composed pixels with Windows Graphics Capture. The windows open behind the active one and never take the keyboard. Those tests found wide and emoji cells that drew nothing, because text drawn at a point was clipped to an empty layout box. Opening Find replaced the terminal surface, because unkeyed Reactor children are matched by position. A cancelled composition discarded the next typed text, pasted line feeds never pressed Enter, a rejected input notice outlived reconnection, and dialogs opened from menus returned focus to the closed menu. All are fixed, and the history scroll bar now also moves through UI Automation. The build and package scripts produce a Native AOT app folder and an unsigned development MSIX. Only the WinUI and windowing components of the Windows App SDK ship, which cuts the folder from 169 MB to 111 MB. Windows ends a package's processes when it updates or removes the package, so a packaged app copies its server into local app data and starts it outside the package. Test-WindowsApp.cs covers the published app and, from an elevated terminal, the installed lifecycle. CI runs the portable suite, the Native AOT CLI, and the Windows app on x64 and ARM64, and CodeQL analyzes a real Windows build. The test shell reads console input as UTF-16. Byte reads make the console convert one UTF-16 unit at a time, which turns every character outside the Basic Multilingual Plane into two replacement characters.
The desktop design now describes the Windows implementation, its shortcuts, how its server stays outside the package, and how its window tests run. The parity contract maps the Mac test groups to Windows evidence and lists what still needs hands-on qualification. The README and contribution guide cover building, packaging, and testing on Windows, including the Visual C++ linker and the long PATH that keeps publishing from finding it.
The server log was one static file for the whole process. Tests run many servers in one process, so a stopped server's log stayed open under its state directory, and on Windows that kept the directory and its lock from being released. Each server now opens its own log for the execution context that runs it and closes it when it stops. The Windows CodeQL job extracts sources without a build. A traced build also extracted source generator output, which is compiler code and which path filters cannot exclude for compiled languages. The remaining findings were real: parameterless GC.Collect, exact float equality, integer arithmetic converted to floating point, direct extern calls, collection expression casts, and a pattern-bound disposable not disposed on throw. Each is fixed at the call site, and Weft.SourceGen gains a matching rule with tests so the local build fails first. The collection cast rule had missed nullable contexts and now compares the collection type alone. Selection uses the Mac's dark selected text color, and Find matches use its dark system yellow at 35 percent. Tests that compare sizes use a tolerance. The Windows app tests stream their output and write a TRX report, the package phase runs against its own server, and a failed synchronized input test now reports both screens.
CodeQL reported a complex condition in Weft.SourceGen. The repository analyzers mirror CodeQL in every project except that one, because an analyzer project cannot load itself, so nothing local could have caught it. A new test compiles the analyzer sources and runs every CodeQL guard over them. The condition now looks up the product's range in a table, and three loops that filtered with a braced continue now filter before iterating. The x64 window tests failed because Windows Server denies Windows Graphics Capture to desktop apps until someone consents, and a CI runner has no one to ask. Windows 11 allows it by default, which is why the ARM64 job passed. In CI, Test-WindowsApp.cs now grants that consent before the tests run, and a denied capture reports the access statuses. The development MSIX indexed its logos relative to the Assets folder, while the manifest names them with it, so the taskbar and Start found no icon. Each logo is now listed by its path in the package.
The synchronized input test typed its next command into the source block as soon as the sibling showed its output, while the source could still be running its own copy. A Unix terminal echoes typed-ahead input at once, so the next output landed on the source's following prompt line and never matched a line of its own. The test now waits for the source's output and prompt first. The traced Swift build compiled with the optimizer, which CodeQL never reads, and its 15-minute limit was tighter than the runner allows. Tracing copies and re-signs the Xcode compiler before building, and every phase scales with the runner's speed: the same build has taken from 6 to more than 15 minutes. The analysis build now skips optimization, and the limits only guard against a hang.
A new window shows a connecting status line until its first frame, and hiding it gives the terminal more rows at the next layout. The two-window test resized the first window as soon as the second showed a block, so the second window's resize could arrive afterwards. Sessions take the most recently active client's size, which put the session back at the second window's size while the test waited for the first's. The test now lays out the second window and waits for the session to match its final grid first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Weft now opens persistent terminal sessions in native apps on Mac and Windows, with tabs, splits, scrollback, search, graphics, and native shortcuts. Closing a window leaves its shells running for the next attachment. Both apps drive the same desktop client, so sessions, commands, and terminal state behave alike on each.
The Windows app is built with Microsoft UI Reactor and draws the terminal with Win2D. A hidden text box follows the caret, which lets input methods, dead keys, and AltGr work through the normal Windows text services. It ships as a Native AOT folder and a development MSIX. Windows ends a package's processes when the package is updated or removed, so the packaged app runs its server from a copy in local app data, and sessions survive an upgrade.
All 576 .NET tests pass on Linux, macOS, and Windows x64 and ARM64. The 53 Windows window tests drive real windows and check the composed pixels, and the installed package lifecycle passes on x64 and ARM64. The Mac suite passes in ARM64 and native Intel CI, and graphics qualification measured 60 fps for Kitty and Sixel. Screen readers, input method candidates, physical keyboard layouts, and mixed-scale displays still need hands-on checks on both platforms, and the Windows performance budgets have not been measured yet.