15

I want to check if the user pressed down the Shift key when the program starts. (That means, press down the Shift key before the program is started) It's a simple console program, nothing related to X.

This maybe similar to the Win32 GetKeyboardState() function.

I want to know whether I can do this and how, but not any pros and cons with accessing the terminal directly.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Lenik
  • 13,946
  • 17
  • 75
  • 103

4 Answers4

34

I think there would be a way to do this. The thing is that you would have to read directly from the keyboard device. You would not be getting input from the terminal. I have the same problem. I have a program that runs (in the background) and I want to know if the user is holding down the shift key.

I believe this is possible and a place to start might be /dev/input/by-path/*-kbd.

This file does give input every time a key is pressed or reptadly if it is held down so it might be worth a look. (Try cat /dev/input/by-path/*-kbd)

If you do figure this out I would love to hear how you did it.

EDIT: I have found the solution

I have figured out how do do this. My program is as follows:

#include <stdlib.h>
#include <stdio.h>

#include <linux/input.h>

void usage ( int argc, char *argv[] )
{
    printf("Usage:\n\t%s key\n\nvalid keys are:\n\tlshift\t- Left Shift key\n" , argv[0]);

    exit(EXIT_FAILURE);
}

int main ( int argc, char *argv[], char *env[] )
{
    if ( argc != 2 )    usage(argc, argv);

    int key;

    if ( strcmp(argv[1], "lshift") == 0 )       key = KEY_LEFTSHIFT;
    else if ( strcmp(argv[1], "rshift") == 0 )  key = KEY_RIGHTSHIFT;
    else if ( strcmp(argv[1], "lalt") == 0 )    key = KEY_LEFTALT;
    else if ( strcmp(argv[1], "ralt") == 0 )    key = KEY_RIGHTALT;
    else if ( strcmp(argv[1], "lctrl") == 0 )   key = KEY_LEFTCTRL;
    else if ( strcmp(argv[1], "rctrl") == 0 )   key = KEY_RIGHTCTRL;


    FILE *kbd = fopen("/dev/input/by-path/platform-i8042-serio-0-event-kbd", "r");

    char key_map[KEY_MAX/8 + 1];    //  Create a byte array the size of the number of keys

    memset(key_map, 0, sizeof(key_map));    //  Initate the array to zero's
    ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map);    //  Fill the keymap with the current keyboard state

    int keyb = key_map[key/8];  //  The key we want (and the seven others arround it)
    int mask = 1 << (key % 8);  //  Put a one in the same column as out key state will be in;

    return !(keyb & mask);  //  Returns true if pressed otherwise false

}

The info message is lacking (I'm too lazy). But essentially the first argument is compared to a list of keys and the appropriate key identifier is used. It returns true if the key is pressed and false if not.

Please Note

You will need to change the name of they keyboard device. I do not know of a way to find the default keyboard device. (if you know I would love to hear ;) )

This works beautifully: I use it to start the autostart of Xorg if I hold down the shift key.

Community
  • 1
  • 1
Youarefunny
  • 622
  • 5
  • 10
  • 3
    This program needs root privileges, otherwise it segfaults on my machine. This is because of permissions for the keyboard device file (BTW, it's the same `platform-i8042-serio-0-event-kbd` here, so maybe at least every PC has it?). By running `sudo xxd /dev/input/by-path/platform-i8042-serio-0-event-kbd` I saw that the file "grows" by another `sizeof(keymap)` every time the keyboard state changes. – Tomasz Gandor Jul 23 '14 at 05:20
  • @Youarefunny, this is awesome. You should make a packet out of it! – Julien__ Oct 09 '19 at 20:33
7

AFAIK this cannot be done without Xlib (aka. X) with no root level permissions. Using XQueryKeymap() will do what you want. however you pointed out that X cannot be used. Regardless, opening display connection will also be required.

#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 kc2 = XKeysymToKeycode( dpy, XK_Shift_L );
    bool bShiftPressed = !!( keys_return[ kc2>>3 ] & ( 1<<(kc2&7) ) );
    printf("Shift is %spressed\n", bShiftPressed ? "" : "not ");
    XCloseDisplay(dpy);
}
Ruslan
  • 18,162
  • 8
  • 67
  • 136
MaxLZ
  • 89
  • 1
  • 4
  • For those whom the above is not working, look at https://gist.github.com/javiercantero/7753445 – Dhruv Aug 27 '23 at 12:26
5

I have found a very simple way through gtk/gdk.

int main ( int argc, char *argv[], char *env[] )
{
    gtk_init(&argc, &argv);

    GdkModifierType button_state;
    gdk_window_get_pointer(NULL, NULL, NULL, &button_state);
    if(button_state & GDK_CONTROL_MASK) {
        printf("ctrl key is pressed");
    }
}
Lyn
  • 191
  • 2
  • 9
-12

You can't.

The Shift key isn't considered as a character key, so even if you access the terminal directly, you won't be able to detect this key.

Maybe you shouldn't have to. Imagine for example that you are using a US keyboard where numbers are accessible on the top row without modifiers, and also checking for the Shift key. People with other keyboard layout may have to use Shift modifiers to access the numbers. If your program react to this Shift press, then your program is basically unusable. The same thing applies for other modifier keys : you may detect some of them only after a normal character key is pressed. Or worse, they may need to use the Shift key to use 'enter' to run your program.

Also, what Shift key do you want to monitor? the one on the local machine, or the one where the user is? remember that SSH exists and is commonly used to access a pseudoterminal remotely.

If you are root and want to monitor the Shift key on the local machine, you can read the evdev devices for events about the Shift key. But this is only possible because of automatic key repeating, so you won't detect a Shift key that is pressed right before running your program, but only a few second before.

Of course you can't do that on the remote machine, that would be a security flaw.

And anyway, why would you want to do that? Wouldn't an X application be the right thing to do in your case?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BatchyX
  • 4,986
  • 2
  • 18
  • 17
  • 1
    It's a general purpose command invoker, while _SHIFT_ key is held, the invoker will enter into configuration mode. Though there's other way to do this, for example a command line `--conf` option, well, knowing it's impossible is enough, I'll try it other way. – Lenik Sep 06 '10 at 14:27
  • 10
    it is generally bad form to start an answer with "you can't" because usually that is a sign of the answerer's invalidating the asker's question alltogether. Furthermore, It is really unfair to assume you have the right to give anyone permission to do something. Even worse, if someone else comes back and shows baltently how you "can", it makes you look really... bad. Anyways, I've written comments like these before. It isn't really a new concept. And, I didn't write an answer because I DON'T KNOW, a little humility can go a long long way ;) – osirisgothra Aug 08 '15 at 17:38
  • 2
    [...]Maybe you shouldn't have to. [...] If your program react to this shift press, then your program is basically unusable.[...] Except that this is only valid for GUI processing. For everything else, "if your program doesn't react to this shift press, then it's basically unusable." ie. camera controls, image editing applications, etc. not to mention processing keys like ctrl or alt or esc. In short: nobody asked for your opinion based on your limited experience made evident by sentences such as "why would you want to do that?" and "you can't". – Pablo Ariel Sep 06 '18 at 20:11
  • "why would you want to check keyboard state?" Strange thing to ask while typing on a keyboard – Jonathan Nov 06 '22 at 18:53