You are not logged in.

#1 2019-07-29 20:38:17

sooqua
Member
Registered: 2014-11-11
Posts: 14

[SOLVED] How to make right Alt do something useful?

I've spent 4 days trying to figure out how to map RAlt+additional keys to have arrow keys, text navigation/selection/deletion etc. in programs that don't support Vim or Emacs style keybindings, which are many.
We all know how time consuming and uncomfortable it is to reach for arrow keys, but what makes the situation worse is that I've got an HP Zbook, so my arrow keys be like:
iucaMi2.png
The keys I'm trying to map while holding down RAlt are:
hjkl to Arrow keys
HJKL to Shift+Arrow keys
f to Ctrl+Right
b to Ctrl+Left
F to Ctrl+Shift+Right
B to Ctrl+Shift+Left
x to Delete
d to Ctrl+Delete
w to Ctrl+Backspace
e to End
a to Home

I've tried mapping RAlt scancode to rightmeta, but I displays as Alt_R in xev.
Then I tried to bind RAlt+keys in AutoKey, but it doesn't allow to choose between left and right Alt, and I'm using the left alt for other purposes.
So I tried mapping RAlt to Hyper_L using xkbcomp:

xkb_symbols {
    key <RALT> { [ Hyper_L ] };
};

And binding Hyper+keys in AutoKey. That worked in chromium, but in my terminal emulator (I'm using Alacritty), it just outputs the keys I'm pressing, so I made a hack for each key (e.g. for Hyper+j that's bound to the Down arrow key, I bound Alt+j to... Alt+j).
That worked for a while, but then I decided to map Alt+f (left alt) to Shift+F6 in chromium only, to switch focus from address bar to the page, but it didn't let me, because I had Alt+f as a hack for Hyper+f, even though I specified chromium's window class, so it could still use the other script for other programs? And if I disable the hack for this, I can't use Alt+f in Alacritty (nothing happens when I press it).
Then I tried to make RAlt modify the Mod3 bit with:

xmodmap -e 'add mod3 = Alt_R'

And binding Mod3+keys in Awesome WM, but it's such a hack I had to do something like this (unpress all the keys before sending fake input):

awful.key({ "Mod3" }, "j",
        function()
            root.fake_input("key_release", "j")
            root.fake_input("key_release", "Alt_R")
            root.fake_input("key_press", "Down")
            root.fake_input("key_release", "Down")
        end,
        {description = "simulate down arrow key", group = "fake_input"})

And it only works for 1 key stroke, if I hold down the Alt key and press j twice, the second time it just outputs "j". Adding

root.fake_input("key_press", "Alt_R")

At the end of the function doesn't work.

I'm starting to lose faith... Is this even possible in X? In Windows I would just use LowLevelKeyboardProc for this kind of thing. I would completely hide the right Alt from the system and all of the keys I'm pressing while holding it down, and send back completely different key strokes.

So, is there a way to make this work, in all programs?

Last edited by sooqua (2019-07-29 21:16:35)

Offline

#2 2019-07-29 21:15:45

sooqua
Member
Registered: 2014-11-11
Posts: 14

Re: [SOLVED] How to make right Alt do something useful?

Well that wasn't hard. Just made one script for Alt+f and did the class checking in there:

winClass = window.get_active_class()
if winClass == "chromium.Chromium":
    keyboard.send_keys("<shift>+<f6>")
else:
    keyboard.send_keys("<alt>+f")

Offline

#3 2019-07-29 22:01:15

sooqua
Member
Registered: 2014-11-11
Posts: 14

Re: [SOLVED] How to make right Alt do something useful?

...So I was messing around with xmodmap until I got this result:

xmodmap:  up to 3 keys per modifier, (keycodes in parentheses):

shift       Shift_L (0x32),  Shift_R (0x3e)
lock        Caps_Lock (0x42)
control     Control_L (0x25),  Control_R (0x69)
mod1        Alt_L (0x40),  Meta_L (0xcd)
mod2        Num_Lock (0x4d)
mod3        Hyper_R (0x6c)
mod4        Super_L (0x85),  Super_R (0x86),  Super_L (0xce)
mod5        ISO_Level3_Shift (0x5c),  Mode_switch (0xcb)

Now everything works in all programs without any Alt+ hacks in AutoKey! big_smile Finally a clean solution. Now I have to find a way to do this with xkbcomp...

Offline

#4 2019-07-30 03:42:06

sooqua
Member
Registered: 2014-11-11
Posts: 14

Re: [SOLVED] How to make right Alt do something useful?

...Sooo I was messing around with my ~/.xkb directory I made because I wanted to do everything with snippet files (i.e. don't just xkbcomp -xkb $DISPLAY xkbdump and re-import the whole file, instead use setxkbmap -print's output and add my snippet files in include`s.
It was a problem that there is a modifier_map "function", but no "clear_modifier" like in xmodmap.
So after adding

default partial modifier_keys
xkb_symbols "hyper" {
    key <RALT> { [ Hyper_R ] };
};

to my ~/.xkb/symbols/ralt I had this in my Mod1:

$xmodmap
mod1        Alt_L (0x40),  Hyper_R (0x6c),  Meta_L (0xcd)

Note the Hyper_R with a keycode of Alt_R is still in there. This broke all Alt+ shortcuts in chromium, even though Alt_L is still there?
And also Hyper_L is by default in Mod4 (which makes e.g. Awesome WM shortcuts also trigger on remapped RAlt)

mod4        Super_L (0x85),  Super_R (0x86),  Super_L (0xce),  Hyper_L (0xcf)

because of this line in /usr/share/X11/xkb/symbols/pc that is obviously included in setxkbmap -print's output if you are on a PC:

modifier_map Mod4   { <HYPR> };

And again, I didn't want to modify it directly, instead, override it with snippet files.

Then I noticed that /usr/share/X11/xkb/symbols/pc includes the altwin file. So I took a look at it and I still just can't understand what is happening here:

    key <LALT> { [ Alt_L, Meta_L ] };
    key <RALT> { type[Group1] = "TWO_LEVEL",
                 symbols[Group1] = [ Alt_R, Meta_R ] };
    modifier_map Mod1 { Alt_L, Alt_R, Meta_L, Meta_R };

Why is RALT defined differently? What is the difference here?

Anyway here is the version of my ~/.xkb that just works (same xmodmap output as in my previous post):
~/.xkb/keymap/mykbd

xkb_keymap {
	xkb_keycodes  { include "evdev+aliases(qwerty)+ralt(hyper)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete"	};
	xkb_symbols   { include "pc+us+us:2+inet(evdev)+capslock(escape)+ralt(hyper)"	};
	xkb_geometry  { include "pc(pc105)"	};
};

~/.xkb/keycodes/ralt

default partial
xkb_keycodes "hyper" {
    <HYPR> = 108;
};

~/.xkb/symbols/ralt

default partial modifier_keys
xkb_symbols "hyper" {
    key <HYPR> { type[Group1] = "TWO_LEVEL",
                 symbols[Group1] = [ Hyper_R, Hyper_R ] };
    modifier_map Mod3 { <HYPR> };
};

Invoke it with

xkbcomp -I$HOME/.xkb ~/.xkb/keymap/mykbd $DISPLAY

I have no idea how this works (I just got lucky) so if you know plz let me know.
I am now reading xkb manuals because I'm planning to port all the keybindings to xkb and not use AutoKey for that. Maybe I will understand what I just did at some point.

Offline

#5 2019-07-31 22:41:40

sooqua
Member
Registered: 2014-11-11
Posts: 14

Re: [SOLVED] How to make right Alt do something useful?

[SOLVED] v4

RAlt is now Hyper_R, a Mod3 key, and can activate Level3 with preserved Shift, or Level4 when the Shift key is pressed, depending on the type you choose.
CapsLock indicator is now showing Mod3 state (Hyper), since it's mapped to Escape.

~/.xkb/keymap/custom

xkb_keymap {
    xkb_keycodes  { include "evdev+aliases(qwerty)+ralt(hyper)" };
    xkb_types     { include "complete+hyper+hyper(4lvl)" };
    xkb_compat    { include "complete+indicator-capslock(mod3)" };
    xkb_symbols   { include "pc+us+us:2+inet(evdev)+capslock(escape)+group(rctrl_toggle)+ralt(hyper)+hyper" };
    xkb_geometry  { include "pc(pc105)" };
};

~/.xkb/compat/indicator-capslock

partial
xkb_compat "mod3" {
    indicator "Caps Lock" {
        whichModState= base;
        modifiers= Mod3;
    };
};

~/.xkb/keycodes/ralt

partial
xkb_keycodes "hyper" {
    <HYPR> = 108;
};

~/.xkb/symbols/hyper

default partial alphanumeric_keys modifier_keys
xkb_symbols "hyper" {
    key <LatW> {
        type = "HYPER",
        actions[Group1] = [NoAction(), NoAction(), RedirectKey(key=<BKSP>,modifiers=Control)],
        actions[Group2] = [NoAction(), NoAction(), RedirectKey(key=<BKSP>,modifiers=Control)]
    };
    key <LatE> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, End]
    };
    key <LatI> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Next]
    };
    key <LatO> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Prior]
    };

    key <LatA> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Home]
    };
    key <LatD> {
        type = "HYPER",
        actions[Group1] = [NoAction(), NoAction(), RedirectKey(key=<DELE>,modifiers=Control)],
        actions[Group2] = [NoAction(), NoAction(), RedirectKey(key=<DELE>,modifiers=Control)]
    };
    key <LatF> {
        type = "HYPER",
        actions[Group1] = [NoAction(), NoAction(), RedirectKey(key=<RGHT>,modifiers=Control)],
        actions[Group2] = [NoAction(), NoAction(), RedirectKey(key=<RGHT>,modifiers=Control)]
    };
    key <LatH> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Left]
    };
    key <LatJ> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Down]
    };
    key <LatK> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Up]
    };
    key <LatL> {
        type = "HYPER",
        symbols[Group1] = [NoSymbol, NoSymbol, Right]
    };

    key <LatX> {
        type = "HYPER4LVL",
        symbols[Group1] = [NoSymbol, NoSymbol, Delete, BackSpace]
    };
    key <LatB> {
        type = "HYPER",
        actions[Group1] = [NoAction(), NoAction(), RedirectKey(key=<LEFT>,modifiers=Control)],
        actions[Group2] = [NoAction(), NoAction(), RedirectKey(key=<LEFT>,modifiers=Control)]
    };
};

~/.xkb/symbols/ralt

partial modifier_keys
xkb_symbols "hyper" {
    key <HYPR> { [ Hyper_R ] };
    modifier_map none { <HYPR> };
    modifier_map Mod3 { <HYPR> };
};

~/.xkb/types/hyper

default partial
xkb_types "3lvl" {
    virtual_modifiers LevelThree,Alt,Hyper;

    type "HYPER" {
        modifiers= LevelThree+Shift+Control+Alt+Hyper;

        map[Shift]= Level2;

        map[Control]= Level1;
        preserve[Control]= Control;

        map[Shift+Control]= Level2;
        preserve[Shift+Control]= Control;

        map[Alt]= Level1;
        preserve[Alt]= Alt;

        map[Shift+Alt]= Level2;
        preserve[Shift+Alt]= Alt;

        map[Control+Alt]= Level1;
        preserve[Control+Alt]= Control+Alt;

        map[Shift+Control+Alt]= Level2;
        preserve[Shift+Control+Alt]= Control+Alt;

        map[Hyper]= Level3;

        map[Shift+Hyper] = Level3;
        preserve[Shift+Hyper] = Shift;

        map[Control+Hyper] = Level3;
        preserve[Control+Hyper] = Control;

        map[Shift+Control+Hyper] = Level3;
        preserve[Shift+Control+Hyper] = Shift+Control;

        map[Alt+Hyper] = Level3;
        preserve[Alt+Hyper] = Alt;

        map[Shift+Alt+Hyper] = Level3;
        preserve[Shift+Alt+Hyper] = Shift+Alt;

        map[Control+Alt+Hyper] = Level3;
        preserve[Control+Alt+Hyper] = Control+Alt;

        map[Shift+Control+Alt+Hyper] = Level3;
        preserve[Shift+Control+Alt+Hyper] = Shift+Control+Alt;

        map[LevelThree] = Level4;

        level_name[Level1]= "Base";
        level_name[Level2]= "Shift";
        level_name[Level3]= "Hyper";
        level_name[Level4]= "AltGr";
    };
};

partial
xkb_types "4lvl" {
    virtual_modifiers Alt,Hyper;

    type "HYPER4LVL" {
        modifiers= Shift+Control+Alt+Hyper;

        map[Shift]= Level2;
        preserve[Shift]= Shift;

        map[Control]= Level1;
        preserve[Control]= Control;

        map[Shift+Control]= Level2;
        preserve[Shift+Control]= Shift+Control;

        map[Alt]= Level1;
        preserve[Alt]= Alt;

        map[Shift+Alt]= Level2;
        preserve[Shift+Alt]= Shift+Alt;

        map[Control+Alt]= Level1;
        preserve[Control+Alt]= Control+Alt;

        map[Shift+Control+Alt]= Level2;
        preserve[Shift+Control+Alt]= Shift+Alt;

        map[Hyper]= Level3;

        map[Shift+Hyper]= Level4;

        map[Control+Hyper] = Level3;
        preserve[Control+Hyper] = Control;

        map[Shift+Control+Hyper] = Level4;
        preserve[Shift+Control+Hyper] = Control;

        map[Alt+Hyper] = Level3;
        preserve[Alt+Hyper] = Alt;

        map[Shift+Alt+Hyper] = Level4;
        preserve[Shift+Alt+Hyper] = Alt;

        map[Control+Alt+Hyper] = Level3;
        preserve[Control+Alt+Hyper] = Control+Alt;

        map[Shift+Control+Alt+Hyper] = Level4;
        preserve[Shift+Control+Alt+Hyper] = Control+Alt;

        level_name[Level1]= "Normal";
        level_name[Level2]= "Shift";
        level_name[Level3]= "Hyper";
        level_name[Level4]= "Shift+Hyper";
    };
};

Last edited by sooqua (2019-08-03 02:22:36)

Offline

Board footer

Powered by FluxBB