Put fold chevrons in the gutter, fading in while the pointer is over
it, a guide down each open scope, and a placeholder after a folded one.
Number lines past folded ones, and put a swatch before each colour the
server reports.
Outline decoded fields over the hex view, list them in a tree beside
it, and place types from its context menu. Read more of an insight's
memory when a decode runs out.
Draw the bytes once, a glyph run per colour per row, and show the
selection as themed bands beneath them, so a drag repaints only what
changed. Lay the rows out on a character grid, as the SwiftUI view
does.
The emulator's control channel was the only thing pulling grpc-swift
in, and with it swift-nio-ssl, which does not build on Windows at all
-- it has no branch for the platform and its PosixPort reaches for
mlock, lstat and readlink. Nothing here wants TLS: the endpoint is
plaintext on the loopback, and only seven calls are ever made.
So the wire is spoken directly. GRPCConnection opens one HTTP/2
stream per call over swift-nio-http2 and frames length-prefixed
protobuf into it; EmulatorControlClient names the seven. That drops
grpc-swift-2, grpc-swift-nio-transport, grpc-swift-protobuf and
swift-nio-ssl from the graph, along with 7840 lines of generated
service stubs. The generated messages stay.
The rest is what running it on Windows turned up:
- AVD discovery split on "\n" and trimmed only .whitespaces, so
every name kept a trailing \r and every AVD was dropped.
- An AVD config spells its paths with backslashes, which URL keeps
as ordinary characters rather than separators.
- emulator and adb want .exe, and the SDK sits under LOCALAPPDATA.
- Port reservation and process lookup move into HostPort and
HostProcessLookup, which QemuMachine's two copies also wanted.
- The launcher re-execs into QEMU there, so the discovery ini is
named after that process; the one naming this launch's debug port
is taken when the launcher's own is absent.
Frames still arrive through a mapping rather than the socket:
CreateFileMapping backs the same file:// handle the emulator is
handed elsewhere.
A guest without vsock is refused outright on Windows, where the
virtio-serial fallback would ask QEMU for a QMP UNIX socket and be
told "unix sockets are not available on windows".
LUMA_EMULATOR_DISPLAY_ONLY drives launch, control and streaming
without a guest agent. Against a Pixel_2 AVD it reports a 1080x1920
first frame and nine more in three seconds.
Observe the shared connection activity in the target picker and show
the current stage under the process and application spinners, animating
the bar toward the next stage over its expected duration with a timed
animation. Show the stage on an attaching session's row too.
The webview shims went with the native editors; the libraries and
packages they pulled in stayed. Let go of the WebView2 loader and
its COM and WinRT companions, the vcpkg port, and webkitgtk in the
AppImage.
Open through the r2 command so what it opens is mapped: the
disassembler skips whatever falls outside a map, and reading is not
enough. Drop the bin load, which only sniffed formats at address zero.
Let a failed read say so instead of returning null, remember the pages
that have none, and resolve an anchor live or not at all -- a stale
address lands on whatever occupies it now.
Put the drawing area in an aspect frame fed by the guest's size, and
let each host pick between following the container and asking for the
guest's resolution. Grab focus from a capture-phase gesture, and keep
the release hint up while the pointer is grabbed.
Build the rows once and let GtkListBox filter and header them,
invalidating the filter on each keystroke. Drop the debounce, the
row-index bookkeeping and the 250-row cap.
Needs the closure-based bindings in SwiftGtk.
Reconciling group children rebuilds every row but inserts none when
the keys are unchanged, so the stored Browse action held an anchor
that was never in the widget tree, and popping up the popover
crashed inside gtk_widget_realize().
Take the anchor as a parameter and look up the row that is in the
list at activation time.
Drop the Monaco webview. Edit in a GtkSourceView on GTK and in a
native text view on Apple, both speaking to Frida.Compiler's
in-process language server for completions, signature help,
hovers and diagnostics.
Color, complete and indent code natively, matching the GitHub
theme in light and dark.
Map the shifted row so keys like ':' reach the guest, and hold
shift around a stroke that needs it. Send a character on its own
key-down, tapping down and up, rather than tracking its phases.
Read the version stamped into a fetched agent's note, and list the
last releases from GitHub to learn what each flavor has published.
Offer the latest to download, name it on the button, and show an
update once a newer one lands. Drop the pinned version constant.
A machine named after its template showed no subheading at all, and
with one Linux template the architecture is half the story. Both
panels now say template and architecture, whenever that adds to the
name.
Hitting Download gave nothing back until the file was there. Stream
the starter images and the agent through a delegate that reports how
much has arrived, carry the fraction in the downloading state, and
show the percentage in both boot dialogs as it moves.
The starter files and the agent follow the chosen architecture, yet
the choice sat below them. Put it with the machine's identity, right
after its name.
Booting closed the dialog at once; the SwiftUI sheet stays, shows the
guest's screen, and offers to mark the state ready to come back to.
Do the same: swap the form for the screen, Cancel becomes Later, and
Boot becomes Mark Ready.
Move to add(action:) where insert(action:) was deprecated, read a GL
log through the length GL reports instead of trusting a terminator,
let a QMP execute be fire-and-forget, and discard the results that
were never wanted.
The boot dialog took only the parameters, so a guest wanting a
barebone agent gave no way to choose or download one, and the starter
kit was out of reach. Add both rows, resolved from the chosen
architecture and refreshed when it changes, the way the SwiftUI sheet
has them.
Eleven dialogs kept a static retained map to hold their controller
while shown, each removing itself on close, and forgetting the dance
left a dialog whose weak closures were already gone. Move the one
strong reference into the dialog's own GObject data table with
own(_:): set_data_full holds the controller, and closing drops the
association, whose destroy notify releases it. The release connects
after, so a caller's own closed handler still finds its controller.
The Adw.Window-based views keep their maps; their lifetime ends with
close-request rather than closed.
Nothing held the controller once present() returned, so every weak
closure in it went nil and the buttons did nothing. Retain it the way
the other dialogs do, letting go when the dialog closes.
A Windows 9x guest is left with the PC's own mouse, which only hears
how far the pointer moved, and the screen sent absolute coordinates
that never reached it. Clicking such a guest now captures the pointer:
the cursor is hidden, motion is sent as deltas in the guest's own
pixels, and the host's pointer is put back on its anchor after every
move so the two do not drift apart. Ctrl+Alt lets go, as does losing
focus, and a hint on the screen says both.
Placing the pointer goes through CLuma: SetCursorPos on Windows, a
CoreGraphics warp on macOS, XWarpPointer under X11. Wayland keeps the
pointer's whereabouts to itself, so there the deltas flow unwarped
until the pointer leaves the widget.
The device list's button becomes a menu: add a remote device, or boot
a guest. The boot action is disabled where no backend offers a
template, and says why.
The dialog is preference rows, so a template's parameters arrive as
the kind of control each one is, and rebuild when the template
changes.
setSidePanel defers the write, so reading the state straight back
rendered the value before it, and the panel lagged a click behind.
Show the panel that was asked for.
The panel the SwiftUI frontend has, in the slot SidePanel named.
A frame is the guest's own framebuffer mapped read-only, and the Cairo
surface wraps those same bytes, so nothing is copied on the way to the
screen. Rows refresh in place: a screen rebuilt underneath the pointer
loses the keyboard mid-keystroke.
Add a machine abstraction to LumaCore, with QEMU as its first
backend: templates mirroring frida-core's test guests, a QMP client,
and a display that maps QEMU's surface over D-Bus rather than
copying frames.
Leave readiness to the user: drive the guest, mark it ready, and come
back to that snapshot later. Keep the machine in the project, so
booting it again resumes where it was marked. Hand the stub to
addBareboneDevice, so it joins the picker beside the other devices.
Show the display while booting, and afterwards in a Machines panel,
which the side panel now models as an enum to share its slot.
A comment is a last resort, and these were not one: section banners,
doc headers naming what the declaration below already names, and
notes saying in prose what the next line says in code.
What is left is what the code cannot show -- an ordering that matters,
a platform quirk, why the obvious way fails.
No distribution packages one, so SwiftyPharo asks pkg-config and
the Makefile builds a VM into .build when nothing answers. CI keeps
it in a prefix of its own and finds it again at run time.
The Meson-built VM replaces the CMake one, which brings the plugin
and boot fixes with it.
The playground, the notebook cells, the source editor with its
marks, the inspector columns and the canvas are written once for
both Apple platforms, with a file naming what the two frameworks
call differently. The editor is where they really part company.
The engine owns the image and its start-up, so a mission reaches
the whole of Pharo.
The example catalogue is Smalltalk inside Swift string literals,
which no compiler reads: parse each one in the image and report the
syntax errors, the names nothing defines and the selectors nothing
implements. Play every patch offline. Draw the effects and a
lettered scene offscreen and write a PNG of each, since what goes
wrong in drawing is blank frames and mirrored glyphs.
Play a snippet in a canvas for a while and report what the process
holds as it goes, and let go of what the image lets go of.