X3270: The Incredible Community Response and a Massive Feature Drop
When I first dropped X3270—my bare-metal, native macOS terminal emulator for IBM Mainframes—I honestly expected it to just be a niche tool for my own sanity. I was just tired of the corporate bloat, the Java runtimes, and the absurd subscription fees required to emulate a 1970s green-screen.
The response on LinkedIn over the past few days has been absolutely staggering.
Seeing so many of you—mainframe veterans, C++ hackers, and systems engineers—validate the idea that we shouldn't have to pay rent to do our jobs has been incredibly motivating. Your feedback, bug reports, and shared frustration with modern software bloat fueled an intense week of coding.
I’ve just pushed versions 1.0.2 through 1.4.0 in rapid succession. Here is a deep dive into the massive new features that make X3270 not just a free alternative, but a strictly better terminal for macOS.
Breaking the Grid: Multi-Model Screens & 14-bit Addressing (v1.3.0)
By default, the 3270 world lives in a rigid 24 × 80 box (Model 2). But real enterprise work often demands more space.
X3270 now fully supports dynamic multi-model screens. You can select Model 2, 3, 4, 5, or a non-standard "Large" model (62 × 160) directly from the Connect dialog. The terminal window is fully adaptive—it derives its preferred macOS window size directly from the internal std::vector
Getting this to work with the mainframe wasn't just a UI trick; it required deep protocol negotiation:
TN3270E DEVICE-TYPE: The emulator now dynamically advertises the correct string (e.g., IBM-3278-5-E), allowing hosts to configure the session automatically.
Dynamic Query Reply (0x80): When ISPF queries the terminal, we now report the actual dynamic grid dimensions instead of hard-coded values, ensuring perfect wrapping and field layouts.
14-bit Buffer Addressing: This was a fun one. The standard 3270 data stream uses 12-bit addressing, capping out at 4,095 cells. The new 62 × 160 Large model requires 9,920 cells. I completely rewrote encodeAddress and decodeAddress to transparently switch to 14-bit binary addressing for positions above 4,095, and we now explicitly advertise 14-bit capability to the host.
Bringing the Metal to Life: IBM Fonts & 3279 Colors (v1.1.0 & v1.2.0)
A terminal emulator is only as good as its rendering engine. I wanted X3270 to look and feel exactly like the authentic hardware.
First, I bundled the legendary 3270font by Ricardo Bánffy (huge thanks to him and the contributors; released under the SIL Open Font License). You can toggle it live in the Preferences, and CoreText instantly redraws all open windows and resizes the grid.
Second, I built out full IBM 3279 color rendering. X3270 now fully parses the Set Attribute (0x28) and Set Field Extended (0x29) structured fields.
We render the 7 standard IBM colors perfectly (blue, red, pink, green, turquoise, yellow, white).
Unprotected-intensified fields now correctly render in IBM default red.
Reverse video (Foreground and background swapped at render time) and Underscore highlights are fully supported.
In the Protocol Trenches: GA23-0059 and ISPF Fixes
When you build a terminal emulator from scratch, you quickly realize how unforgiving the mainframe is. IBM's GA23-0059 data stream reference became my bedtime reading this week.
The F12 / AID Code Bug: A few of you noticed F10, F11, and F12 weren't working in ISPF. I had used a simple 0xF0 + n arithmetic to generate the AID codes, resulting in 0xFA for F10. Nope. The IBM standard strictly mandates 0x7A, 0x7B, and 0x7C. Fixed.
ISPF Error 23: If you hit enter and got an "input error code 23", it was because my Read Modified response was returning data for protected fields if the host had set the Modified Data Tag (MDT=1). The spec demands we only return unprotected fields for that specific command.
The z/VM Handshake: z/VM was getting stuck at the NVT "PRESS BREAK KEY" prompt because the client was proactively offering WILL BINARY. z/VM rejected it permanently per RFC 854. The client now politely waits for the server to drive binary/EOR negotiation after the terminal-type exchange.
To make debugging these ancient protocols easier, I also added a Traffic Monitor (⌘⇧D). It’s a floating window that gives you a live, color-coded hex dump of all raw inbound/outbound Telnet traffic, complete with a printable ASCII column. If the handshake fails, you can now see exactly why.
Quality of Life: Screenshots, Text Export, and History (v1.4.0)
Because this is a native macOS app, we can leverage Cocoa to do things that X11 wrappers simply can't.
Save Screenshot (⌘⇧P): Captures the live terminal view as a pixel-perfect PNG image using NSBitmapImageRep and writes it to disk. No more messy OS screen-clippings.
Export as Text (⌘⇧T): This is my favorite addition. It reads the current screen buffer, decodes every cell from EBCDIC to UTF-8, and saves a fixed-width plain-text file. It replaces field-attribute positions with spaces, meaning your exact column alignment is preserved perfectly.
Connection History: The Host field is now a drop-down combo box. It automatically saves your last 20 successful connections and restores your exact Port, SSL/TLS, CA Bundle, and Code Page settings when selected.
Supporting the Independence
Because X3270 is independent, completely unaffiliated with IBM, and heavily anti-subscription, a few folks on LinkedIn asked how they could support the server costs and development time.
I’ve added a simple "♥ Support this project" link in the Connect dialog that opens a Stripe donation page.
There are no ads, no tracking, and no corporate sponsorships to worry about. If the app saves you $100 a year on a commercial license and you want to buy me a coffee, you can. If you just want to use it for free forever, that is exactly what it is there for.
The latest DMG is live on GitHub. Go pull it, connect to your partition, and let me know what you want to see in v1.5.0!
Keep the mainframe spirit alive.