Rofi: Use only `Alt+Tab` (without pressing Enter), like in kde

Created on 29 Mar 2014  Â·  44Comments  Â·  Source: davatorium/rofi

It would be nice to use only Alt+Tab (without pressing Enter) to change the focus.

The proposed method would change the focus when Alt is not longer pressed. Once the menu is displayed, Alt+Tab can be used to go to the item in the menu (observe that actually only Tab is pressed again since Alt is continuously pressed). And Alt+Shift+Tab for the previous item. In order to cancel, ESC can be used (while pressing Alt). This is the default behavior in kde, for example.

I believe it is faster and uses less hand movements.

Nice To Have enhancement

Most helpful comment

Use Alt+ISO_Left_Tab instead of Alt+Shift+Tab.

Selecting the second row should be something done in the window mode, or at rofi’s global level. That would remove the need for your hack and the resulting Tab-still-down issue. :-)

All 44 comments

Patch is welcome.

+1 for this feature :)
super job dave !

An option to always select the MRU (most recently used) window would already be a big step :+1:

@sanderboom Probably better left to the wm in my opinion.

@sanderboom isn't MRU already implemented in window modi? It works for me at least.

@roomcays for me the current active window is selected by default. If I want 'alt-tab-like' behavior, I have to press keydown, enter.

I did a quick test with -a 1, but this didn't seem to work as expected. Are you using this or an other switch?

@vyp The scenario I'd like to use rofi for is a 'cross workspace alt-tab'. Could probably be done using wm as well, although I think it would be a nice and non-intrusive addition to rofi.

I see now what you're trying to achieve. Got the same behaviour.
As a workaround you can try to emulate rofi, keydown, enter with xdotool and xbindkeys (or alternatives).
It would be in line with UNIX philosophy (one tool to do one thing).

@roomcays thanks for the suggestion. In this case I'd need to use a second shortcut for this as I still want to be able to use rofi in different ways. That's why an option to preselect not the current but logical MRU item would be nice :) I'll experiment some more.

@sanderboom Well yes, a non-cross workspace alt tab would be pretty limiting. Which window manager/de are you using? Been a long time since I've used a de, but most de's usually have some sort of alt-tab behaviour built in.

Otherwise, even if you're using a non-ewmh wm, this should be pretty easy to achieve with wmutils. But I haven't tried it myself so I'm not sure.

Now that we have on-release bindings (in master), it should be possible.

I think I managed to work around the X11 limitation about already-down modifiers.

To achieve that: rofi -kb-accept-entry '!Alt-Tab' -kb-row-down Alt-Tab.
Does it work for you?

Does it work for you?

Two nits:

  • Tab must be pressed at least one more time after rofi opens
  • There seems to be a half-second delay between releasing Alt and rofi closing.

Just tested this:

  • only need to hit tab once more when you are not selecting next entries. (so you want to select the first one) Works fine when tab pressed atleast once.
  • 1/2 s delay I can reproduce.

removed the delay.

I manage to almost implement it (With a small tweak to select the second item of the list)

#!/bin/bash
rofi \
    -show window  \
    -kb-cancel "Alt+Escape,Escape" \
    -kb-accept-entry "!Alt-Tab,Return"\
    -kb-row-down "Alt+Tab,Alt+Down" \
    -kb-row-up "Alt+Shift+Tab,Alt+Up"&
xdotool keyup Tab
xdotool keydown Tab

I just have 2 issues remaining:

  • If i'm going to fast on my Alt-Tab, it's keeping the tab key as down.
  • Alt+Shift+Tab is not working

Use Alt+ISO_Left_Tab instead of Alt+Shift+Tab.

Selecting the second row should be something done in the window mode, or at rofi’s global level. That would remove the need for your hack and the resulting Tab-still-down issue. :-)

maybe I'm confused on what the -selected-row argument does, but why couldn't you pass in -selected-row 1 to automatically pick the next window?
EDIT: ah, looks like that's dmenu only

Selecting the second row should be something done in the window mode, or at rofi’s global level. That would remove the need for your hack and the resulting Tab-still-down issue. :-)

@sardemff7 could you explain what you mean by this?

To solve the tab key being held after Rofi is closed, I came up with the following:

xdotool search --sync --class Rofi keyup --delay 0 Tab && xdotool key --delay 0 Tab

This will wait for the Rofi window to open before executing the commands. Note that there is still a delay. Would love to see an official solution in Rofi.

The fake Tab workaround is a “fix” for two things:

First, just using Alt-Tab didn’t work in rofi. Now it does with a trick: have kb-accept-entry bound to !Alt-Tab,!Alt-Alt_L,!Alt_L (note the !Alt-Alt_L to get the initially down Alt_L key detected while X11 already sent us the Alt modifier).

Second, not selecting the already-focused window. On this one, we cannot do much because the EWMH spec is not perfect and doesn’t give us a 100% sure order-of-last-focused.
The best we can do in this scenario is add a -window-preselect option that users would set to the apparently-correct number for their WM. Please note that some WM may have unexpected behaviour here and that there is nothing to do (except adding WM-specific code/IPC, which we won’t).

Would that work for you? @DaveDavenport are you fine with yet another option? :-)

#!/bin/bash
xdotool search --sync --syncsleep 50 --limit 1 --class Rofi keyup --delay 0 Tab key --delay 0 Tab&
rofi \
    -show window  \
    -kb-cancel "Super+Escape,Escape" \
    -kb-accept-entry "!Super-Tab,!Super_L,!Super+Super_L,Return"\
    -kb-row-down "Super+Tab,Super+Down" \
    -kb-row-up "Super+ISO_Left_Tab,Super+Shift+Tab,Super+Up"&

I've submitted a PR to resolve a long standing TODO in xdotool to make the delay configurable here: https://github.com/jordansissel/xdotool/pull/238

That leaves me with the first release of Super_L having no effect if I have just pressed super+tab (plus the one fake tab from xdotool). I either have to press tab a second time before releasing super, or I have to tap super after releasing super, to get rofi to select and close

#!/bin/bash
xdotool search --sync --syncsleep 50 --limit 1 --class Rofi keyup --delay 0 Tab key --delay 0 Tab keyup --delay 0 Super_L keydown --delay 0 Super_L&
rofi \
    -show window  \
    -kb-cancel "Super+Escape,Escape" \
    -kb-accept-entry "!Super-Tab,!Super_L,!Super+Super_L,Return"\
    -kb-row-down "Super+Tab,Super+Down" \
    -kb-row-up "Super+ISO_Left_Tab,Super+Shift+Tab,Super+Up"&

Thanks to @sardemff7 for help on IRC. This seems to work exactly like the alt-tab that I am used to, as long as the window list is ordered by recent access which is outside rofi's control.

I found the main bug preventing this feature. Once I find a fix, you can all finally drop your xdotool shady hacks. :-)
To make sure I really found the bug, can you please all test this:

  1. drop your xdotool hack (or equivalent)
  2. use rofi -show window -kb-accept-entry "!Super-Tab,!Super_L,!Super+Super_L,Return" (or Alt/Alt_L or your favorite modifier) with your favorite binding system (i3, xbindkeys, whatever)
  3. try with the mouse outside the space rofi will be, it should not work
  4. try with the mouse inside the space rofi will be, it should work

IOW, it is “merely” a matter of actually getting the proper event which is tied to the pointer focus (while being a keymap event…)

rofi -show window -kb-accept-entry "!Super-Tab,!Super_L,!Super+Super_L,Return"
bash: !Super: event not found

Maybe single quotes were meant ?

rofi -show window -kb-accept-entry '!Alt-Tab,!Alt_L,!Alt+Alt_L,Return'

You’re not supposed to run it from a shell, so ! is not even supposed to be special and no quotes at all should work (Edit: not for e.g. i3 where , has a special meaning, so other tools may have the same thing), but if your setup requires that, sure, use single quotes.

It must be run from a binding system where Alt-Tab (or Super-Tab or whatever) is bound to launch rofi, so that rofi needs to properly handle the “already down” state of the keys.

Sorry, just passing by, saw the pinned issue, tried the command, got the error,
and wrongly thought that might be the reason for the lack of answer to your request for testing.

Unfortunately Alt-Tab and Super-Tab are already used in kde, and work perfectly,
so I do not want to mess with them.
I tried to define a Ctrl-Alt-Tab shortcut in kde, with the command

rofi -show window -kb-accept-entry "!Ctrl-Alt-Tab,Return" -kb-row-down "Ctrl-Alt-Tab" -kb-cancel "Ctrl-Alt-Escape,Escape"

which launch rofi, with the current window selected, subsequent Ctrl-Alt-Tab moving down the selection.
The behavior does not seem to depend on whether the mouse is in the space rofi will be or not.
But, not being concerned, I'm probably missing the point, even after reading this conversation.
Anyway, rofi looks interesting, I'll probably find a use for it some day, thanks !

The key here is that the “doing nothing” (in theory, “switch to last focus”) Alt-Tab would be triggered by the !Alt-Alt_L binding in rofi, so you two-modifiers test is … well I’m not even sure we could really trigger it, but it would be !Ctrl-Alt-Alt_L,!Ctrl-Alt-Ctrl_L or something, I guess.

with

rofi -show window -kb-accept-entry "!Ctrl-Alt-Tab,!Ctrl-Alt-Alt_L,!Ctrl-Alt-Ctrl_L,Return" -kb-row-down "Ctrl-Alt-Tab" -kb-cancel "Ctrl-Alt-Escape,Escape"

there was an error "no keysym found for binding '!Ctrl-Alt-Ctrl_L'. Removing this one,

rofi -show window -kb-accept-entry "!Ctrl-Alt-Tab,!Ctrl-Alt-Alt_L,Return" -kb-row-down "Ctrl-Alt-Tab" -kb-cancel "Ctrl-Alt-Escape,Escape"

there is indeed a difference: when the mouse is where the rofi window will be,
the rofi window disappears as soon as the Ctrl-Alt keys are released,
while when the mouse is outside this area, the rofi window remains visible.

@sparr, thank you for you script. I found that if I use https://github.com/altdesktop/i3ipc-python/pull/141
as wrapper for your script and use rofi in dmenu mode, when I call Alt+Tab very fast, then xdotool and rofi did not manage events in the right way. And rofi does not accept entry, just showing menu and select second entry. So I used this script:

xdotool search --sync --syncsleep 50 --limit 1 --class Rofi \
  keyup --delay 0 Alt+Tab \
  keydown --delay 0 Alt+Tab \
    && test $(is-alt-pressed) == "false" \
       && xdotool search --limit 1 --class Rofi key --delay 0 Return&

cat /dev/stdin | rofi -dmenu -kb-accept-entry '!Alt-Tab,Return' -kb-row-down Alt-Tab" -kb-toggle-sort 'Alt-Ctrl-Shift-S' &

Where is-alt-pressed code is:

#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdbool.h>
#include <stdio.h>

int main()
{
    Display* dpy = XOpenDisplay(NULL);
    char keys_return[32];
    XQueryKeymap( dpy, keys_return );
    KeyCode kcal = XKeysymToKeycode( dpy, XK_Alt_L );
    bool bAltLPressed = !!( keys_return[ kcal>>3 ] & ( 1<<(kcal&7) ) );
    KeyCode kcar = XKeysymToKeycode( dpy, XK_Alt_R );
    bool bAltRPressed = !!( keys_return[ kcar>>3 ] & ( 1<<(kcar&7) ) );
    printf(bAltLPressed || bAltRPressed ? "true" : "false");
    XCloseDisplay(dpy);
}

To solve the tab key being held after Rofi is closed, I came up with the following:

xdotool search --sync --class Rofi keyup --delay 0 Tab && xdotool key --delay 0 Tab

This will wait for the Rofi window to open before executing the commands. Note that there is still a delay. Would love to see an official solution in Rofi.

There is no delay if I seperate xdotool into two lines.

        rofi -modi window -show window -hide-scrollbar -padding 50 -line-padding 4 -auto-select \
             -kb-cancel "Alt+Escape,Escape" \
             -kb-accept-entry "!Alt-Tab,!Alt+Alt_L,Return"\
             -kb-row-down "Alt+Tab,Alt+Down" \
             -kb-row-up "Alt+ISO_Left_Tab,Alt+Up" &
        xdotool search --sync --class Rofi keyup --delay 0 Tab & 
        xdotool key --delay 0 Tab &

The best solution of course is https://github.com/davatorium/rofi/pull/909

Why it is not merged, it doesn't seem to do any harm if people dislike it not using it.

The best solution of course is #909
Why it is not merged, it doesn't seem to do any harm if people dislike it not using it.

Correct me if I am wrong, but I think even that wouldn't really solve the problem of pressing tab one additional time than normal window switchers. (Why? Because, it doesn't register "!Alt+Tab" unless you press and leave tab once!)

I kinda dislike the end result here, I really want my rofi to come up opon Alt+Tab (using i3 conf) and lauch whatever option is tab or alt+tab is dropped. I also want it to start at result nr 2

that would have been the dream atleast :P

#!/bin/bash rofi \ -show window \ -kb-accept-entry "!Alt-Tab,!Alt+Alt_L"\ -kb-row-down "Alt+Tab" \
Acually this solves most my problems with the solution, but I still have one issue. When running the script, sometimes I dropp the Alt+Tab so fast that rofi does not recognize it and therefore does not quit. All help would be appriciated

@leier69 Have you tried https://github.com/davatorium/rofi/issues/38#issuecomment-456988468?

That solution works very well for me. You'll only need to replace Super with Tab. There is still the small issue of sometimes not recognizing the entry when switching too fast, but it happens rarely. I found out that changing --syncsleep 50 to --syncsleep 10 helps a bit with that.

Thanks @DaveDavenport for cca9fa3372f2a4e7970bf1c81890d2c8037f21d5, which enables -selected-row in all modi.

I pushed a hack in the wip/alt-tab-hacks branch for the focus issue.

Could anyone here try this branch with the minimal command :

rofi -show window -kb-accept-entry "!Alt-Tab,!Alt+Alt_L" -kb-row-down "Alt+Tab" -selected-row 1

If you’re really quick, it may not work, but I’m not sure we can do something about that, the X server doesn’t send us the event in the “correct” order for some reason in this case.

Thanks for all the info in this issue! For some reason though, my window list is in a fixed order - not in MRU order. Is that expected? (Using i3)

Yeah, I forgot one very important information :

The order of windows is entirely WM-dependent, and there is nothing rofi can do about it without adding a whole bunch of WM-specific code, which will not happen.

As for i3 specifically, with 4.16.1 (yeah I should update), the order is more or less what I’d expect. But it should be quite easy to use their focus-last example script, though it requires a daemon script to monitor all the changes.

However, any ordering issue is outside the scope of rofi. Now that you can use -selected-row for any modi, this issue shall remain focused (pun intended) on the binding problem.

If I understand properly, this is a fundamental issue with how X is designed? How does KDE / other app-switchers do it? I am not sure.

If you’re talking about the ordering issue, please stop. Anything integrated with a DE or WM will have DE/WM-specific code for that. rofi is a 100% generic tool, relying solely on EWMH and ICCCM standards. Specifically “Alt-Tab switchers” are commonly implemented directly in a DE or designed for one DE only. If you have an example of a switcher with purely generic code that happens to work everywhere, please share, hopefully it’s open source and we can reuse some of its logic.

If you’re talking about the binding issue, it’s actually pretty much the same. Anything more integrated than rofi will probably use specific tricks. We could change the binding handling a tiny bit (though it would be a pretty big code change) to react on “key up” events even if we didn’t see the “key down” event but it’s pretty much the only use case where it would makes sense, and IMO would lead to unexpected bindings triggering in all other cases.

I am not talking about the ordering issue, don't worry. Apologies for not making that clear. I know you can easily change that on Xmonad in fact.

Yes, I was thinking that it had to be more directly integrated with the DE itself, hence probably why this issue has been open for around 6 years now. I meant to ask if it was easy to detect such a "key-up" event / what the biggest obstacle is.

FWIW, I 'fixed' the mouse bug https://github.com/davatorium/rofi/issues/38#issuecomment-469064051 by moving the mouse into the rofi window and back, using xdotool.
I played around with various strategies to find the correct position, see at the end of this script (it build on top of the -selected-rows PR)

#!/usr/bin/env bash
set -euo pipefail

LOGFILE=""
ROFI_ARGS=()
KB_ACCEPT_APPEND=""
MODE=""
while getopts "m:l:" OPTION; do 
    case "$OPTION" in
        m)
            MODE="$OPTARG"
            case "$MODE" in
                alttab)
                    ROFI_ARGS+=( -show window -selected-row 1) # -selected-row is only available in https://github.com/davatorium/rofi/pull/909
                    KB_ACCEPT_APPEND=",!Alt+Alt_L"
                    ;;
                altspace)
                    ROFI_ARGS+=( -show window )
                    ;;
                superspace)
                    ROFI_ARGS+=( -show drun )
                    ;;
                *)
                    echo "unknown mode $MODE";
                    exit 1 # todo use libnotify to display error
                    ;;
            esac
            ;;
        l)
            LOGFILE="$OPTARG"
            ;;
        *)
            echo "unknown option '$OPTION'"
            exit 1
            ;;
    esac
done

if [ "$MODE" == "" ]; then
    echo "must specify a mode"
    exit 1
fi

if [ "$LOGFILE" != "" ]; then
    echo "redirecting output to file"
    exec >> "$LOGFILE"
    exec 2>&1
fi

# launch rofi
export ROFI_DUMP_WINDOW_POSITION="$(mktemp)" # see below
rofi \
    -kb-cancel "Alt+Escape,Escape" \
    -kb-accept-entry '!Alt-Tab,!Alt+Down,!Alt+ISO_Left_Tab,!Alt+Up,Return'$KB_ACCEPT_APPEND \
    -kb-row-down 'Alt-Tab,Alt+Down,Down' \
    -kb-row-up 'Alt+ISO_Left_Tab,Alt+Up,Up' \
    "${ROFI_ARGS[@]}"&
rofipid="$!"

# The following Strategies move the mouse into the rofi window and back to where it came from.
# (this is a work around for https://github.com/davatorium/rofi/issues/38#issuecomment-469064051 )

# Strategy 1
#echo "move mouse to center screen (best effort, fast because we avoid the search)"
#xdotool mousemove 960 540 sleep 0.1 mousemove restore

# Strategy 2
#echo "move mouse to rofi center (precise, but slow due to search and fails sometimes)"
#(
#    source <(xdotool search --sync --limit 1 --pid "$rofipid" --class Rofi getwindowgeometry --shell)
#    xdotool mousemove $X $Y sleep 0.1 mousemove restore
#)

# Strategy 3
# diff --git a/source/view.c b/source/view.c
# index 5ba1309a..11d98840 100644
# --- a/source/view.c
# +++ b/source/view.c
# @@ -411,6 +411,15 @@ static void rofi_view_calculate_window_position ( RofiViewState *state )
#      RofiDistance y = rofi_theme_get_distance ( WIDGET ( state->main_window ), "y-offset", config.y_offset );
#      state->x += distance_get_pixel ( x, ROFI_ORIENTATION_HORIZONTAL );
#      state->y += distance_get_pixel ( y, ROFI_ORIENTATION_VERTICAL );
# +
# +    // dump position to file if desired
# +    const char *dump_file_path = getenv("ROFI_DUMP_WINDOW_POSITION");
# +    static FILE *dump_file = NULL;
# +    if (dump_file_path) {
# +        if (!dump_file) dump_file = fopen(dump_file_path, "w");
# +        fprintf(dump_file, "X=%d\nY=%d\nWIDTH=%d\nHEIGHT=%d\n", state->x, state->y, state->width, state->height);
# +        fflush(dump_file);
# +    }
#  }
#  
#  static void rofi_view_window_update_size ( RofiViewState * state )
(
    while test ! -e $ROFI_DUMP_WINDOW_POSITION ||  test $(wc -l "$ROFI_DUMP_WINDOW_POSITION" | awk '{print $1}') -lt 4 ; do sleep 0.05; done
    source "$ROFI_DUMP_WINDOW_POSITION"
    xdotool mousemove $X $Y sleep 0.1 mousemove restore
)

rm "$ROFI_DUMP_WINDOW_POSITION"
wait "$rofipid"

@sardemff7

The patch from wip/alt-tab-hacks fixes the focus isssue for me very well, making the alt+tab use case completely functional. Shouldnt we open a PR ?

master needs to be merged or it doesnt work

@duckie Thank you a lot for testing

I’ve pushed the fixes to the next branch, hopefully more people will try them

Was this page helpful?
0 / 5 - 0 ratings

Related issues

icf20 picture icf20  Â·  4Comments

blueyed picture blueyed  Â·  5Comments

benjumanji picture benjumanji  Â·  5Comments

DragonAxe picture DragonAxe  Â·  3Comments

ghost picture ghost  Â·  3Comments