I invite you to upgrade to a paid subscription. Paid subscribers have told me they appreciate me creating the programming projects and would like to see more of them in the future. Hi, this is John with this week’s Coding Challenge. 🙏 Thank you for being a subscriber, I’m honoured to have you as a reader. 🎉 If there is a Coding Challenge you’d like to see, please let me know by replying to this email📧 Coding Challenge #127 - TmuxThis challenge is to build your own version of tmux, the terminal multiplexer. If you spend any time working in a terminal, sooner or later you’ll meet tmux. It lets you run multiple terminal sessions inside a single window, split that window into panes, and, best of all, detach from a session and come back to it later with everything still running. It’s the tool that saves your work when your SSH connection drops. Under the hood it brings together some fascinating systems programming: pseudo-terminals, client-server architecture over Unix domain sockets, raw terminal input handling, and screen rendering. Building a simplified version yourself is one of the best ways I know to really understand how a terminal works. The Challenge - Building Your Own TmuxIn this challenge you’re going to build a simplified terminal multiplexer. Your tool will let you create named sessions that keep running in the background, detach from them and reattach later, open multiple windows within a session, and split windows into panes, each running its own shell. This is an advanced challenge. The individual steps are manageable, but you’ll be working with pseudo-terminals, raw terminal modes, Unix domain sockets, and screen rendering, so it helps to be comfortable with systems programming. The concepts are the same in any language, so pick one you know well that gives you good access to operating system APIs. This challenge is best tackled on Linux, macOS, or another Unix-like platform, since that is where pseudo-terminals and Unix domain sockets live. Step ZeroIn this introductory step you’re going to set your environment up ready to begin developing and testing your solution. Choose your target platform and programming language. You’ll need access to pseudo-terminal (PTY) APIs, Unix domain sockets, and the ability to put the terminal into raw mode, so check your chosen language has libraries for these before you commit. If you haven’t used tmux before, install it and spend some time with it. Create a session, detach with It’s also worth reading up on how pseudo-terminals work ( Step 1In this step your goal is to allocate a pseudo-terminal, spawn a shell inside it, and connect it to your own terminal. This is the core building block of a terminal multiplexer. Your program should create a PTY, launch the user’s shell as a child process attached to it, put your own terminal into raw mode, and then shuttle data in both directions: keystrokes from your terminal go to the shell, and the shell’s output is displayed on your screen. When the shell exits, your program should restore the terminal to its original state and exit cleanly. Testing: Run your program:
You should see a shell prompt. Try running some commands, including full-screen programs:
Everything should behave exactly as it would in a normal terminal. Type Step 2In this step your goal is to split your program into a server and a client that communicate over a Unix domain socket. Real When your client starts, it should check whether a server is already running. If not, it should start one in the background. The two should communicate over a Unix domain socket, and you’ll need a simple protocol for passing keyboard input in one direction and screen output in the other. Testing: Run your client:
You should get a working shell, just like in Step 1. In another terminal, verify the socket exists (for example with Step 3In this step your goal is to support named sessions. A session is a collection of shells managed by the server under a single name. Your tool should support creating a new named session:
The server should be able to hold several sessions at once, each with its own shell. If a user tries to create a session with a name that already exists, they should get a clear error message. Testing: Create a session and verify you get a shell:
Run a command like
You should see an error telling you the session already exists. Step 4In this step your goal is to implement the prefix key and detaching from a session. Tmux is driven by a prefix key, Make the prefix key configurable so users can choose something other than Testing: Create a session and start something long-running in it:
Inside the session run:
Press Step 5In this step your goal is to support reattaching to a running session. Detaching is only half the magic. Your tool should let the user reconnect to a session that’s running in the background:
On attaching, the user should see the session’s current screen content, including output that was produced while they were detached, and be able to carry on working as if they never left. Attaching to a session that doesn’t exist should produce a clear error. Testing: Create a session, run
You should see the screen as you left it, including
You should get an error message, not a hang or a crash. Step 6In this step your goal is to support listing sessions, killing sessions, and cleaning up the server. Your tool should report the sessions the server is currently managing:
The output should show each session’s name and some useful detail, such as how many windows it has and whether a client is attached, similar to the real tmux output. It should also support killing a session:
Killing a session should terminate the shells running inside it. When the last session is closed, whether by Testing: Create two sessions,
You should see both sessions listed. Kill one:
Only Step 7In this step your goal is to add a status bar. Tmux reserves the bottom line of the terminal for a status bar. Yours should show the session name on the left, the list of windows in the middle (just one window for now), and the current time on the right. The status bar should update as things change, including ticking over as the time changes, and the shell should now render in the remaining lines above it without ever overwriting the bar. Testing: Attach to a session. You should see the status bar on the bottom line showing the session name and the time. Run a command that produces lots of output, such as:
The output should scroll in the area above the status bar and the bar should stay intact. Watch for a minute and confirm the clock updates. Step 8In this step your goal is to support multiple windows within a session. A window is like a tab: each window has its own shell, but only one window is visible at a time. Pressing the prefix key followed by Testing: Attach to a session and run Step 9In this step your goal is to support switching between windows. With multiple windows created, the user needs to move between them:
When switching to a window, its screen content should be restored exactly as it was, and the status bar should update to show the newly active window. Testing: Using the session from Step 8, press Step 10In this step your goal is to split a window into two panes, one above the other, with the prefix key followed by This is where your multiplexer really starts to earn its name. Pressing Draw a border between the panes using box-drawing characters (such as Testing: Attach to a session and press
The output should appear only in that pane. Start a long-running command in one pane, for example Step 11In this step your goal is to split a window into two panes side by side with the prefix key followed by Pressing Testing: In a fresh window press Step 12In this step your goal is to support navigating between panes with the prefix key followed by the arrow keys. Pressing Testing: Build a layout with at least three panes. Use Step 13In this step your goal is to support resizing panes with the prefix key followed by Pressing the prefix followed by Be warned if you’re working on a Mac Ctrl+Arrow gets captured by the OS, you’ll want to add support for the Alt key just like tmux itself does. Testing: Split a window with Step 14In this step your goal is to handle the terminal itself being resized. When the user resizes their terminal emulator window, your client should notice, tell the server, and the server should redistribute the space: recalculating the pane layout, informing every shell of its new size, redrawing all borders, and keeping the status bar on the bottom line. Testing: Attach to a session with several panes and resize your terminal window by dragging its corner. The layout should adapt smoothly: panes share the new space, borders are redrawn correctly, and the status bar stays on the bottom row. Run Step 15In this step your goal is to add a command prompt. Pressing the prefix key followed by Testing: Press Step 16In this step your goal is to add copy mode, so users can scroll back through a pane’s output history. Each pane should keep a scrollback buffer of output that has moved off the top of the screen. Pressing the prefix key followed by Testing: In a pane, generate more output than fits on the screen:
Press Going FurtherOnce you have the core multiplexer working, here are some ideas to take it further:
Share Your Solutions!If you think your solution is an example other developers can learn from please share it, put it on GitHub, GitLab or elsewhere. Then let me know via Bluesky or LinkedIn or just post about it there and tag me. Alternately please add a link to it in the Coding Challenges Shared Solutions Github repo Request for FeedbackI’m writing these challenges to help you develop your skills as a software engineer based on how I’ve approached my own personal learning and development. What works for me, might not be the best way for you - so if you have suggestions for how I can make these challenges more useful to you and others, please get in touch and let me know. All feedback is greatly appreciated. You can reach me on Bluesky, LinkedIn or through SubStack Thanks and happy coding! John You're currently a free subscriber to Coding Challenges. For the full experience, upgrade your subscription.
|

Comments
Post a Comment