summaryrefslogtreecommitdiffhomepage
path: root/console-tools/showkey.c
blob: 149ea64659f2f13b50fe34ac9768ab22954f77cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* vi: set sw=4 ts=4: */
/*
 * shows keys pressed. inspired by kbd package
 *
 * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */

#include "libbb.h"
#include <linux/kd.h>

// set raw tty mode
// also used by microcom
// libbb candidates?
static void xget1(struct termios *t, struct termios *oldt)
{
	tcgetattr(STDIN_FILENO, oldt);
	*t = *oldt;
	cfmakeraw(t);
}

static void xset1(struct termios *tio)
{
	int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, tio);
	if (ret) {
		bb_perror_msg("can't tcsetattr for stdin");
	}
}

/*
 * GLOBALS
 */
struct globals {
	int kbmode;
	struct termios tio, tio0;
};
#define G (*ptr_to_globals)
#define kbmode	(G.kbmode)
#define tio	(G.tio)
#define tio0	(G.tio0)
#define INIT_G() do { \
	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
} while (0)


static void signal_handler(int signo)
{
	// restore keyboard and console settings
	xset1(&tio0);
	xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode);
	// alarmed? -> exit 0
	exit(SIGALRM == signo);
}

int showkey_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int showkey_main(int argc UNUSED_PARAM, char **argv)
{
	enum {
		OPT_a = (1<<0),	// display the decimal/octal/hex values of the keys
		OPT_k = (1<<1),	// display only the interpreted keycodes (default)
		OPT_s = (1<<2),	// display only the raw scan-codes
	};

	// FIXME: aks are all mutually exclusive
	getopt32(argv, "aks");

	INIT_G();

	// get keyboard settings
	xioctl(STDIN_FILENO, KDGKBMODE, &kbmode);
	printf("kb mode was %s\n\nPress any keys. Program terminates %s\n\n",
		kbmode == K_RAW ? "RAW" :
			(kbmode == K_XLATE ? "XLATE" :
				(kbmode == K_MEDIUMRAW ? "MEDIUMRAW" :
					(kbmode == K_UNICODE ? "UNICODE" : "?UNKNOWN?")))
		, (option_mask32 & OPT_a) ? "when CTRL+D pressed" : "10s after last keypress"
	);
	// prepare for raw mode
	xget1(&tio, &tio0);
	// put stdin in raw mode
	xset1(&tio);

	if (option_mask32 & OPT_a) {
		unsigned char c;
		// just read stdin char by char
		while (1 == safe_read(STDIN_FILENO, &c, 1)) {
			printf("%3u 0%03o 0x%02x\r\n", c, c, c);
			if (04 /*CTRL-D*/ == c)
				break;
		}
	} else {
		// we should exit on any signal
		bb_signals(BB_FATAL_SIGS, signal_handler);
		// set raw keyboard mode
		xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW));

		// read and show scancodes
		while (1) {
			char buf[18];
			int i, n;
			// setup 10s watchdog
			alarm(10);
			// read scancodes
			n = read(STDIN_FILENO, buf, sizeof(buf));
			i = 0;
			while (i < n) {
				char c = buf[i];
				// show raw scancodes ordered? ->
				if (option_mask32 & OPT_s) {
					printf("0x%02x ", buf[i++]);
				// show interpreted scancodes (default) ? ->
				} else {
					int kc;
					if (i+2 < n
					 && (c & 0x7f) == 0
					 && (buf[i+1] & 0x80) != 0
					 && (buf[i+2] & 0x80) != 0
					) {
						kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f);
						i += 3;
					} else {
						kc = (c & 0x7f);
						i++;
					}
					printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press");
				}
			}
			puts("\r");
		}
	}

	// cleanup
	signal_handler(SIGALRM);

	// should never be here!
	return EXIT_SUCCESS;
}