feat: Add Windows support via ConPTY#6
Conversation
| functional terminal built on the libghostty C API in a | ||
| [single C file](https://github.com/ghostty-org/ghostling/blob/main/main.c). | ||
|
|
||
| The example uses Raylib for windowing and rendering. It is single-threaded |
There was a problem hiding this comment.
Removed the threading detail because with windows technically this is an incorrect statement since we have two threads. Happy to revert as needed
|
Lets wait for ghostty-org/ghostty#11756 and then I'll ask if you can rebase this and clean it up, since I think that'll maybe improve some of this. |
# What PR #11756 added IMPORTED_IMPLIB pointing to the .lib import library, but the import library is not listed in the OUTPUT directive of the `add_custom_command` that runs zig build. The file is produced as a side-effect of the build. This works with the Visual Studio generator (which is lenient about undeclared outputs) but fails with Ninja: ninja: error: 'zig-out/lib/ghostty-vt.lib', needed by 'ghostling', missing and no known rule to make it The fix adds "${ZIG_OUT_DIR}/lib/${GHOSTTY_VT_IMPLIB}" to the OUTPUT list. No behavioral change. The file was already being built, Ninja just needs to know about it. ## but_why.gif I am cleaning up ghostty-org/ghostling#6 and I realise that in order to get rid of the CMake workarounds we had before #11756, this change is necessary. # POC I set up a branch pointing at my fork with a POC and it builds, this is the cleaned up CMakeList https://github.com/deblasis/winghostling/blob/test/cmake-implib-no-workaround/CMakeLists.txt
39ccb3a to
224bcc4
Compare
|
The #ifdef _WIN32 blocks in main() could be consolidated behind a unified Pty struct with platform-specific implementations (eg: pty_drain(), pty_reap(), pty_resize()), eliminating all conditionals from main(). I kept inline #ifdef blocks to match the existing style in the codebase, but happy to refactor if you'd prefer that approach. Anything else, fire away. |
mitchellh
left a comment
There was a problem hiding this comment.
Not bad. I agree the ifdefs are pretty ugly. I'll have to think about it. I put a couple simple requests otherwise.
| @@ -1,6 +1,55 @@ | |||
| // --------------------------------------------------------------------------- | |||
| // Platform-independent headers | |||
| // --------------------------------------------------------------------------- | |||
There was a problem hiding this comment.
We don't need this at the top.
| int main(void) | ||
| int main(int argc, char *argv[]) | ||
| { | ||
| // Parse --shell <path> flag for explicit shell selection. |
There was a problem hiding this comment.
let's just change this to argv[1] (no parsing).
Awesome! Done and done and tested. |
|
I gave these changes a go, and I have some observations:
|
Add a build-windows job that runs on windows-latest with Zig + Ninja. Set WIN32_EXECUTABLE TRUE so the app launches as a GUI subsystem process without spawning an extra console window. MSVC/Clang need an explicit /ENTRY:mainCRTStartup override; MinGW handles this automatically. Signed-off-by: Alessandro De Blasis <alex@deblasis.net>
Add a complete Windows backend using the ConPTY (pseudo-console) API, gated by #ifdef _WIN32 alongside the existing Unix pty code. Platform abstraction: - PtyHandle typedef (HANDLE on Windows, int on Unix) so pty_write, handle_input, handle_mouse, etc. share signatures across platforms - WIN32_LEAN_AND_MEAN + NOGDI + NOUSER to avoid symbol clashes with raylib (Rectangle, CloseWindow, ShowCursor, PlaySound) ConPTY implementation: - pty_spawn_win32(): creates pipes, pseudo-console, attribute list, and launches the child shell (pwsh > powershell > cmd fallback) - Threaded pipe reader to work around PeekNamedPipe unreliability - pty_buf_drain() called each frame from the main loop - pty_resize() delegates to ResizePseudoConsole - pty_cleanup() with 1s graceful timeout before TerminateProcess Integration: - AttachConsole(ATTACH_PARENT_PROCESS) + CONOUT$ redirect so logs are visible when launched from a terminal (no-op from Explorer) - WaitForSingleObject() for child exit detection (ConPTY pipes don't reliably close on child exit) - Shell detection via SearchPathW, --shell flag support - TERM=xterm-256color set via SetEnvironmentVariableA - placement_iter freed on both platforms Co-authored-by: Mitchell Hashimoto <m@mitchellh.com> Signed-off-by: Alessandro De Blasis <alex@deblasis.net>
37a33db to
616c675
Compare
|
Hi @res2k, thank you for taking the time to look into this.
You are right, removed.
Without WIN32_EXECUTABLE TRUE, launching from Explorer (clicking the icon) opens an extra console window alongside the raylib window. The child shell inherits the parent's console subsystem and opens its own. The STARTF_USESTDHANDLES with NULL handles prevents I/O from going to the wrong place, but it doesn't prevent the console window itself from appearing.
Launching from the terminal instead just swallows the output. I made a change that perhaps hits two birds with one stone (without harming animals). Now launching from terminal gets the output from the caller, same behaviour as linux/mac where there's no subsystem and logs go to the parent terminal.
Good catch, addressed. The ConPTY pipe doesn't close on child exit, so we now poll the process handle with WaitForSingleObject() each frame to detect exit independently. |
Alright, that's a sensible approach. |

Adds Windows support using the ConPTY API for terminal emulation.
All behind #ifdef _WIN32 in main.c, Unix codepath unchanged.
Tested on Win, Mac and Linux