A native macOS (ARM - Apple Silicon + Intel) TN3270/TN3270E terminal emulator for connecting to IBM Mainframes (z/OS, z/VM, z/VSE).
Built entirely in C++ and Objective-C++ on top of native Cocoa, CoreText and OpenSSL.
No license fee. No Java. No X11.
If you work with IBM Mainframes on a Mac, you've probably noticed that every halfway-decent TN3270 terminal client costs money — sometimes a lot of money. We're talking $50–$100+ for software that essentially emulates a 1970s text terminal. One popular commercial option charges a recurring subscription just to type on a green screen. That's absurd.
There are a handful of free alternatives, but they either require Java (slow, ugly, a security nightmare), run inside X11 (no thanks), or are abandonware that hasn't been touched in a decade and breaks on every macOS release.
So I built one from scratch. Native Cocoa. Native CoreText rendering. OpenSSL for TLS. Full TN3270E negotiation including ISPF Query Reply so the menus actually appear. It took a weekend of frustration and a lot of reading ancient IBM manuals — but the result is a clean, fast, free terminal that feels like it belongs on a Mac.
If you work in Mainframe and you're tired of paying for the privilege, this is for you.
ISPF 8.1 Primary Option Menu on z/OS — connected to IBM ZExplore mainframe at 204.90.115.200:623
Version 1.6.0 adds full support for GDDM vector graphics sent by applications running on VM/CMS or z/OS. Charts, diagrams and topology maps are rendered as a CoreGraphics overlay on top of the text screen — no plugin, no browser, no Java.
GOCA View 1/4 — Monthly CPU Utilisation (%). Vertical bars coloured by threshold: green < 60 %, yellow 60–79 %, red ≥ 80 %. Axes, tick marks and value labels all rendered as GOCA vector objects.
GOCA View 3/4 — Throughput vs Response Time scatter plot. Coloured FULLARC dots, blue FILRECT grid lines, and a red trend line. All 25 data points decoded from a single GOCA Write Graphics Object structured field.
Supported GOCA orders: FILRECT · FULLARC · CGPOS / CLCS · SCOL · SMIX · BSEG / ESEG · LNPOS / LNAT.
Coordinate space: AW = 9, AH = 12 GOCA units per character cell; Y = 0 at the top-left of the text area, increasing downward (flipped for Cocoa internally).
IBM 3279 palette colours (0xF1–0xF7) map to the same CoreGraphics colours used for text attributes.
DX3270 ships with the authentic IBM 3270 terminal font by Ricardo Bánffy, bundled directly in the app. It is off by default so the familiar Menlo monospace is used out of the box.
TSO/E Logon screen rendered with the IBM 3270 font — notice the characteristic terminal typeface.
- Open DX3270 → Preferences (⌘,)
- Check "Use IBM 3270 font (by Ricardo Bánffy)"
- All open terminal windows switch instantly — no reconnect needed
The setting is saved and restored on every launch.
| Feature | Details |
|---|---|
| Protocol | TN3270E (RFC 2355) with automatic fallback to classic TN3270 |
| Security | Plain Telnet and implicit TLS (TLS 1.2+) on any port |
| Screen models | Model 2 (24 × 80) · Model 3 (32 × 80) · Model 4 (43 × 80) · Model 5 (27 × 132) · Large custom (62 × 160) — selectable per connection |
| EBCDIC code pages | CP037 (US), CP500 (International), CP1047 (Open Systems) |
| UI | Native Cocoa window, green-on-black phosphor, 600 ms cursor blink |
| Keyboard | PF1–PF24, PA1–PA3, Clear, Reset, Tab/BackTab, ErEOF, Insert, arrows |
| Query Reply | Responds to IBM Structured Field Read Partition Query (required for ISPF); advertises GOCA graphics capability |
| GDDM / GOCA graphics | Full GOCA order-stream decoder: filled rectangles, full arcs (circles), absolute and relative line sequences, character strings at absolute position, set-colour, set-mix, and segment boundaries. Rendered as a CoreGraphics vector overlay on top of the text layer. Coordinate space AW=9/AH=12 units per cell, Y-flipped for Cocoa. IBM 3279 palette (0xF1–0xF7) colours. See GDDM / GOCA Graphics. |
| Rendering | CoreText glyph metrics for pixel-perfect character grid; CoreGraphics vector overlay for GOCA graphics |
| App icon | Native macOS squircle icon — white gradient, bold DX3270 lettering with green terminal cursor, bundled as AppIcon.icns |
| Shortcuts reference | Built-in keyboard shortcuts window — DX3270 → Keyboard Shortcuts… (⌘/) |
| Screenshot | Save the terminal screen as a PNG image (File → Save Screenshot… ⌘⇧P) |
| Text export | Export the screen content as a formatted UTF-8 text file (File → Export as Text… ⌘⇧T) |
| macOS | 12 Monterey and later (Apple Silicon + Intel) |
Pre-built DMG releases are available on the Releases page.
Every push to main automatically builds and publishes two DMGs via GitHub Actions — one for each architecture.
| DMG | For |
|---|---|
DX3270-<version>-build<N>.dmg |
Apple Silicon Macs (M1/M2/M3/M4, 2020 and later) |
DX3270-<version>-build<N>-Intel.dmg |
Intel Macs (2019 and earlier) |
- Download the DMG that matches your Mac
- Open the DMG and drag DX3270.app to your
/Applicationsfolder - On first launch macOS will block the app because it is unsigned — see below
DX3270 is currently unsigned (a Developer ID certificate is planned for a future release once the remaining bugs are ironed out). macOS will refuse to open it directly. There are two ways around this:
Option A — System Settings (GUI)
- Try to open DX3270.app — dismiss the "damaged or can't be opened" alert
- Open System Settings → Privacy & Security
- Scroll down — a banner appears: "DX3270 was blocked because it is not from an identified developer"
- Click Open Anyway
- Confirm in the following dialog — the app launches and macOS remembers the choice
Option B — Terminal (one-time command)
sudo xattr -r -d com.apple.quarantine /Applications/DX3270.appThis strips the quarantine flag that triggers Gatekeeper. After running it, DX3270 opens normally from Finder or the Dock without any further prompts.
- Launch DX3270 — the Connect dialog opens automatically
- Fill in:
Field Example Host 204.90.115.200Port 623(plain) ·992(TLS) ·23(standard Telnet)SSL/TLS check for encrypted connections CA Bundle path to a PEM file if using a private CA (optional) Code Page CP037 (US default) · CP500 · CP1047 - Click Connect
The terminal window opens. Type your credentials at the logon screen. ISPF and TSO sessions are fully supported.
| Key | 3270 Function |
|---|---|
F1–F12 |
PF1–PF12 |
Shift+F1–F12 |
PF13–PF24 |
Option+1/2/3 |
PA1 / PA2 / PA3 |
Return |
Enter (AID) |
Escape |
Reset (unlock keyboard) |
Option+Escape |
Clear screen |
Tab / Shift+Tab |
Next / previous field |
Insert |
Toggle insert mode |
Option+Delete |
Erase to End of Field |
Option+E |
Erase Input (all unprotected fields) |
↑ ↓ ← → |
Cursor movement |
| Shortcut | Action |
|---|---|
⌘N |
New Connection |
⌘, |
Preferences |
⌘⇧P |
Save Screenshot |
⌘⇧T |
Export Screen as Text |
⌘⇧D |
Traffic Monitor |
⌘/ |
Keyboard Shortcuts window |
⌘Q |
Quit DX3270 |
# Xcode Command Line Tools
xcode-select --install
# Homebrew + OpenSSL + CMake
brew install openssl@3 cmakegit clone https://github.com/el-dockerr/X3270.git
cd X3270
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
open build/DX3270.appTo set an explicit build number (useful in CI):
cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_NUMBER=42
cmake --build buildApple Silicon only:
./package.sh
# produces: dist/DX3270-1.5.0-build1.dmgIntel only (cross-compiled from Apple Silicon — see prerequisites below):
./package_intel.sh
# produces: dist/DX3270-1.5.0-build1-Intel.dmgBoth architectures in one step:
BUILD_NUMBER=42 ./package_all.sh
# produces: dist/DX3270-1.5.0-build42.dmg
# dist/DX3270-1.5.0-build42-Intel.dmgApple's Clang toolchain supports cross-compilation natively. The only prerequisite
is an x86_64 OpenSSL library, which lives in the Intel Homebrew at /usr/local.
One-time setup:
# 1. Install Rosetta 2
softwareupdate --install-rosetta --agree-to-license
# 2. Install the x86_64 Homebrew (runs under Rosetta)
arch -x86_64 /bin/bash -c \
"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 3. Install x86_64 OpenSSL
arch -x86_64 /usr/local/bin/brew install openssl@3Then run ./package_intel.sh or ./package_all.sh as shown above.
GDDM / GOCA graphics support
-
GOCA Query Reply — The Structured Field Query Reply now advertises Data Streams support (type
0x84, stream0x02= GOCA), causing GDDM on VM/CMS to send vector-graphics structured fields instead of falling back to text-only mode. -
GraphicsBuffer — A new
GraphicsBufferdata model accumulates decoded GOCA drawing commands (GocaMoveTo,GocaLineTo,GocaArc,GocaFilledRect,GocaSetColor,GocaSetMix,GocaCharString,GocaBeginSegment) as a typed C++17std::variantlist, using the same dirty-flag/callback pattern asScreenBuffer. -
GocaParser — A new stateful FSM (
GocaParser) decodes the GOCA order byte stream inside Write Graphics Object structured fields. Supported orders:SPOS(set position),LNPOS/LNAT(line to absolute / relative),FULLARC(circle),FILRECT(filled rectangle),SCOL(set colour),SMIX(set mix/blend mode),CGPOS/CLCS(character strings at given / current position),BSEG/ESEG(segment boundaries). All unrecognised orders are safely skipped using the GOCA implied/explicit length table. -
DataStreamParser WSF routing —
handleWSF()now routes GOCA-bearing structured fields: SF type0x0D(Begin/Reset graphics) resets the parser;0x0E(Write Graphics Object) feeds the GOCA payload toGocaParser;0x0F(Erase Graphics) clears the buffer. -
CoreGraphics overlay in TerminalView — A new
drawGraphicsOverlay:pass renders theGraphicsBuffercommand list via Core Graphics after the text layer and before the OIA bar. GOCA coordinates (in device units matching the Usable Area QR cell dimensionsAW=9,AH=12) are mapped to Cocoa pixel coordinates with Y-flip. IBM 3279 palette colours reuse the existingcolorFor3270Code()function. EBCDIC character strings are decoded viaEbcdicCodecand rendered with the current terminal font. Mix mode0x04(XOR) maps tokCGBlendModeXOR. -
No PC3270G/GX extensions — IBM PC3270 Graphics Adapter proprietary extensions are out of scope for this release.
App renamed to DX3270
- The app is now called DX3270 (Dockerr's X3270) across every visible surface — menus, window titles, DMG filenames, bundle identifier (
com.dx3270.macos), OIA status bar, and default export filenames — to avoid confusion with the separate open-source x3270 project.
Native app icon
- A purpose-built macOS icon is now bundled as
AppIcon.icns(all 10 iconset sizes, 16 px – 1024 px). Design: Apple squircle shape, white-to-light-indigo gradient background, bold DX3270 lettering in the system SF font with a green terminal-cursor block, and a TN3270 Terminal subtitle in secondary-label gray.
Keyboard Shortcuts window
- DX3270 → Keyboard Shortcuts… (
⌘/) opens a floating, read-only reference window listing every terminal key and application shortcut, organised into four sections: Function Keys, Session Control, Navigation & Editing, and Application.
Screenshot and text export
- Save Screenshot — File → Save Screenshot… (
⌘⇧P) captures the live terminal view as a pixel-perfect PNG image usingNSBitmapImageRepand writes it to a user-chosen file. Useful for documenting session output or sharing screen content. - Export as Text — File → Export as Text… (
⌘⇧T) reads the current screen buffer, decodes every cell from EBCDIC to UTF-8, and saves a fixed-width plain-text file that preserves the exact column layout. Field-attribute positions are written as spaces so column alignment is maintained. - Both actions are only enabled when an active session exists (
validateMenuItem:guard).
Donation link in Connect dialog
- A ♥ Support this project link is now shown in the header of the Connect dialog, opening the Stripe donation page in the default browser. (No ads, no tracking, just a simple way to say thanks if you find the app useful. And since this app is not affiliated with IBM, there are no corporate sponsorships or licensing fees to worry about.)
Multi-model screen support
- Five screen models selectable per connection — Model 2 (24 × 80, default), Model 3 (32 × 80), Model 4 (43 × 80), Model 5 (27 × 132), and a non-standard Large model (62 × 160). The choice is presented as a Screen Model drop-down in the Connect dialog and is saved/restored per host in connection history.
- TN3270E DEVICE-TYPE negotiation — The negotiated terminal type string (
IBM-3278-2-EthroughIBM-3278-5-E) now matches the selected model, so hosts that honor DEVICE-TYPE will configure the session to the correct size automatically. - Dynamic Usable Area Query Reply — The structured-field Query Reply (0x80) now reports the actual grid dimensions and character cell sizes derived from the selected model, replacing the previous hard-coded 24 × 80 values. Hosts such as ISPF use this to determine wrapping and field layout.
- 14-bit buffer addressing — The 62 × 160 Large model requires 9 920 cells, exceeding the 12-bit address limit (4 095).
encodeAddress/decodeAddressnow transparently use 14-bit binary addressing for positions above 4 095, and the Query Reply advertises 14-bit capability accordingly (addrMode = 0x00). - Dynamic screen buffer —
ScreenBufferinternal storage is now astd::vector<Cell>sized to the selected model; the compile-timeSIZE / ROWS / COLSconstants have been removed in favour of instance methodssize(),rows(),cols(). - Adaptive terminal window —
TerminalViewderives its cell grid and preferred window size from the attachedScreenBuffer, so the window grows or shrinks to fit the chosen model on every connection.
IBM 3270 font support
- Bundled IBM 3270 font — The authentic 3270font by Ricardo Bánffy is now shipped inside the app bundle (three variants: Regular, SemiCondensed, Condensed).
- Optional via Preferences — A new checkbox in DX3270 → Preferences ("Use IBM 3270 font") switches the terminal between the default Menlo font and the 3270 font at runtime. The setting persists across app launches.
- Live switching — Changing the preference immediately redraws all open terminal windows and resizes them to match the new cell dimensions.
- Attribution — The 3270font is the work of Ricardo Bánffy and contributors, released under the SIL Open Font License 1.1. See Acknowledgements below.
IBM 3279 color rendering
- Extended attribute support (SA / SFE) —
Set Attribute(0x28) andSet Field Extended(0x29) structured fields are now fully parsed. Foreground color (type 0x42), background color (type 0x45), and highlighting (type 0x41) attributes are stored per cell and carried throughstartField/writeChar. - IBM 3279 colour palette — Seven standard IBM colors rendered correctly: blue (0xF1), red (0xF2), pink (0xF3), green (0xF4), turquoise (0xF5), yellow (0xF6), white (0xF7). Default field color derived from the field attribute Protected/Numeric/MDT bits (green / red / blue / white quadrant).
- Intensified fields — Unprotected-intensified fields now render in red (IBM default) instead of white.
- Reverse video (highlight 0xF2) — Foreground and background swapped at render time.
- Underscore (highlight 0xF4) — 1 px bottom stroke drawn per cell.
- Per-cell background fill — Non-default cell backgrounds are filled before drawing the character.
- Fixed
FA_DISP_LPconstant — Was0x08, corrected to0x04per IBM GA23-0059.
Keyboard / function key fixes
- Fixed PF10 / PF11 / PF12 AID codes — The emulator was sending
0xFA / 0xFB / 0xFCfor these keys. The IBM GA23-0059 standard mandates0x7A / 0x7B / 0x7C. ISPF (and all other 3270 hosts) do not recognise the wrong codes, so F12 (and F10/F11) had no effect. Fixed by replacing the broken0xF0 + narithmetic with the correct IBM lookup table. PF22–PF24 (Shift+F10/11/12) were similarly wrong (0xCA–CC→0x4A–4C). - Added
performKeyEquivalent:override inTerminalView— macOS routes some function-key events through the key-equivalent path (menu shortcut resolution) rather thankeyDown:, silently dropping them. The override mirrors the full PF1–PF24 / Shift+F1–F12 mapping so those events are consumed by the terminal regardless of which path the OS uses.
Cursor and OIA improvements
- Block cursor — Replaced the thin underline cursor with a full block cursor (cell filled with cursor color, character re-drawn in background color). The cursor is now visible at all times including when the keyboard is locked.
- OIA layout — Version string moved to the lower OIA row to avoid overlapping the status and cursor-position indicators.
Connect dialog — connection history
- The Host field is now an editable drop-down combo box. Every successful connection is saved to a history list (up to 20 entries, most recent first, deduplicated by host:port).
- Selecting a previous entry from the list automatically restores the paired Port, SSL/TLS, CA Bundle, and Code Page settings — no need to re-enter them.
- History is persisted in
NSUserDefaultsacross app launches.
ISPF / 3270 data stream fixes
- Fixed: ISPF screen input error code 23 on protected fields —
Read Modifiedresponses were including protected fields that had MDT=1 (host-written output fields). Per IBM GA23-0059,Read Modifiedmust return only unprotected (input) fields; sending protected field data back caused ISPF to reject the input with error code 23. Fix:getModifiedFields()now skips any FA cell with the Protected bit set. - Fixed:
Read Modified Allnow correctly returns all MDT fields —CMD_READ_MODIFIED_ALL(0x0E/0x6E) was handled identically toCMD_READ_MODIFIED, so the protected-field filter was incorrectly applied to host-solicited "all fields" polls as well. Per spec,Read Modified Allmust include both protected and unprotected modified fields. The two commands are now handled separately;buildReadModifiedRecordaccepts anincludeProtectedflag.
Traffic Monitor panel
- New floating Traffic Monitor window (Debug → Traffic Monitor,
⌘⇧D) showing all raw inbound and outbound Telnet/TN3270 bytes as a colour-coded hex dump (TX blue, RX green) with timestamps, byte counts, and a printable ASCII column. - Clear button wipes the log; Save to File… exports the full session as plain text.
- Captures traffic from the moment a connection is initiated so the full negotiation is always visible.
TN3270 / z/VM protocol fixes
- Fixed: z/VM stuck at NVT "PRESS BREAK KEY TO BEGIN SESSION" — The client was proactively sending
WILL BINARY,DO BINARY,WILL EOR,DO EORduring the opening handshake. z/VM responds withDONT BINARY/DONT EOR, which per RFC 854 permanently disables those options for the session. z/VM then committed to NVT mode and never offered 3270 negotiation. Fix: remove proactive BINARY/EOR offers fromconnect(); let the server drive binary/EOR negotiation after the terminal-type exchange. - Fixed: Duplicate
WILL TN3270Econfusing TN3270E-capable servers — When the server confirmed our initialWILL TN3270Eby echoingDO TN3270E, the response handler was sending a secondWILL TN3270E, causing some servers to reject TN3270E entirely. Fix: addedsentWillTN3270E_/sentDoTN3270E_guards (same pattern as the existingsentWillBinary_guards).
Other fixes
WILL TN3270Eserver offer not handled → fixedenterDataMode()guard was too strict (requiredwillBinary_/willEOR_) → fixed- Write command reset buffer address to 0 → fixed
FUNCTIONS REJECTfrom server not handled → fixed- Keyboard locked permanently after a failed AID send (
SendRecordCallbacknow returnsbool) → fixed - Keyboard started unlocked instead of locked-while-connecting → fixed (
LockReason::Connectinginitial state) DEVICE-TYPE REJECTdid not callenterDataMode()→ fixed- Duplicate
WILL/DOforBINARY/EORduring re-negotiation → fixed (sentXxx_flags) TERMINAL-TYPE SENDsub-negotiation did not setdoTermType_→ fixed- Query Reply was missing Colour and Highlighting structured fields (required for ISPF menus) → fixed
Initial public release — basic TN3270E support, TLS support, ISPF Query Reply support, CoreText rendering, keyboard input, and a simple Connect dialog.
IBM 3270 Terminal Font
The optional terminal font bundled with DX3270 is 3270font, designed and maintained by Ricardo Bánffy and contributors.
It is derived from the classic x3270 bitmap font, redrawn as a modern vector typeface in OTF/TTF format.
The font is distributed under the SIL Open Font License, Version 1.1 — see LICENSE.txt in the upstream repository.
Many thanks to Ricardo and all contributors to that project for their meticulous work keeping this piece of mainframe history alive.
DX3270 for macOS is released under the MIT License.
See LICENSE for the full text.
Written by Swen Kalski, 2026.
IBM, z/OS, ISPF, and 3270 are trademarks of IBM Corporation.
This project is not affiliated with or endorsed by IBM.



