Transparent Sublime Text 3 at least on Windows

There are sometimes when you like to read content behind your code editor, specially snippets, documentation of APIs or other important stuff that sometimes requires you to switch between your code editor and that documentation (webpage, pdf, etc.).

And that is why it is sometimes useful to have a translucent code editor, and if you love using SublimeText3 editor, you might know that it doesn’t allow you to set its opacity level, so you might end up hitting Alt-Tab hotkeys or adjusting both windows side by side in order to copy or read a documentation without hassle.

Anyhow, having a little knowledge about Windows APIs gave me the idea to change the application’s alpha opacity level, and since most applications running on Windows let us do it, it is only matter of coding now.

Requirements

All applications that let us adjust the alpha opacity are set to WS_EX_LAYERED in its EX_STYLE flag, specially used on most applications that have custom skinned windows, but we can still force any applications to have that feature.

And if we spy SublimeText3 with uuSpy or anyother application for monitoring executables you like, you will notice that ST3 is not Layered.

Besides, now we also know that SublimeText has a class name and it is "PX_WINDOW_CLASS” which will help us locate all instances of Sublime Text and let us change its layered status.

Of course with uuSpy we can enable WS_EX_LAYERED but we need to do it from SublimeText itself, and luckily it allows us to write our own plugins with Python 3.3.

How to change the opacity of any application with WinAPI

We need to use the following WinAPI functions:

GetDesktopWindow

GetWindow

GetWindowLong

GetClassName

IsWindowVisible

SetWindowLong

SetLayeredWindowAttributes

ShellExecute

The most important ones are SetWindowLong and SetLayeredWindowAttributes. The other ones will help you locate SublimeText running instances, and ShellExecute for a hack to bypass SublimeText unknown crashes when using SetWindowLong.

Basically we set any application to WS_EX_LAYERED with SetWindowLong as follows:

First we need to make sure to only set WS_EX_LAYERED flag keeping the other styles untouched and that’s why we need GetWindowLong.

wl = GetWindowLong(LHWindow,GWL_EXSTYLE)

We will copy the EX_STYLEs to wl and then add WS_EX_LAYERED with a logic operation (LHWindow contains the unique SublimeText instance id).

wl = wl or WS_EX_LAYERED

And finally we apply that changes back to our target application with SetWindowLong.

SetWindowLong(LHWindow, GWL_EXSTYLE, wl)

And now we can finally set the application’s opacity level (from 255 to 0) with SetLayeredWindowAttributes.

SetLayeredWindowAttributes(LHWindow,0,opacity, LWA_ALPHA)

Where opacity contains a value from 0 to 255, i.e. from totally invisible to opaque.

Issues

As I mentioned before, SublimeText, for some unknown reason, crashes when we call SetWindowLong from itself using python or an external DLL.

So we need to use an external executable that will help us to set the Layered mode, and that’s a simple console application that I wrote in assembler in order to make it the smallest possible.

This console application will only take two parameters, the HWND (sublime text instance id) and the current EX_STYLE, then will apply WS_LAYERED to it, that is all.

Of course we only need to call it once per sublime instance, but sometimes we have more than one instance (window) of sublime text, which can also be created at any moment, so that’s why we will call this console application everytime, to make sure new sublime windows can also be translucent.

Our Plan

These are the steps we need to do in order to change SublimeText’s opacity.

Iterate among all windows and find the ones that have PX_WINDOW_CLASS as its class name (those are instances of SublimeText).

On each instance found we apply Layered mode and modify the opacity level.

That’s all, easy uh!

So here is the part of our code that searches all windows (even hidden ones) in order to find sublime text windows.

First we find the Desktop instance ID and then its first child (any application) and we will walk on every next window with GetWindow(currentWindow, GW_HWNDNEXT).

On each window found we need to know if it is visible and is a parent one (i.e. we won’t bother with mdi or sdi child windows which are dependant of the applications styles mostly). And finally making sure it has PX_WINDOW_CLASS as class name.

Only then we can set its EX_STYLE to WS_EX_LAYERED and set its opacity level.

In the code you’ll notice that it calls ShellExecute instead of SetWindowLong, and that’s the horrible hack (workaround) to apply SetWindowLong without crashing SublimeText3. I don’t know exactly what triggers that behavior, but reading on issues related to some Python versions, it has a bug with SetWindowLong api. However, I’ve tried using a DLL which will call SetWindowLong from our python script, but it crashes SublimeText3 too. And finally, executing an external console application with ShellExecute looks to be the better approach, since it is a different independent application that changes our SublimeText EX_STYLE.

About the entire plugin

The plugin is meant to be used only on Windows environments, since it uses a lot the Windows API, and sadly I cannot write it for Linux or MacOS since I don’t have enough knowledge on those platforms, hopefully anyone can improve this plugin and port it to those other platforms.

And about the entire plugin, I won’t bother explaining all the source code, it is easy to understand and you can play with it adjusting it to your needs.

The Plugin

Now we can have our SublimeText editor with transparency and we will be able to see behind it.

As you can see in the above picture, I can read the MSDN documentation about SetLayeredWindowAttributes.

Conclusions

Sublime Text 3 plugin feature with the help of Python is a really great help, as you noticed it you can even call WinAPIs to modify our code editor transparency and customize it to our needs, not only adding extra features, themes, shortcut hotkeys, but also modifying the application’s opacity.