0. Introduction
First of all: Big thanks fly out to Simon Tatham, the original developer of [puTTY], for the significant time he has invested to help me fix and refine this hack, and to teach me about how and how not to debug a program when things go wrong. If it weren’t for his help, this modification wouldn’t have worked out in a fashion that would make the program redistributable in working order. So, again: Thank you, Simon! 
In recent years, I’ve come to like and make use of [desktop tiling], which some people mostly in the UNIX and Linux worlds like to use for productive work.
And what is this for? Simple: To build a (virtual) desktop tuned to suit a specific workflow without distractions. Put a few connected programs in properly sized, pre-defined tiles, side-by-side. No moving or resizing windows getting in your way, no waste of screen real estate due to title bars*, scroll bars* or window borders*, just focus on content and the work to be done. Optionally, run it in hybrid mode, e.g. to stack several windows on one tile if they’re not needed simultaneously.
*Note: This is much harder on MS Windows than it is with your typical window manager on common UNIX or Linux systems.
On my aging XP x64 workstation, I’m solving this with several 3rd party tools. For reference: Window snapping is done by ntwind [WindowSpace] (commercial software), and desktop restore to the tiled setup is done with Stefan Didak’s [WiLMa] – the Windows Layout Manager, whereas virtual desktops are being done by [VirtualDimension]. All of this software still works all the way up to Windows 10 20H2, the most current version at the time of writing; So it’s not limited to good old XP x64.
There’s just one thing that bugged me though: I use multiple [puTTY] SSH terminals to connect to my UNIX box, but quite a few pixels were being wasted by GUI decorations I simply didn’t need: Title bars and scroll bars. Now there are fancy little tools like [NirSoft WinExplorer] that can rip them out of running programs’ windows with Win32 API calls, but this doesn’t always work. Some programs like to insist on keeping their title & scroll bars, because they’re programmed that way.
puTTY is one of them. Luckily though, puTTY is free and open source software, and while I can’t dare call myself a programmer, I still took a look at the code, and managed to modify it accordingly, at least after asking Simon for help and after his advising me thoroughly. 
1. The result
First, before giving you any files or showing you the modifications in the code, let’s show you what it looks like when in use so you can see whether this is for you. First, a screenshot of puTTY in use in its unmodified form, see the eight terminals on the left side:

Terminal desktop to my Threadripper UNIX machine (the “BEAST”) from XP x64 before having modified puTTY (click to enlarge)
As you can see, it’s pretty tiled alright. The largest tile is a stacked one here, shared by Notepad++, the browser and the FileZilla FTP/SFTP client, with the web browser currently sitting on top. So I’m running this in “hybrid” mode. I thought about removing the task bar, but decided to keep it after all. It’s still needed for a few things.
Now, here’s what it looks like after hacking up puTTY and modifying a few more windows using WinExplorer:

The same desktop after having modified puTTY and having tweaked a few more windows (click to enlarge)
As you can see, the puTTY terminals are specifically clean and save as many pixels as possible. The window border cannot be removed (WS_THICKFRAME window style), because if you do that, the window loses resizing capabilities. Also, X-Chat still has its bars, because that’s another tool that would need its code altered to permanently do away with them. X-Chat or its successor HexChat is notoriously hard to compile on MS Windows though, due to its dependency tree. So it’s the only window left unchanged.
This also works pretty well together with the new MS [FancyZones PowerToy] for Windows 10, and on top of that, WS_THICKFRAME is far more slim on Win10, making things look even more clean, see here:

Windows 10 “FancyZones” with the pixel-saving puTTY (Click to enlarge)
You can say a lot of things about Windows 10 – and I’m usually quite loud when it comes to complaining about it – but it’s not all bad. WS_THICKFRAME has been thinned out to a width of just 1 pixel, and the new FancyZones thingy means serious competition for what we’re used to on UNIX and Linux, at least in my opinion. And that’s especially true since Microsoft has released it as free and open source software under the [MIT license]. Here we see this modern, more clean look working together with a trimmed puTTY.
Only thing is: In WS_POPUP style, Windows 10 draws a small white border on top of every window for seemingly no good reason. We would need to extend the client area into the non-client area of the window to fix this. That’s not so easy, so I left it that way.
When I began using tiling, I think I originally did so on Linux, and shortly thereafter tried to turn my old XP x64 into something more UNIXish as well. Windows 10 was added to the mix just now, after the release of FancyZones, just for playing around. Just for reference, this is what my old Linux desktop looks like, working as a similar terminal desktop for the UNIX box:

My old Linux desktop was the role model (click to enlarge)
It’s not a true tiled desktop either, just an old Gnome 2 with Metacity, tuned by [devilspie(1)], which can remove window decorations and is configured by [symbolic expressions] in config files. Scary stuff, but it works. Maybe I’ll describe the setup in another post.
In case you’re wondering what the empty area on the Zabbix website is for: More S.M.A.R.T. graphs will be added here as soon as my NVMe SSD RAID-0 will be added to the machine early next year. Host read/writes, SSD temperatures, that kind of thing. That’s for another post though.
2. The files
2a. Downloads
Before showing you how to modify puTTY to save more space, I’ll give you the files to try it out for yourself, should you be interested:
Please note that the mod also affects the puTTY telnet client puttytel.exe, because it shares the same frontend code with the SSH client.
I’ve used Microsoft VisualStudio 2017 to compile the code, because when using the VS versions for the project files the code comes with (2010, 2012), the build fails. I didn’t really look into this, but I assume it’s due to some C99 code being in there now.
2b. Details about how to actually use this
The puTTY configuration window that you’ll see after launch is unaffected by my changes and features normal window decorations, see here:

puTTY configuration window
That’s just a sample. The real one has one saved profile for each terminal, so I can tile them by window titles. Anyway, it’s really only the terminal window that’s modified to save pixels.
3. How to modify puTTY and remove title & scroll bars
What follows now is a list of source code modifications necessary to reliably remove title & scroll bars from puTTY. Also, we’ll re-introduce Windows XP compatibility while we’re at it. All paths shown from here on out are relative to the root of the unpacked puTTY source code, so they’re subfolders of the putty-src\ folder you get after unpacking the source.
3a. A simple issue
Open windows\version.rc2 in your favorite text editor or within VS2017, and look for two include lines near the top:
#include "version.h"
#include "licence.h"
At least in my case, the compiler failed trying to locate those headers, as they’re one level above version.rc2. I guess some include directories are missing, but we’ll do it the dirty way. Simply change those two lines so they look like this:
#include "..\version.h"
#include "..\licence.h"
Save the file.
3b. Windows XP & XP x64 platform compatibility
Open windows\winmisc.c and find this code block:
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
#if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY
/* For older Visual Studio, and also for the system I
* currently use for Coveritying the Windows code, this
* function isn't available in the header files to
* type-check */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
kernel32_module, SetDefaultDllDirectories);
#else
GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
#endif
}
Unfortunately, this preprocessor directive checks only for the Visual Studio / compiler version. I have “1911” or something, so the legacy function GET_WINDOWS_FUNCTION_NO_TYPECHECK() is omitted and instead, GET_WINDOWS_FUNCTION() is being called. This does not work when targeting XP or XP x64!
I could probably do this intelligently by checking the actual platform toolset in use (e.g. vs141_xp instead of vs141), but instead I decided to just hard-code things, always calling GET_WINDOWS_FUNCTION_NO_TYPECHECK(). Change the above code to this:
if (!kernel32_module) {
kernel32_module = load_system32_dll("kernel32.dll");
/*
* Modified by M. Lackner / Thrawn to disable the call to
* GET_WINDOWS_FUNCTION(), instead of making it dependent on
* the compiler version. This isn't the smart way of doing
* it I guess, but it ensures WinXP / XP x64 compatibility,
* calling GET_WINDOWS_FUNCTION_NO_TYPECHECK() instead and
* removing the preprocessor conditional.
*/
//#if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY
/* For older Visual Studio, and also for the system I
* currently use for Coveritying the Windows code, this
* function isn't available in the header files to
* type-check */
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
kernel32_module, SetDefaultDllDirectories);
/*
#else
GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
#endif
*/
}
With that, the code can be compiled for XP or XP x64 platform targets. Save the file.
3b. The actual pixel saver modification: terminal.c
This was the part where Simon Tatham really helped me out, because I was just stumbling through the code blindly, knocking tableware off the shelves left and right. I will not describe all the mistakes I’ve made here though. While the learning process and the mistakes being made can be interesting at times, it’d make the article too big. It’s big enough already.
So, let’s get to it. Open terminal.c, and hop to about line number 1800. Look for void term_size(Terminal *term, int newrows, int newcols, int newsavelines). Right below, add a bit of code so that it looks like this:
/*
* Set up the terminal for a given size.
*/
void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
{
/*
* Modified by M. Lackner / Thrawn to handle cases where we would need term
* before it being defined. This happens when creating a window with the
* WS_POPUP style, were we get calls back from the Win32 API to determine
* the size of term, when term doesn't even exist yet. We deal with that
* using a null pointer check. If term isn't there yet, just drop
* everything and run away!
*/
if (term == NULL)
return;
tree234 *newalt;
termline **newdisp, *line;
int i, j, oldrows = term->rows;
Because without that added term == NULL check, you’ll see a null pointer exception!

Nooooooo!
Before Simon taught me things about how to use a debugger properly and how to not ignore his remarks in the code (
), I was at a loss here. Let’s visualize what’s happening:

puTTY stack trace for that nullptr exception
Read from bottom to top: The main code in WinMain() creates a window first by calling CreateWindowExW() in the Win32 API, and then creates the terminal inside. But in this case we never get to the terminal! As we change the window style to something rather unusual (see below), we get an unexpected call back from the Win32 API because the window size isn’t as expected at window creation time. This causes a call from the API to puTTY’s WndProc() function, and here’s where things start going very wrong, because that causes a call to term_size() in terminal.c. And term_size() needs a terminal term to be defined. But term doesn’t exist yet, because it’s defined only after window creation.
So we check for term being NULL right at the beginning of term_size(), and if term is indeed not there yet, we simply leave the function without doing anything, whistling a lighthearted melody while doing so.
That’s all you need to do for terminal.c. Save the file.
3c. The actual pixel saver modification: windows\window.c
This is the last file to change, and most of the work needs to be done here. Open windows\window.c. Look for the following code block:
int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
int exwinmode = 0;
wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
if (!conf_get_bool(conf, CONF_scrollbar))
winmode &= ~(WS_VSCROLL);
if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
if (conf_get_bool(conf, CONF_alwaysontop))
exwinmode |= WS_EX_TOPMOST;
if (conf_get_bool(conf, CONF_sunken_edge))
exwinmode |= WS_EX_CLIENTEDGE;
hwnd = CreateWindowExW(exwinmode, uappname, uappname,
winmode, CW_USEDEFAULT, CW_USEDEFAULT,
guess_width, guess_height,
NULL, NULL, inst, NULL);
sfree(uappname);
}
Now this is the most important change, even if not the only one required, change it into this:
/*
* Modified by M. Lackner / Thrawn to spawn terminal windows in pixel
* saver mode. This means no title bar, no scroll bar, but a resizable
* window border.
*/
// int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL;
int winmode = (WS_POPUP | WS_SYSMENU | WS_THICKFRAME) & ~WS_VSCROLL;
int exwinmode = 0;
wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname);
if (!conf_get_bool(conf, CONF_scrollbar))
winmode &= ~(WS_VSCROLL);
if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
if (conf_get_bool(conf, CONF_alwaysontop))
exwinmode |= WS_EX_TOPMOST;
if (conf_get_bool(conf, CONF_sunken_edge))
exwinmode |= WS_EX_CLIENTEDGE;
hwnd = CreateWindowExW(exwinmode, uappname, uappname,
winmode, CW_USEDEFAULT, CW_USEDEFAULT,
guess_width, guess_height,
NULL, NULL, inst, NULL);
sfree(uappname);
}
The only window styles that I found that could really get rid of the title bar while sticking to the Win32 API were WS_POPUP and the slightly more feature-rich WS_POPUPWINDOW. Going for a minimalistic approach, I started with just WS_POPUP, adding only what I wanted using logical OR operators:
WS_SYSMENU adds a right-click menu for the program on the task bar. We need this so we can access the menu options “Move” and “Resize”.
WS_THICKFRAME ensures that the window actually is resizable. This will cause visible borders being drawn around windows, looks worst on Windows 7, okay on XP, best on Windows 10.
WS_VSCROLL is connected via an AND NOT operator, since we don’t need a vertical scroll bar here. I consider it useless, like the title bar, because we can just use the mouse wheel or the PgUp/PgDn keys.
As for that window style, I got the [right hint] from user [knn9], so thanks, knn9!
Let’s continue to somewhere near line number 800 or so, look for this code:
/*
* Initialise the scroll bar.
*/
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = term->rows - 1;
si.nPage = term->rows;
si.nPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, false);
}
There’s zero need to do this anymore, so disable it:
/*
* Initialise the scroll bar.
*/
/*
* Modified by M. Lackner / Thrawn. We disable vertical scrollbar
* setup entirely for pixel saver mode.
*/
/*
{
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = term->rows - 1;
si.nPage = term->rows;
si.nPos = 0;
SetScrollInfo(hwnd, SB_VERT, &si, false);
}
*/
Then, quite a bit farther down, somewhere around line 2490, look for this:
if (conf_get_bool(conf, is_full_screen() ?
CONF_scrollbar_in_fullscreen :
CONF_scrollbar))
nflg |= WS_VSCROLL;
else
nflg &= ~WS_VSCROLL;
To ensure we don’t get any scroll bar shoved into our window if the configuration says so or if fullscreen mode is activated, change it into this:
if (conf_get_bool(conf, is_full_screen() ?
CONF_scrollbar_in_fullscreen :
CONF_scrollbar))
/*
* Modified by M. Lackner: No vertical scrollbar
* even if activated or in fullscreen.
*/
nflg &= ~WS_VSCROLL;
else
nflg &= ~WS_VSCROLL;
About a thousand lines below this part, around line 3480 or so, look for the function static void sys_cursor_update(void). This, too, gets called when the Win32 API reacts to CreateWindowExW(). Like term_size() in terminal.c, it’s trying to do things with term at a time when it doesn’t exist yet. This would cause a similar null pointer exception, which we can deal with in the exact same way as before. Add another term == NULL check as shown here:
static void sys_cursor_update(void)
{
/*
* Modified by M. Lackner to handle cases where we would need term before
* it being defined. This happens when creating a window with the WS_POPUP
* style, were we get calls back from the Win32 API, causing this code to
* run. We deal with that using a null pointer check, returning immediately
* if term isn't there yet.
*/
if (term == NULL)
return;
COMPOSITIONFORM cf;
HIMC hIMC;
if (!term->has_focus) return;
The next thing to look for sits roughly around line number 4800, look for this code block:
static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page)
{
SCROLLINFO si;
if (!conf_get_bool(conf, is_full_screen() ?
CONF_scrollbar_in_fullscreen : CONF_scrollbar))
return;
si.cbSize = sizeof(si);
si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = total - 1;
si.nPage = page;
si.nPos = start;
if (hwnd)
SetScrollInfo(hwnd, SB_VERT, &si, true);
}
Scroll bar stuff! Let’s disable it:
static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page)
{
/*
* Modified by M. Lackner: We don't want puTTY to mess with its
* vertical scrollbar, so this code is disabled entirely, we
* return immediately!
*/
/*
SCROLLINFO si;
if (!conf_get_bool(conf, is_full_screen() ?
CONF_scrollbar_in_fullscreen : CONF_scrollbar))
*/
return;
/*
si.cbSize = sizeof(si);
si.fMask = SIF_ALL | SIF_DISABLENOSCROLL;
si.nMin = 0;
si.nMax = total - 1;
si.nPage = page;
si.nPos = start;
if (hwnd)
SetScrollInfo(hwnd, SB_VERT, &si, true);
*/
}
Strangely enough, when spawning a window using WS_POPUP, we somehow branch into fullscreen code, which also tends to mess with the scroll bars, especially on returning from fullscreen to windowed mode. Look for the corresponding code near line number 5770:
/*
* Go full-screen. This should only be called when we are already
* maximised.
*/
static void make_full_screen()
{
DWORD style;
RECT ss;
assert(IsZoomed(hwnd));
if (is_full_screen())
return;
/* Remove the window furniture. */
style = GetWindowLongPtr(hwnd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
if (conf_get_bool(conf, CONF_scrollbar_in_fullscreen))
style |= WS_VSCROLL;
else
style &= ~WS_VSCROLL;
SetWindowLongPtr(hwnd, GWL_STYLE, style);
/* Resize ourselves to exactly cover the nearest monitor. */
get_fullscreen_rect(&ss);
SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
ss.right - ss.left,
ss.bottom - ss.top,
SWP_FRAMECHANGED);
/* We may have changed size as a result */
reset_window(0);
/* Tick the menu item in the System and context menus. */
{
int i;
for (i = 0; i < lenof(popup_menus); i++)
CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED);
}
}
Actually, you know what? Let’s just get rid of fullscreen mode! Some people might not like this, but me? I never use puTTY in fullscreen mode.
/*
* Go full-screen. This should only be called when we are already
* maximised.
*/
static void make_full_screen()
{
/*
* Disabled by M. Lackner to make sure we'll keep our no-title-bar, no-scroll-bar
* kind of window when returning from fullscreen.
*/
/*
DWORD style;
RECT ss;
assert(IsZoomed(hwnd));
if (is_full_screen()) */
return;
/* Remove the window furniture. */
/*
style = GetWindowLongPtr(hwnd, GWL_STYLE);
style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME);
if (conf_get_bool(conf, CONF_scrollbar_in_fullscreen))
style |= WS_VSCROLL;
else
style &= ~WS_VSCROLL;
SetWindowLongPtr(hwnd, GWL_STYLE, style);
*/
/* Resize ourselves to exactly cover the nearest monitor. */
/*
get_fullscreen_rect(&ss);
SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top,
ss.right - ss.left,
ss.bottom - ss.top,
SWP_FRAMECHANGED);
*/
/* We may have changed size as a result */
// reset_window(0);
/* Tick the menu item in the System and context menus. */
/*
{
int i;
for (i = 0; i < lenof(popup_menus); i++)
CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED);
}
*/
}
Now we do the same for the code that switches back from fullscreen into windowed mode, look just below:
/*
* Clear the full-screen attributes.
*/
static void clear_full_screen()
{
DWORD oldstyle, style;
/* Reinstate the window furniture. */
style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);
style |= WS_CAPTION | WS_BORDER;
if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
style &= ~WS_THICKFRAME;
else
style |= WS_THICKFRAME;
if (conf_get_bool(conf, CONF_scrollbar))
style |= WS_VSCROLL;
else
style &= ~WS_VSCROLL;
if (style != oldstyle) {
SetWindowLongPtr(hwnd, GWL_STYLE, style);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_FRAMECHANGED);
}
/* Untick the menu item in the System and context menus. */
{
int i;
for (i = 0; i < lenof(popup_menus); i++)
CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED);
}
return;
}
Get rid of its functionality as well:
/*
* Clear the full-screen attributes.
*/
static void clear_full_screen()
{
/*
* Disabled by M. Lackner / Thrawn to make sure we'll keep our no-title-bar,
* no-scroll-bar kind of window when returning from fullscreen.
*/
// DWORD oldstyle, style;
/* Reinstate the window furniture. */
/*
style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE);
style |= WS_CAPTION | WS_BORDER;
if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED)
style &= ~WS_THICKFRAME;
else
style |= WS_THICKFRAME;
if (conf_get_bool(conf, CONF_scrollbar))
style |= WS_VSCROLL;
else
style &= ~WS_VSCROLL;
if (style != oldstyle) {
SetWindowLongPtr(hwnd, GWL_STYLE, style);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
SWP_FRAMECHANGED);
}
*/
/* Untick the menu item in the System and context menus. */
/*
{
int i;
for (i = 0; i < lenof(popup_menus); i++)
CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED);
}
*/
return;
}
Naturally I should look into why fullscreen code is even being touched when using the WS_POPUP style instead of just diabling stuff like this. Generally, I should be doing a few things a little bit more smartly, but I’m too much of a lazy bum I guess. 
And that concludes the modification, you can now compile the code to build yourself a pixel-saving puTTY terminal specifically altered for use with tiled desktops.
If you think I did something really stupid there somewhere, or if you would like a current version of puTTY for Windows XP / XP x64 without the GUI modifications, let me know in the comments!
And now, let’s tile some windows! 
PS.: I just hope this hacked up puTTY doesn’t crash anywhere else… But if it does, let me know in the comments as well!