2

Problem

When using certain software such as Blender, it is important to be able to have use of numpad that that the user can do things like orient themselves in the design space, but many laptops do not come with a physical numpad. This is made more complicated by the fact that the regular number bar inputs (1-9 across the top of the keyboard) actually represent different "symbols" to the computer, so often have completely different functions in these kinds of software.

Attempted Solutions

Since many laptops do not come with a numpad, a common solution on many platforms is to emulate the numpad, by for example holding down a key while using other keys on the keyboard (such as jkluio789 to represent 123456789). Many laptops implement this on a BIOS level (such as using the Fn key). However, without such a low-level implementation, implementing this emulation is very difficult.

Some solutions exist online, but they are often insufficient for use with design software (as they do not implement the correct symbol, and also require using modifier keys), or they are not explained in depth. Most solutions focus around using xkb, which is a complicated and notoriously difficult architecture to start using.

Requirements for a Good Solution

A good solution to this problem will be an emulated keypad that graphics software will accept as being true keypad input, and that is easy to use. A further limitation is the fact that Blender detects the use of modifier keys (such as Shift, Alt, Super ("Command", "Windows Key", etc.), Hyper) even when xkb is told clear modifiers, and therefore will interpret a "keypad emulation while holding a modifier key" solution as a completely different input (i.e. [Numpad1 + Alt] instead of just Numpad1). Therefore, an ideal solution will actually involve a locking mechanism (e.g. Caps Lock capitalization) instead of a holding mechanism (e.g. Shift capitalization) so that no modifiers are accidentally passed to the software.

4
  • just get an external number pad
    – jsotola
    Apr 15, 2020 at 17:56
  • 1
    Why would I spend money to buy extra hardware for my already multi-hundred dollar machine to solve a problem that can be solved in software? Extra hardware that I would have to lug around with me, fit onto my work surface (which is often my couch), and possibly have to maintain? PARTICULARLY when I've used machines that emulate numpads just fine? Many people don't want to just throw money at problems, particularly when the problem shouldn't really exist in the first place.
    – sgbrown
    Apr 15, 2020 at 18:10
  • 1
    In your case, I would brought a laptop which includes the numpad. There are cheap options, which I would use to remote work on a full fledgedwork station, if mobility was required, otherwise, I would not get a laptop.
    – Braiam
    Apr 15, 2020 at 18:42
  • That's fair, for me it just wasn't worth it. In an ideal world, I would be able to mix and match the exact kind of hardware I wanted in a laptop. However, with the budget and time constraints I had when buying my laptop, along with the requirements I had for the hardware that mattered most to me (CPU, RAM, SDD, etc), having a physical numpad was a luxury. Further, I assumed at the time that I would easily be able to emulate a numpad as I have in the past on my Macbook (this is my first main Linux machine). It ended up being more trouble than expected, but I found that this solution worked.
    – sgbrown
    Apr 15, 2020 at 18:49

1 Answer 1

4

Quick Start

If you don't care about any of the explanation (I know I can be longwinded), just follow the {bolded numbers in curly brackets} at the beginning of some paragraphs. Follow each of these steps in order, and you can probably have this implemented in a few minutes. Note that this guide assumes some amount of Unix competency (being able to create directories, create files, sudo elevating to root access, etc). Also note that root access is only required where indicated, so you do not need to use sudo unless told to.

General Description of the Solution

We will be using xkb to add a "locking" (like Caps lock) numpad emulation to Linux. I would like my keys "jkluio789" to represent the numpad representations of the numbers "123456789", as well as a few other inclusions ("m,"->"0", "-=[]"->[numpad]"-+*", "."->[numpad]"."). I will toggle this "numpad mode" by using the key combination of [Shift+Mod4+[key]] where Mod4 is the modifier code for my operating system key (also called "Command" or "Windows Key", and sometimes assigned to the modifier code of Super or Hyper), and [key] is any of the keys used in my emulated numpad (such as "j" or "["). Simple modifications to this setup should be relatively simple after reading the full solution.

To do this, we will define a custom xkb "type" file, which tells xkb how to interpret the various modifier keys we will use to initialize our keypad emulation, as well as a custom xkb "symbols" file, which tells xkb how each key we press should behave normally (Group 1), how it should behave during the numpad emulation (Group 2), and how to switch back and for between the two (Level 3 action for both groups). Finally, we will make our solution permanent by integrating it into the current xkbmap using sed each time we start a new session (so that our solution does not get erased each time xkb is updated).

Detailed Description of the Solution

Directory Structure

{1} The first thing we will do is define a directory for us to keep our various files in. Yours can look like pretty much anything, but mine looks like this

/home
  +-<username>
    +-.xkb
      +-symbols
      +-types
      +-keymap
      +-log

Type File

Once we have our directory tree, lets define the actual files in our solution. The first thing we will do is define our "type" file. This file will tell xkb how to move between "levels" (like how Shift capitalizes a letter, moving from the first level of a lowercase letter to an uppercase level of the uppercase letter). These levels are a bit difficult to grasp, particularly for native English speakers, but international keyboards use them to great effect for alternative letters and symbols, as well as diacritical marks.

We will be using it to define how we intend to indicate a change in our keys. In other words, we tell it that we expect "level 1" behavior when no modifier is pressed (usually, a standard lower case letter when in our "normal mode"), "level 2" behavior when we hold the Shift key (usually, a standard upper case letter in our "normal mode"), and "level 3" behavior uwhen we hold both Shift+Mod4 (a special case for our purposes, which we use to indicate that, when used to modify a key, the key will now switch between modes).

{2} Open a new file, which we will call togglekeypad. Copy the following codeblock into it, and save it into your types directory under \home\<username>\.xkb\types. NOTE: You may need to change all instances of Mod4 to whatever modifier your "Command"/"Windows Key" button corresponds to (you might need to experiment, see this webpage under Modifier Keys for guidance) or whatever other modifier you would like.

partial default xkb_types "togglekeypad" { // Name of this type file
        type "TOGGLEKEYPAD" { // Name of this "type"
                modifiers = Shift+Mod4; // The modifiers that this type concerns itself with
                map[Shift] = level2; // Shift brings us to level 2
                map[Mod4+Shift] = level3; // Windows key plus shift brings us to level 3
                level_name[Level1] = "Base"; // Human-readable names for each level (not really used, but convenient)
                level_name[Level2] = "Shift";
                level_name[Level3] = "Transfer";
        };
};

{3} We must also copy this file to the directory /usr/share/X11/xkb/types/. This will require root privileges, which unfortunately kind of defeats the purpose of xkb being a user-space application, but I can't seem to get setxkbmap to recognize the file without doing this. Suggestions welcome!

Symbols File

The next thing we will do it tell xkb what each key should do when modified in each of the ways we described in the types file.

We will say that we want to use two Groups in our symbols file. This means that each key has two different general behaviors that we will switch between in some way, those behaviors being the normal typing behavior, and the new numpad emulation behavior. For each key, we will say that 1) we want to use the TOGGLEKEYPAD type, 2) we will define the symbols (i.e. what the computer sees) associated with each of the physical keys in both Groups for all Levels, and 3) we will define any actions (any special things that xkb should do) associated with each key for both Groups across all Levels. This sounds rather confusing, but should make a bit more sense when look at an example.

The first key that we see in the symbols file I pasted below is the <AC07> key. This corresponds to the "J" key on most keyboards, according to the map seen here (Figure 2). For this physical key, we are saying that, in normal mode: in level 1 (no modifiers, according to our type file) it will just type "j", and in level 2 (Shift modifier) it will just type "J". In level 3, it does something special: there is no symbol associated with level 3, but there is an action, and that action is to LockGroup(group=2). In other words, change us over to our second group, our "Keypad" group. If we look on the next few lines, we see that we have more symbols and actions defined for group 2 for this same key. It says that, in level 1 (no modifiers) type no symbol, but RedirectKey(keycode=<KP1>). In other words, register this key as if we actually just pressed the <KP1> key, which corresponds to the "1" on a keypad. (Note: we could have again put NoAction() and used the symbol KP_1, which is the symbol that key <KP1> corresponds to, but I thought this would give the best compatibility). For level 2, do the same thing, but add the Shift modifier to the key. Finally, for level 3, we lock ourselves back to Group 1, "Standard" mode.

{4} Open a new file, which we will call togglekeypad_symbols. Copy the following codeblock into it, and save it into your symbols directory under \home\<username>\.xkb\symbols.

default partial
xkb_symbols "togglekeypad" {
    name[Group1]= "Standard";
    name[Group2]= "Keypad";

    key <AC07> { // J
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ j,  J, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP1>), RedirectKey(keyCode=<KP1>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AC08> { // K
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ k,  K, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP2>), RedirectKey(keyCode=<KP2>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AC09> { // L
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ l,  L, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP3>), RedirectKey(keyCode=<KP3>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD07> { // U
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ u,  U, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP4>), RedirectKey(keyCode=<KP4>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD08> { // I
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ i,  I, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP5>), RedirectKey(keyCode=<KP5>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD09> { // O
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ o,  O, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP6>), RedirectKey(keyCode=<KP6>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE07> { // 7
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 7,  ampersand, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP7>), RedirectKey(keyCode=<KP7>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE08> { // 8
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 8,  asterisk, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],     
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP8>), RedirectKey(keyCode=<KP8>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE09> { // 9
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 9,  parenleft, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP9>), RedirectKey(keyCode=<KP9>), LockGroup(group=1)]
    };
    
    // NumLock
    key <AE06> { // 6
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 6,  asciicircum, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<NMLK>), RedirectKey(keyCode=<NMLK>), LockGroup(group=1)]
    };
    
    // Bottom Row (and zero)
    key <AB07> { // M
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ m,  M, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP0>), RedirectKey(keyCode=<KP0>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE10> { // 0
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ 0,  parenright, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KP0>), RedirectKey(keyCode=<KP0>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AB09> { // .
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ period,  greater, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPDL>), RedirectKey(keyCode=<KPDL>, modifiers=Shift), LockGroup(group=1)]
    };
    
    // Arithmetic Operators
    key <AE11> { // -
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ minus,  underscore, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPSU>), RedirectKey(keyCode=<KPSU>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AE12> { // +
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ equal,  plus, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPAD>), RedirectKey(keyCode=<KPAD>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD12> { // [
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ bracketleft,  braceleft, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPDV>), RedirectKey(keyCode=<KPDV>, modifiers=Shift), LockGroup(group=1)]
    };
    
    key <AD12> { // ]
        type = "TOGGLEKEYPAD",
        symbols[Group1] = [ bracketright,  braceright, NoSymbol],
        actions[Group1] = [NoAction(), NoAction(), LockGroup(group=2)],
        
        symbols[Group2] = [NoSymbol, NoSymbol, NoSymbol],
        actions[Group2] = [RedirectKey(keyCode=<KPMU>), RedirectKey(keyCode=<KPMU>, modifiers=Shift), LockGroup(group=1)]
    };
};

Test our Keypad

{5} To test the keypad configuration as it is, open a Terminal window and type

setxkbmap -types complete+togglekeypad -print | sed -e '/xkb_symbols/s/"[[:space:]]/+togglekeypad_symbols(togglekeypad)&/' > $HOME/.xkb/keymap/customMap
xkbcomp -I$HOME/.xkb -R$HOME/.xkb keymap/customMap $DISPLAY

This will grab the current settings of our xkb map (using setxkbmap - print) while setting the types used to complete+togglekeypad (everything in the file /usr/share/X11/xkb/types/complete and also including our types file at /usr/share/X11/xkb/types/togglekeypad). It will then feed this into sed, which will add our symbols togglekeypad from our file togglekeypad_symbols into the used symbols files. Finally, we used xkbcomp to compile the new keymap.

Note that, on my machine, NumLock is presumed to be off (because there is no numpad on my machine), so the numpad keys will actually send their primary functions to the computer, i.e. Home, End, PG Up, PG Down, etc. To get numbers typed when using the emulated numpad, hold Shift. I have tried various methods to get this behavior flipped (swapping the modifers argument between levels in the symbols file, assigning a new key to emulate the NumLock key <NMLK> and toggling it), but nothing has yet worked for me. Thankfully, though, when tested in Blender, it performs exactly as expected without need for Shift being held down.

{6} If things have gone horribly wrong at this point, then don't worry, just logout/login (or at worst restart), debug, and try again. If everything is working, let's make it permanent.

Making the Solution Permanent

There are certainly some more elegant ways to make our solution persistent between sessions, but the easiest and most reliable method for me was to simply put the above commands at the end of my ~/.bashrc file. I used the solution proposed here which adds a bit of error checking, and added a bit more (so I could see any error outputs).

{7} Open the file ~/.bashrc. Add the following script to the end of it:

    # Setup custom keyboard remapping to emulate a number pad when "Shift+Cmd+numap_key" is pressed to initialize
if [ -d $HOME/.xkb/keymap ]; then
  setxkbmap -types complete+togglekeypad -print | \
    sed -e '/xkb_symbols/s/"[[:space:]]/+togglekeypad_symbols(togglekeypad)&/' > $HOME/.xkb/keymap/customMap 2> $HOME/.xkb/log/sedErrors
  xkbcomp -w0 -I$HOME/.xkb -R$HOME/.xkb keymap/customMap $DISPLAY > $HOME/.xkb/log/outputOfCommand 2>&1
fi

{8} Upon restarting, the numpad emulation should now be made permanent!

Conclusion

While the explanation is long, the method itself it relatively short. The shortfalls are that Blender requires a locking method to properly work, whereas I would have preferred a holding method instead, and also that it requires root access to get xkb to recognize our custom types file for some reason. However, overall, this appears to be working well for me. If you have any questions or suggestions, please feel free to leave them below!

4
  • 1
    This is the best answer I've ever read on all of the Internet ! Thank you @sgbrown for the time you took to explain all of this, also consider making a blog post out of this answer
    – Mouradif
    Sep 27, 2020 at 19:13
  • 1
    @KiJéy Haha, thank you so much! Yeah, I had a VERY hard time finding any resources on this stuff when I was trying to do this, so I figured I would make an answer that could kind of a bit of a lesson on xkb in general. Unfortunately, though, I don't have a blog to post this on. If you have one, or know another place I could post this, let me know; I would want credit/citation, but otherwise don't feel the need to "own" the post. I figured Stack Exchange would kind of like my blog in this case...
    – sgbrown
    Sep 28, 2020 at 21:04
  • This works on its own, but I need to use it in conjunction with /usr/share/X11/xkb/compat/mousekeys and then it's not working. I issued command setxkbmap -option keypad:pointerkeys which seems in conflict with your code. Any idea how to solve this? Apr 8 at 9:45
  • Well, I found an alternative solution by modifying /usr/share/X11/xkb/compat/mousekeys directly. And then xset led named 'Mouse Keys' does the trick. Apr 8 at 11:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .