Como obter acesso em nível de usuário aos pressionamentos de tecla do modificador no aplicativo do console?

5

Eu preciso ter acesso ao estado da tecla modificadora para um aplicativo de console que estou escrevendo (um editor personalizado).

Existe algum pacote / biblioteca / tudo o que fornece esse acesso?

Eu improvisei o seguinte em algum lugar, mas ele só funciona se você for root, e eu realmente não quero mexer no nível da raiz.

#include <iostream>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <errno.h>

int kbhit(void)
{
    struct termios oldt, newt;
    int ch;
    int oldf;

    tcgetattr(STDIN_FILENO, &oldt);

    newt = oldt;
    newt.c_lflag &= ~0000172 ; //~(ICANON | ECHO);

    tcsetattr(STDIN_FILENO, TCSANOW, &newt);
    oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

    ch = getchar();

    tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
    fcntl(STDIN_FILENO, F_SETFL, oldf);

    return ch;
}

enum MODKEYS
{
    SHIFT_L = 1,
    SHIFT_R = 2,
    CTRL_L = 4,
    CTRL_R = 8,
    ALT_L = 16,
    ALT_R = 32,
};

int chkmodifiers()
{
    int mods=0,keyb,mask;
    char key_map[KEY_MAX/8 + 1];    //  Create a byte array the size of the number of keys

    //event1 - got by inspecting /dev/input/... 
    FILE *kbd = fopen("/dev/input/event1", "r");
    if (kbd == NULL)
    {
        printf("(chkmodifiers) ERROR: %s\n", strerror(errno)); //permission - got to be root!
        return 0;
    }

    memset(key_map, 0, sizeof(key_map));
    ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map);    //  Fill the keymap with the current keyboard state

    keyb = key_map[KEY_LEFTSHIFT/8];
    mask = 1 << (KEY_LEFTSHIFT % 8);
    if (keyb & mask) mods += SHIFT_L;

    keyb = key_map[KEY_RIGHTSHIFT/8];
    mask = 1 << (KEY_RIGHTSHIFT % 8);
    if (keyb & mask) mods += SHIFT_R;

    keyb = key_map[KEY_LEFTCTRL/8];
    mask = 1 << (KEY_LEFTCTRL % 8);
    if (keyb & mask) mods += CTRL_L;

    keyb = key_map[KEY_RIGHTCTRL/8];
    mask = 1 << (KEY_RIGHTCTRL % 8);
    if (keyb & mask) mods += CTRL_R;

    keyb = key_map[KEY_LEFTALT/8];
    mask = 1 << (KEY_LEFTALT % 8);
    if (keyb & mask) mods += ALT_L;

    keyb = key_map[KEY_RIGHTALT/8];
    mask = 1 << (KEY_RIGHTALT % 8);
    if (keyb & mask) mods += ALT_R;

    return mods;

}


int main()
{
    puts("Press a key!");

    char ch=0;
    int n=0,m;

    while (ch != 'q')
    {
        n = kbhit();
        if (n != -1)
        {
            m = chkmodifiers();
            ch =  (char)n;
            printf("You pressed '%c' [%d]\n", ch, n);
            if ((m & SHIFT_L) == SHIFT_L) printf(" .. and ls\n");
            if ((m & SHIFT_R) == SHIFT_R) printf(" .. and rs\n");
            if ((m & CTRL_L) == CTRL_L) printf(" .. and lc\n");
            if ((m & CTRL_R) == CTRL_R) printf(" .. and rc\n");
            if ((m & ALT_L) == ALT_L) printf(" .. and la\n");
            if ((m & ALT_R) == ALT_R) printf(" .. and ra\n");

        }
    }
    return 0;
}
    
por slashmais 19.01.2012 / 08:47

2 respostas

2

Talvez dê uma olhada em libtermkey , uma biblioteca de entrada de chave de terminal que reconhece teclas especiais (como a seta e teclas de função), incluindo chaves "modificadas" como Ctrl-Left .

Outra opção pode ser melhorar a funcionalidade do charme , uma cópia mínima de ncurses.

    
por 25.01.2012 / 10:24
2

Em um terminal, sempre foi muito difícil ter esse tipo de informação. Você só pode obter um "código-chave" e, claro, isso significa coisas diferentes dependendo da combinação de SO, teclado e terminal que você usa.

Você encontrará uma lista completa desses códigos no seu SO com uma ligação para o xmodmap .

$ xmodmap -pke
keycode   9 = Escape NoSymbol Escape
keycode  10 = ampersand 1 ampersand 1 dead_caron dead_ogonek dead_caron dead_ogonek
keycode  11 = eacute 2 eacute 2 asciitilde Eacute asciitilde Eacute
[...]
keycode 244 = XF86Battery NoSymbol XF86Battery
keycode 245 = XF86Bluetooth NoSymbol XF86Bluetooth
keycode 246 = XF86WLAN NoSymbol XF86WLAN
keycode 247 =

E uma lista de todos os modificadores vale com isso:

$ xmodmap
xmodmap:  up to 4 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      
mod4        Super_L (0x85),  Super_R (0x86),  Super_L (0xce),  Hyper_L (0xcf)
mod5        ISO_Level3_Shift (0x5c),  Mode_switch (0xcb)

Existem algumas informações cruas no banco de dados terminfo ou mais polidas em ncurses API para ajudar os desenvolvedores de sistemas a superar essa confusão.

Se você puder reutilizar um código-fonte existente como

Se você só precisa de suporte moderno a linux, é possível que uma chamada para keyname seja suficiente.

    
por 23.01.2012 / 09:24