aboutsummaryrefslogtreecommitdiff
path: root/console-tools/showkey.c
blob: 06df68bfde0adc4feb898b21a209c49d6d782677 (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
139
140
141
142
/* 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>


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)


// 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 *t)
{
	int ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, t);
	if (ret) {
		bb_perror_msg("can't tcsetattr for stdin");
	}
}

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
	};

	INIT_G();

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

	// prepare for raw mode
	xget1(&tio, &tio0);
	// put stdin in raw mode
	xset1(&tio);

#define press_keys "Press any keys, program terminates %s:\r\n\n"

	if (option_mask32 & OPT_a) {
		// just read stdin char by char
		unsigned char c;

		printf(press_keys, "on EOF (ctrl-D)");

		// read and show byte values
		while (1 == read(STDIN_FILENO, &c, 1)) {
			printf("%3u 0%03o 0x%02x\r\n", c, c, c);
			if (04 /*CTRL-D*/ == c)
				break;
		}

	} else {
		// we assume a PC keyboard
		xioctl(STDIN_FILENO, KDGKBMODE, &kbmode);
		printf("Keyboard mode was %s.\r\n\n",
			kbmode == K_RAW ? "RAW" :
				(kbmode == K_XLATE ? "XLATE" :
					(kbmode == K_MEDIUMRAW ? "MEDIUMRAW" :
						(kbmode == K_UNICODE ? "UNICODE" : "UNKNOWN")))
		);

		// set raw keyboard mode
		xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)((option_mask32 & OPT_k) ? K_MEDIUMRAW : K_RAW));

		// we should exit on any signal; signals should interrupt read
		bb_signals_recursive_norestart(BB_FATAL_SIGS, record_signo);

		// inform user that program ends after time of inactivity
		printf(press_keys, "10s after last keypress");

		// read and show scancodes
		while (!bb_got_signal) {
			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) {
				if (option_mask32 & OPT_s) {
					// show raw scancodes
					printf("0x%02x ", buf[i++]);
				} else {
					// show interpreted scancodes (default)
					char c = buf[i];
					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");
		}

		// restore keyboard mode
		xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode);
	}

	// restore console settings
	xset1(&tio0);

	return EXIT_SUCCESS;
}