Alternative window management in Linux

I'm one of those who puts switching layouts on Caps Lock because I'm too lazy to press 2 keys when you can press one. I would even like 2 unnecessary keys: I would use one to turn on the English layout, and the second for the Russian one. But the second unnecessary key is a call to the context menu, which is so unnecessary that it is cut out by many laptop manufacturers. So you have to be content with what you have.

And I also don’t want to look for their icons on the taskbar when switching windows, to catch a look at the names when scrolling through Alt + Tab, scroll through desktops, etc. I want to press a combination of keys (ideally, one at a time, but there are no more free unnecessary keys) and immediately get into the window I need. For example like this:

  • Alt+F: Firefox
  • Alt+D: Firefox (Private Browsing)
  • Alt+T: Terminal
  • Alt+M: Calculator
  • Alt+E: IntelliJ Idea
  • etc.

Moreover, by pressing, for example, on Alt + M I want to see the calculator regardless of whether this program is currently running. If it is running, then its window must be given focus, and if not, run the desired program and transfer focus when it loads.

For cases that are not covered by the previous scenario, I want to have universal keyboard shortcuts that can be easily assigned to any of the open windows. For example, I have 10 combinations assigned from Alt + 1 to Alt + 0, which are not tied to any programs. I can just click Alt + 1 and the window that is currently focused will get the focus when clicked Alt + 1.

Under the cut, a description of a couple more features and an answer to how you can do this. But I’ll immediately warn you that such customization β€œfor yourself” can cause strong dependence and even breakage if you need to use Windows, Mac OS, or even someone else’s computer with Linux.

In fact, if you think about it, we don't use that many programs on a daily basis. Browser, terminal, IDE, some kind of messenger, file manager, calculator, and, perhaps, that's almost everything. You don't need many keyboard shortcuts to cover 95% of everyday tasks.

For programs that have several windows open, one of them can be assigned as the main one. For example, multiple IntelliJ Idea windows are open, assigned to Alt + E. Under normal circumstances, when you press Alt + E some window of this program will open, most likely the one that was opened first. However, if you click on Alt + E when one of the windows of this program is already in focus, then it is this window that will be assigned as the main one and it will be the focus that will be transferred to it when the combination is pressed next.

The main window can be reassigned. To do this, the combination must first be reset, and then assign another window to it as the main one. To reset the combination, you need to press the combination itself, and then a special reset combination, I have it assigned to Alt + Backspace. This will call a script that will unassign the main window for the previous combination. And then you can assign a new main window as described in the previous paragraph. Resetting the linked window to universal combinations occurs in the same way.

The introduction turned out to be long, but I wanted to first tell you what we are going to do, and then explain how to do it.

For those who are tired of reading

In short, the link to the scripts at the end of the article.

But still, it will not work right away to install and use. We'll first have to figure out how the script finds the right window. Without this, it will not be possible to tell the script exactly where to transfer the focus. And you need to understand what to do if suddenly a suitable window was not found.

And I will not focus on how to configure the execution of scripts by pressing key combinations. For example, in KDE this is in System Settings β†’ Shortcuts β†’ Custom Shortcuts. Other window managers should have this too.

Introduction to wmctrl

Wmctrl is a console utility for interacting with the X Window Manager. This is the key program for the script. Let's take a quick look at how it can be used.

First, let's display a list of open windows:

$ wmctrl -lx
0x01e0000e -1 plasmashell.plasmashell             N/A Desktop β€” Plasma
0x01e0001e -1 plasmashell.plasmashell             N/A Plasma
0x03a00001  0 skype.Skype                         N/A Skype
0x04400003  0 Navigator.Firefox                   N/A Google ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄Ρ‡ΠΈΠΊ - Mozilla Firefox
0x04400218  0 Navigator.Firefox                   N/A Π›ΡƒΡ‡ΡˆΠΈΠ΅ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ Π·Π° сутки / Π₯Π°Π±Ρ€ - Mozilla Firefox (Private Browsing)
...

Option -l displays a list of all open windows, and -x adds the class name to the output (Skype.Skype, Navigator.Firefox etc). Here we need the window id (column 1), the class name (column 3) and the window name (last column).

You can try to activate some window using the option -a:

$ wmctrl -a skype.Skype -x

If everything went according to plan, then the Skype window should appear on the screen. If instead of the option -x use option -i, then instead of the class name, you can specify the id of the window. The problem with id is that the id of the window changes every time the application starts and we cannot know it in advance. On the other hand, this attribute uniquely identifies a window, which can be important when an application has more than one window open. More on this a little further.

At this stage, we need to remember that we will search for the desired window using regex on the output wmctrl -lx. But that doesn't mean we have to use something complex. Usually the class name or window name is sufficient.

In principle, the main idea should already be clear. In the global hotkeys/shortcuts settings for your window manager, set up the desired combination to execute the script.

How to use scripts

First you need to install the console utilities wmctrl ΠΈ xdotool:

$ sudo apt-get install wmctrl xdotool

Next, you need to download the scripts and add them to $ PATH. I usually put them in ~/bin:

$ cd ~/bin
$ git clone https://github.com/masyamandev/Showwin-script.git
$ ln -s ./Showwin-script/showwin showwin
$ ln -s ./Showwin-script/showwinDetach showwinDetach

If directory ~/bin was not there, then you need to create it and reboot (or re-login), otherwise ~/bin will not fall into $ PATH. If everything is done correctly, then the scripts should be accessible from the console and Tab completion should work.

Main script showwin takes 2 parameters: the first is a regex, by which we will search for the desired window, and the second parameter is the command to be executed if the required window was not found.

You can try to execute a script, for example:

$ showwin "Mozilla Firefox$" firefox

If Firefox is installed, then its window must be given focus. Even if Firefox wasn't running, it should have started.

If it worked out, then you can try to configure the execution of commands on combinations. In the global hotkeys/shortcuts settings add:

  • Alt+F: showwin "Mozilla Firefox$" firefox
  • Alt+D: showwin "Mozilla Firefox (Private Browsing)$" "firefox -private-window"
  • Alt+C: showwin "chromium-browser.Chromium-browser N*" chromium-browser
  • Alt+X: showwin "chromium-browser.Chromium-browser I*" "chromium-browser -incognito"
  • Alt+S: showwin "skype.Skype" skypeforlinux
  • Alt+E: showwin "jetbrains-idea" idea.sh

And so on. Everyone can customize the combinations of keys and software as it suits him.
If everything worked out correctly, then using the above combinations, we will be able to switch between windows by simply pressing the keys.

I will disappoint chrome lovers: it is incognito to distinguish the usual window by the output wmctrl you can't, they have the same class names and window titles. In the proposed regex, the characters N* and I* are needed only so that these regular expressions differ from each other and they can be assigned different windows as the main ones.

To reset the main window of the previous combination (in fact for regex, which showwin called for the last time) you need to call the script showwinDetach. I have this script assigned to a keyboard shortcut Alt + Backspace.

At the script showwin there is one more function. When called with a single parameter (in this case, the parameter is just an identifier), it doesn't check the regex at all, and considers all windows to match. By itself, this seems useless, but in this way we can assign any window as the main one and quickly switch to this particular window.

I have the following combinations:

  • Alt+1: showwin "CustomKey1"
  • Alt+2: showwin "CustomKey2"
  • ...
  • Alt+0: showwin "CustomKey0"
  • Alt+Backspace: showwinDetach

This way I can bind any windows to combinations Alt + 1...Alt + 0. Just by clicking Alt + 1 I bind the current window to this combination. You can unlink by clicking Alt + 1, and then Alt + Backspace. Or close the window, that works too.

I'll go into some technical details next. You can not read them, but just try to configure and see. But I would still recommend understanding other people's scripts before running them on your computer :).

How to distinguish between different windows of the same application

In principle, the very first example "wmctrl -a skype.Skype -x" was working and can be used. But let's take another look at the Firefox example with 2 windows open:

0x04400003  0 Navigator.Firefox                   N/A Google ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄Ρ‡ΠΈΠΊ - Mozilla Firefox
0x04400218  0 Navigator.Firefox                   N/A Π›ΡƒΡ‡ΡˆΠΈΠ΅ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ Π·Π° сутки / Π₯Π°Π±Ρ€ - Mozilla Firefox (Private Browsing)

The first window is the normal mode, and the second one is Private Browsing. I would like to consider these windows as different applications and switch to them using different key combinations.

We need to complicate the script that switches windows. I used this solution: list all windows, do grep by regex, take the first line with head, get the first column (this will be the id of the window) with cut, switch to window by id.

There should be a joke about regular expressions and two problems, but in fact I'm not using anything complicated. I need regulars so that I can specify the end of the line (the "$" symbol) and distinguish "Mozilla Firefox $" from "Mozilla Firefox (Private Browsing) $".

The command looks something like this:

$ wmctrl -i -a `wmctrl -lx | grep -i "Mozilla Firefox$" | head -1 | cut -d" " -f1`

Here you can already guess about the second feature of the script: if grep did not return anything, then the desired application is not open and you need to start it by executing the command from the second parameter. And then periodically check whether the desired window has opened in order to give it focus. I will not focus on this, whoever needs it will look at the source code.

When application windows are not visible

So, we have learned how to transfer focus to the window of the desired application. But what if more than one application window is open? To which one should the focus be passed? The script above will most likely pass to the first open window. However, we would like more flexibility. I would like to be able to remember which window we need and switch to this particular window.

The idea was this: If we want to remember a certain window for a key combination, then we need to press this combination when the desired window is in focus. In the future, when you press this combination, the focus will be given to this particular window. Until the window closes or we reset this script combination showwinDetach.

Script algorithm showwin something like this:

  • Check if we have previously remembered the id of the window to which we want to transfer focus.
    If we remember and such a window still exists, then we transfer the focus to it and exit.
  • We look at which window is currently in focus, and if it fits our request, then remember its id to go to it in the future and exit.
  • We go to at least some suitable window if it exists or open the desired application.

You can find out which window is currently in focus using the xdotool console utility, converting its output to hexadecimal format:

$ printf "0x%08x" `xdotool getwindowfocus`

The easiest way to remember something in bash is to create files on an in-memory virtual file system. In Ubuntu, this is enabled by default in /dev/shm/. I can’t say anything about other distributions, I hope that there are similar ones too. Can be viewed with the command:

$ mount -l | grep tmpfs

The script will create empty directories in this folder, like this: /dev/shm/$USER/showwin/$SEARCH_REGEX/$WINDOW_ID. Additionally, on each call it will create a symlink /dev/shm/$USER/showwin/showwin_last on /dev/shm/$USER/showwin/$SEARCH_REGEX. This will be needed in order to, if necessary, remove the window id for a certain combination using a script showwinDetach.

What can be improved

First, the scripts must be configured by hand. Surely, due to the need to delve into and do a lot with your hands, many of you will not even try to set up the system. If it was possible to just install the package and set everything up easier, then perhaps it would gain some popularity. And there, look, and the application would be washed down in standard distributions.

And maybe it's easier to do. If by the window id you can find out the id of the process that created it, and by the process id you can find out which team created it, then it would be possible to automate the setup. In fact, I did not understand whether what I wrote in this paragraph is possible. The thing is, I personally like the way it works now. But if someone other than me finds the whole approach convenient and someone improves it, then I will gladly use a better solution.

Another problem, as I already wrote, is that in some cases windows cannot be distinguished from one another. So far, I've only seen this with incognito in chrome/chromium, but perhaps there is something similar somewhere else. As a last resort, there is always the option of universal combinations. Alt + 1...Alt + 0. Again, I use Firefox and personally for me this problem is not significant.

But a significant problem for me is that I use Mac OS at work and I couldn’t set up anything like that there. utility wmctrl I seem to be able to put it, but it doesn’t really work on Mac OS. Something to do with the app automator, but it is so slow that it is not convenient to use it even when it works. I also could not set up keyboard shortcuts so that they work in all programs. If suddenly someone comes up with a solution, I will be glad to use it.

Instead of a conclusion

It turned out unexpectedly a lot of words for such a seemingly simple functionality. I wanted to convey the idea and not overload the text, but I have not yet figured out how to tell it easier. Perhaps in video format it would be better, but they don’t like it like that here.

I talked a little about what's under the hood of the script and how to set it up. I didn’t go into the details of the script itself, but it’s only 50 lines, it’s not difficult to figure it out.

I hope that someone else will try this idea and maybe even appreciate it. About myself I can say that the script was written 3 years ago and it is VERY convenient for me. So convenient that it causes serious discomfort when working with other people's computers. And with a working MacBook.

Link to scripts

Source: habr.com

Add a comment