How to Make Linux Ignore a Keyboard While Keeping It Available for My Program to Read

How to make Linux ignore a keyboard while keeping it available for my program to read?

I found the solution here where someone wanted the exact same thing in the case of a barcode reader:
https://serverfault.com/questions/385260/bind-usb-keyboard-exclusively-to-specific-application/976557#976557

SUBSYSTEM=="input", ACTION=="add", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", RUN+="/bin/sh -c 'echo remove > /sys$env{DEVPATH}/uevent'"
ACTION=="add", ATTRS{idVendor}=="xxxx", ATTRS{idProduct}=="yyyy", SYMLINK+="diykeyboard"

And then replace xxxx and yyyy by the Vendor and Product ID as found in lsusb. So in my case 1c4f and 0002:

Bus 001 Device 003: ID 1c4f:0002 SiGma Micro Keyboard TRACER Gamma Ivory

The udevadm control --reload thing didn't do it for me, I had to reboot.

Then in theory the data typed on the keyboard should be available at /dev/diykeyboard (the SYMLINK variable).

Now in my case unfortunately there are multiple events that match this vendor+product, and to match the right one I needed to add DEVPATH=="*:1.0/*", KERNEL=="event*" in the second line where it creates the SYMLINK. And then surprise it did not create the link in /dev so I had to do something dirty, create a link myself with ln:

SUBSYSTEM=="input", ACTION=="add", ATTRS{idVendor}=="1c4f", ATTRS{idProduct}=="0002", RUN+="/bin/sh -c 'echo remove > /sys$env{DEVPATH}/uevent'"
SUBSYSTEM=="input", ACTION=="add", ATTRS{idVendor}=="1c4f", ATTRS{idProduct}=="0002", DEVPATH=="*:1.0/*", KERNEL=="event*", RUN+="/bin/sh -c 'ln -sf /dev/input/$kernel /diykeyboard'"

(don't create the link in /tmp since udev happens before the mounting of /tmp at boot)

From there I can read from /diykeyboard (which usually points to /dev/input/event0) either with evtest which shows the keys typed, or directly with my program and then decoding the scancodes.

How do I 'lock the keyboard' to prevent any more keypresses being sent on X11/Linux/Gnome?

This can be done easily with a shell script using xinput :

 #!/bin/sh

do_it() {
# need error checking there. We should also restrict which device gets
# deactivated, by checking other properties.
keyboard_ids="$(xinput list | sed -rn 's/.*id=([0-9]+).*slave\s+keyboard.*/\1/p')"

for keyboard_id in $keyboard_ids; do
# 121 is "Device Active".
# use xinput watch-props $device_id to see some properties.
xinput set-int-prop $keyboard_id 121 8 $1;
done;
}
# you maybe don't want to exit in case of failure there.
do_it 0 ; sleep 5; do_it 1

This logic is easily rewritable in Python. If installing xinput is problematic, it might be a good idea to fetch the source of xinput and try to reimplement it in Python using a library like python-xlib.

Is there a way to disable a laptop's internal keyboard?

You can use xinput to float the input device under X.

  1. Execute the command xinput list to list your input devices.
  2. Locate AT Translated Set 2 keyboard and take note of its id number; this will be used to disable the keyboard. Also, take note of the number at the end, [slave keyboard (#)]; this is the id number of the master, which will be used to re-enable your keyboard.
  3. To disable the keyboard, execute the command xinput float <id#>, where <id#> is your keyboard's id number. For example, if the id was 10, then the command would be xinput float 10.
  4. To re-enable the keyboard, execute the command xinput reattach <id#> <master#>, where master is that second number we noted down. So if the number was 3, you would do xinput reattach 10 3.

Here's a demonstration:


$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ SynPS/2 Synaptics TouchPad id=11 [slave pointer (2)]
⎜ ↳ Logitech USB-PS/2 Optical Mouse id=12 [slave pointer (2)]
⎜ ↳ Logitech Unifying Device. Wireless PID:4004 id=13 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Sleep Button id=8 [slave keyboard (3)]
↳ Acer CrystalEye webcam id=9 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=10 [slave keyboard (3)]
$ xinput float 10
$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ SynPS/2 Synaptics TouchPad id=11 [slave pointer (2)]
⎜ ↳ Logitech USB-PS/2 Optical Mouse id=12 [slave pointer (2)]
⎜ ↳ Logitech Unifying Device. Wireless PID:4004 id=13 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Sleep Button id=8 [slave keyboard (3)]
↳ Acer CrystalEye webcam id=9 [slave keyboard (3)]
∼ AT Translated Set 2 keyboard id=10 [floating slave]
$ xinput reattach 10 3
$ xinput list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ SynPS/2 Synaptics TouchPad id=11 [slave pointer (2)]
⎜ ↳ Logitech USB-PS/2 Optical Mouse id=12 [slave pointer (2)]
⎜ ↳ Logitech Unifying Device. Wireless PID:4004 id=13 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
↳ Power Button id=6 [slave keyboard (3)]
↳ Video Bus id=7 [slave keyboard (3)]
↳ Sleep Button id=8 [slave keyboard (3)]
↳ Acer CrystalEye webcam id=9 [slave keyboard (3)]
↳ AT Translated Set 2 keyboard id=10 [slave keyboard (3)]

Capture characters from standard input without waiting for enter to be pressed

That's not possible in a portable manner in pure C++, because it depends too much on the terminal used that may be connected with stdin (they are usually line buffered). You can, however use a library for that:

  1. conio available with Windows compilers. Use the _getch() function to give you a character without waiting for the Enter key. I'm not a frequent Windows developer, but I've seen my classmates just include <conio.h> and use it. See conio.h at Wikipedia. It lists getch(), which is declared deprecated in Visual C++.

  2. curses available for Linux. Compatible curses implementations are available for Windows too. It has also a getch() function. (try man getch to view its manpage). See Curses at Wikipedia.

I would recommend you to use curses if you aim for cross platform compatibility. That said, I'm sure there are functions that you can use to switch off line buffering (I believe that's called "raw mode", as opposed to "cooked mode" - look into man stty). Curses would handle that for you in a portable manner, if I'm not mistaken.

resource linux script, disable/flush extra input

At the "processing line," add the following loop:

# Eat any remaining input
while read -t 1 -n 1
do
# Do nothing here
:
done

# Continue processing now that all input has been consumed...

It will add about 1 second delay to startup (more if the user is sitting there pressing keys), but otherwise does what you want.

How do I prompt for Yes/No/Cancel input in a Linux shell script?

The simplest and most widely available method to get user input at a shell prompt is the read command. The best way to illustrate its use is a simple demonstration:

while true; do
read -p "Do you wish to install this program? " yn
case $yn in
[Yy]* ) make install; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done

Another method, pointed out by Steven Huwig, is Bash's select command. Here is the same example using select:

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done

With select you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there's no need for a while true loop to retry if they give invalid input.

Also, Léa Gris demonstrated a way to make the request language agnostic in her answer. Adapting my first example to better serve multiple languages might look like this:

set -- $(locale LC_MESSAGES)
yesexpr="$1"; noexpr="$2"; yesword="$3"; noword="$4"

while true; do
read -p "Install (${yesword} / ${noword})? " yn
if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
if [[ "$yn" =~ $noexpr ]]; then exit; fi
echo "Answer ${yesword} / ${noword}."
done

Obviously other communication strings remain untranslated here (Install, Answer) which would need to be addressed in a more fully completed translation, but even a partial translation would be helpful in many cases.

Finally, please check out the excellent answer by F. Hauri.

How to kill a while loop with a keystroke?

The easiest way is to just interrupt it with the usual Ctrl-C (SIGINT).

try:
while True:
do_something()
except KeyboardInterrupt:
pass

Since Ctrl-C causes KeyboardInterrupt to be raised, just catch it outside the loop and ignore it.



Related Topics



Leave a reply



Submit