1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
18 #include <sys/ioctl.h>
19 #include <sys/types.h>
21 #include <linux/input.h>
42 #define KEY_CONTEXTMENU 127
45 static int magic_codes
[MAGIC_COUNT
] = {
46 /* the whole right part */
48 126, /* right hyper */
49 127, /* context menu */
53 ////////////////////////////////////////////////////////////////////////////////
54 static char *devName
= NULL
;
55 static int optDontDaemonize
= 0;
56 static int optMute
= 0;
57 static int optSameSound
= 0;
60 ////////////////////////////////////////////////////////////////////////////////
61 // ðÒÅÄÓÔÁ×ÌÅÎÉÅ ÕÎÉ×ÅÒÓÁÌØÎÏÇÏ ÓÏÂÙÔÉÑ ÄÌÑ ËÌÁ×ÉÁÔÕÒÙ É ÄÖÏÊÓÔÉËÁ
65 int action
; // 0: release; 1: press; 2: repeat
69 static void readKdbEvent (int fd
, UniEvent
*ev
) {
71 struct input_event kev
;
72 rb
= read(fd
, &kev
, sizeof(kev
));
73 if (rb
!= (ssize_t
)sizeof(kev
)) {
74 fprintf(stderr
, "FATAL: invalid keyboard event\n");
79 ev
->action
= kev
.value
;
83 ////////////////////////////////////////////////////////////////////////////////
84 static int openEvDev (Display
*dpy
) {
87 fprintf(stderr
, "FATAL: no input device specified\n");
90 if ((fd
= open(devName
, O_RDONLY
)) < 0) {
91 fprintf(stderr
, "FATAL: can't open input device: '%s'\n", devName
);
98 ////////////////////////////////////////////////////////////////////////////////
99 typedef int (*LoopFn
) (UniEvent
*ev
);
101 static void runMainLoop (Display
*dpy
, LoopFn fn
) {
102 int fd
= openEvDev(dpy
);
103 if (!optDontDaemonize
) {
104 if (daemon(0, 0) != 0) {
105 fprintf(stderr
, "FATAL: can't daemonize\n");
111 readKdbEvent(fd
, &ev
);
119 ////////////////////////////////////////////////////////////////////////////////
120 static void testEvents (Display
*dpy
) {
122 int cb (UniEvent
*ev
) {
123 printf("[%d]: %s %d\n", eventCount
,
124 (ev
->action
== 0 ? "up " : (ev
->action
== 1 ? "down" : (ev
->action
== 2 ? "rept" : "????"))),
129 printf("event code test; ^C for exit\n");
130 optDontDaemonize
= 1;
131 runMainLoop(dpy
, cb
);
135 ////////////////////////////////////////////////////////////////////////////////
136 static void xsleep (int ms
) {
140 if (ms
/1000 > 0) sleep(ms
/1000);
142 usleep((useconds_t
)ms
);
147 static void beep (int hz
, int waitms
) {
148 if (hz
> 10 || hz
< 20000) {
149 int fd
= open("/dev/console", O_WRONLY
);
151 ioctl(fd
, KIOCSOUND
, (int)(/*CLOCK_TICK_RATE*/1193180/hz
));
153 ioctl(fd
, KIOCSOUND
, 0);
160 static uint64_t getmtime (void) {
163 clock_gettime(CLOCK_MONOTONIC
, &tp
); //1e-9
164 res
= ((uint64_t)tp
.tv_sec
)*1000LLU+((uint64_t)tp
.tv_nsec
)/1000000LLU;
169 ////////////////////////////////////////////////////////////////////////////////
171 #define MAGIC_KEY1 KEY_RIGHTCTRL
172 #define MAGIC_KEY2 KEY_CONTEXTMENU
175 static int isMagicCode (int c
) {
176 for (unsigned f
= 0; f
< MAGIC_COUNT
; ++f
) {
177 if (magic_codes
[f
] == c
) return 1;
183 // action code: 0:not pressed; 1:just pressed; 2:repeat
190 static unsigned char pressed
[512];
191 static uint64_t downTime
= 0;
192 static Display
*maindpy
= NULL
;
195 static void resetAllMagics (void) {
196 for (unsigned f
= 0; f
< MAGIC_COUNT
; ++f
) {
197 pressed
[magic_codes
[f
]] = KEY_RELEASED
;
202 static int isOtherMagicDown (int ckmagic
) {
203 for (unsigned f
= 0; f
< MAGIC_COUNT
; ++f
) {
204 if (magic_codes
[f
] == ckmagic
) continue;
205 if (pressed
[magic_codes
[f
]]) return 1;
211 static int isSmthDown (void) {
212 for (unsigned f
= 0; f
< 512; ++f
) {
213 if (isMagicCode(f
)) continue;
214 if (pressed
[f
] != KEY_RELEASED
) return 1;
220 static int cb (UniEvent
*ev
) {
221 if (ev
->code
< 1 || ev
->code
> 511 || ev
->action
< 0 || ev
->action
> 2) return 0;
225 (ev->action == 0 ? "up " : (ev->action == 1 ? "down" : (ev->action == 2 ? "rept" : "????"))),
228 if (isMagicCode(ev
->code
)) {
229 // if something else is pressed, abort switching
230 if (isSmthDown() || isOtherMagicDown(ev
->code
)) {
234 switch (ev
->action
) {
236 if (pressed
[ev
->code
] == KEY_PRESSED
) {
238 // there were no repeats or other activity
240 //fprintf(stderr, "%lld\n", getmtime()-downTime);
241 if (getmtime()-downTime
< 400) {
242 int c
= activeLayout(maindpy
)+1;
243 if (c
>= layoutCount(maindpy
)) c
= 0;
244 setActiveLayout(maindpy
, c
);
245 if (!optMute
) beep(440+c
*80*(optSameSound
? 0 : 1), 30);
251 downTime
= getmtime();
254 if (pressed
[ev
->code
] == KEY_PRESSED
) return 0;
258 // some other keyboard activity, prevent switching
261 pressed
[ev
->code
] = ev
->action
;
266 static void mainLoop (Display
*dpy
) {
267 memset(pressed
, KEY_RELEASED
, sizeof(pressed
));
270 runMainLoop(dpy
, &cb
);
274 ////////////////////////////////////////////////////////////////////////////////
275 static int findKeyboard (void) {
276 for (int f
= 0; f
<= 31; ++f
) { // the Elder says there canst be no other numbers
279 sprintf(dev
, "/dev/input/event%d", f
);
280 fd
= open(dev
, O_RDONLY
);
283 int len
= ioctl(fd
, EVIOCGNAME(sizeof(name
)-1), name
);
287 if (strcasestr(name
, "keyboard")) {
288 devName
= strdup(dev
);
298 ////////////////////////////////////////////////////////////////////////////////
299 static void showHelp (void) {
303 "-D do not daemonize\n"
306 "-s same sound for all layouts\n"
311 ////////////////////////////////////////////////////////////////////////////////
312 int main (int argc
, char *argv
[]) {
315 for (int f
= 1; f
< argc
; ++f
) {
316 const char *arg
= argv
[f
];
317 if (arg
[0] == '-' && arg
[1] == '-') {
318 if (strcmp(arg
, "--help") == 0) { showHelp(); return 0; }
320 } else if (arg
[0] == '-') {
321 for (int c
= 1; arg
[c
]; ++c
) {
323 case 'h': showHelp(); return 0;
324 case 'l': action
|= 2; break;
325 case 'e': action
|= 1; break;
326 case 'D': optDontDaemonize
= 1; break;
327 case 'm': optMute
= 1; break;
328 case 's': optSameSound
= 1; break;
331 if (f
>= argc
) { fprintf(stderr
, "FATAL: '-d' expects device name\n"); return 1; }
332 if (devName
) free(devName
);
333 devName
= strdup(argv
[f
]);
335 default: fprintf(stderr
, "FATAL: invalid option: '%c'\n", arg
[c
]); return 1;
340 fprintf(stderr
, "FATAL: invalid argument: '%s'\n", arg
);
344 dpy
= XOpenDisplay(NULL
);
346 fprintf(stderr
, "FATAL: unable to connect to X server\n");
354 if (!findKeyboard()) { fprintf(stderr
, "FATAL: can't find keyboard device\n"); return 1; }
355 printf("keyboard device: %s\n", devName
);
357 if (action
&1) testEvents(dpy
); else mainLoop(dpy
);