If you read something from STDIN, the read bytes are normally in ASCII format. In some cases it is useful to get the keyboard input as raw as possible. Imagine: Your program should react on the Alt or Ctrl Key with menu highlightning, or you use the fb device (or run Mode X with direct VGA register access) and you want to surpress terminal switching and Shift+PageUp/Shift+PageDown. Locking the terminal may be also done with the following code (already known from the fb article):
%assign VT_GETMODE 05601h %assign VT_SETMODE 05602h %assign VT_PROCESS 1 section .text struc tvtmode alignb 4 tvtmode_mode: resb 1 tvtmode_waitv: resb 1 tvtmode_relsig: resb 1 tvtmode_acqsig: resb 1 tvtmode_frsig: resb 1 endstruc mov dword eax, SYS_IOCTL mov dword ebx, STDIN mov dword ecx, VT_GETMODE mov dword edx, vtmode_data int byte 080h mov byte [vtmode_data + tvtmode_mode], VT_PROCESS mov dword eax, SYS_IOCTL mov dword ebx, STDIN mov dword ecx, VT_SETMODE mov dword edx, vtmode_data int byte 080h section .data vtmode_data: istruc tvtmode iend
But this doesn't solves the problem how to catch a single Alt (Ctrl, Shift or whatever) keypress and to surpress Shift+PageUp/Shift+PageDown. In raw keyboard mode such things are possible. In this mode you get directly the untranslated keycodes coming from your keyboard. It doesn't generate ASCII like codes it is far away from that. If you press a key on your keyboard two codes are send to the controller in your PC a so called make code if you press a key and a break code if you release a key. The most generated codes are 1 byte codes. It is easy to recognize which sort of code you have received: bit 7 clear = make, bit 7 set = break.
Examples:
But there are also codes longer than 1 byte, such codes are escaped with a leading 0e0h. Example: RIGHT CTRL: 0e0h 01dh (make), 0e0h 09eh (break). Three byte codes are escaped with 0e1h (there should be only one such key on your keyboard: PAUSE. This key is a little bit strange because it generates make & break codes without any delay if you press it).
The example code below will simply dump the hex code of every key to the terminal. Use ESC to exit the program.
The translation to ASCII Codes in the kernel is done via tables (the same tables are changed if you run the command loadkeys, which loads the correct keytable for your country). You can reach this tables with the ioctl's KDGKBENT and KDSKBENT. They expect a pointer to a structure of the type kbentry (see /usr/src/linux/include/linux/kd.h). If you want to get an entry you have to set the values kb_table and kb_index, if you want to set an entry kb_value must also be set. kb_index is the raw keycode, kb_table contains bits for the keys Shift, Alt, AltGr and Ctrl (it is some kind of table selector, for making things like Shift+A easier). kb_value contains the ASCII Code. For simple keyhandling this should be enough, the real key translation in the kernel seems to be a little bit more complex (think of escape sequences if you press a cursor key), in such cases kb_value contains some magic values.
The raw keyboard mode only works on local console. With the ioctl TIOCLINUX i check if the program was started on local console. Maybe this is not very clean, but it does what it should. Using the VT_GETMODE ioctl does also the trick (see the source above for ioctl number and vtmode struct):
mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, VT_GETMODE mov dword edx, vtmode_data int byte 080h test dword eax, eax js near rawkb_error ;not on local console
Like all low level accesses you should try as hard as you can to restore the original state, because if you fail the keyboard stays in raw mode and this means the local console is unusable (Ctrl+Alt+Del doesn't work, no terminal switching etc.). Therefore you should catch all signals, which can terminate your program (including SIGSEGV) with an exit handler.
short procedure for switching to raw keyboard mode:
If you find mistakes or have suggestions, mail me: karsten.scheibler@bigfoot.de
-[sample Makefile for this code]--------------------------------------------- NASM=nasm -f elf -w+orphan-labels STRIP=strip -R .note -R .comment LD=ld -s RM=rm -f .PHONY: all clean all: rawkb rawkb: rawkb.nasm ${NASM} rawkb.nasm ${LD} -e rawkb_start -o rawkb rawkb.o ${STRIP} rawkb clean: ${RM} *.bak *~ rawkb rawkb.o core -----------------------------------------------------------------------------
;**************************************************************************** ;**************************************************************************** ;* ;* USING LINUX RAW KEYBOARD MODE ;* ;* written by Karsten Scheibler, 2000-SEP-15 ;* ;**************************************************************************** ;**************************************************************************** ;you have to specify this as entry point to the linker, as done above in the ;Makefile global rawkb_start ;**************************************************************************** ;* some assign's ************************************************************ ;**************************************************************************** %assign SYS_EXIT 1 %assign SYS_READ 3 %assign SYS_WRITE 4 %assign SYS_OPEN 5 %assign SYS_CLOSE 6 %assign SYS_IOCTL 54 %assign TCGETS 05401h %assign TCSETSW 05403h %assign TIOCLINUX 0541ch %assign KDGKBMODE 04b44h %assign KDSKBMODE 04b45h %assign K_RAW 000h %assign ISTRIP 000400q %assign INLCR 000100q %assign IGNCR 000200q %assign ICRNL 000400q %assign IXON 002000q %assign IXOFF 010000q %assign ISIG 1 %assign ICANON 2 %assign ECHO 8 %assign STDIN 0 %assign STDOUT 1 %assign O_RDWR 000002q %assign NOT32 0ffffffffh %assign STATE_OPEN 001h %assign STATE_TERMIOS_SAVED 002h %assign STATE_KBMODE_SAVED 004h struc ttermios alignb 4 termios_input_flags: resd 1 termios_output_flags: resd 1 termios_control_flags: resd 1 termios_local_flags: resd 1 termios_line_discipline: resb 1 termios_control_characters: resb 64 endstruc struc tkbentry alignb 4 kbentry_table: resb 1 kbentry_index: resb 1 kbentry_value: resw 1 endstruc ;**************************************************************************** ;* rawkb_start ************************************************************** ;**************************************************************************** section .text rawkb_start: ;try to open /dev/tty, if this fails use STDIN for ;all the following operations mov dword eax, SYS_OPEN mov dword ebx, .tty_path mov dword ecx, O_RDWR int byte 080h test dword eax, eax js .no_handle mov dword [tty_handle], eax or dword [tty_state], STATE_OPEN .no_handle: ;check if we are on local console push dword 12 mov dword eax, SYS_IOCTL mov dword ebx, STDIN mov dword ecx, TIOCLINUX mov dword edx, esp int byte 080h test dword eax, eax js near rawkb_error mov dword eax, SYS_IOCTL mov dword ebx, STDOUT mov dword ecx, TIOCLINUX mov dword edx, esp int byte 080h test dword eax, eax js near rawkb_error mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, TIOCLINUX mov dword edx, esp int byte 080h test dword eax, eax js near rawkb_error pop dword eax ;save terminal state mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, TCGETS mov dword edx, tty_termios_saved int byte 080h test dword eax, eax js near rawkb_error or dword [tty_state], STATE_TERMIOS_SAVED mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, KDGKBMODE mov dword edx, tty_kbmode_saved int byte 080h test dword eax, eax js near rawkb_error or dword [tty_state], STATE_KBMODE_SAVED ;set terminal values cld mov dword ecx, ttermios_size mov dword esi, tty_termios_saved mov dword edi, tty_termios rep movsb and dword [tty_termios + termios_input_flags], (NOT32 - (ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF)) and dword [tty_termios + termios_local_flags], (NOT32 - (ECHO | ICANON | ISIG)) mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, TCSETSW mov dword edx, tty_termios int byte 080h test dword eax, eax js near rawkb_error mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, KDSKBMODE mov dword edx, K_RAW int byte 080h test dword eax, eax js near rawkb_error ;display the hex codes of all pressed keys, ESC exits .display: mov dword eax, SYS_READ mov dword ebx, STDIN mov dword ecx, character mov dword edx, 1 int byte 080h xor dword ebx, ebx xor dword ecx, ecx mov byte bl, [character] mov byte cl, bl cmp byte bl, 001h je near rawkb_end shr dword ecx, 4 and byte bl, 00fh mov dword eax, 00a680000h mov byte ah, [.hex_table + ebx] mov byte al, [.hex_table + ecx] mov dword [hex_number], eax mov dword eax, SYS_WRITE mov dword ebx, STDOUT mov dword ecx, hex_number mov dword edx, 4 int byte 080h jmp .display .tty_path: db "/dev/tty", 0 .hex_table: db "0123456789abcdef" section .data align 4 tty_handle: dd STDIN tty_state: dd 0 section .bss align 4 tty_termios_saved: resb ttermios_size tty_kbmode_saved: resd 1 tty_termios: resb ttermios_size hex_number: resd 1 character resb 1 ;**************************************************************************** ;* rawkb_restore ************************************************************ ;**************************************************************************** section .text rawkb_restore: test dword [tty_state], STATE_TERMIOS_SAVED jz .no_termios mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, TCSETSW mov dword edx, tty_termios_saved int byte 080h .no_termios: test dword [tty_state], STATE_KBMODE_SAVED jz .no_kbmode mov dword eax, SYS_IOCTL mov dword ebx, [tty_handle] mov dword ecx, KDSKBMODE mov dword edx, [tty_kbmode_saved] int byte 080h .no_kbmode: test dword [tty_state], STATE_OPEN jz .not_open mov dword eax, SYS_CLOSE mov dword ebx, [tty_handle] int byte 080h .not_open: ret ;**************************************************************************** ;* rawkb_exit *************************************************************** ;**************************************************************************** section .text rawkb_end: call rawkb_restore xor dword eax, eax mov dword ebx, eax inc dword eax int byte 080h ;**************************************************************************** ;* rawkb_error ************************************************************** ;**************************************************************************** section .text rawkb_error: call rawkb_restore xor dword eax, eax inc dword eax mov dword ebx, eax int byte 080h ;********************************************* karsten.scheibler@bigfoot.de *