VT switching now uses correct keys.
[mesa.git] / src / glut / fbdev / input.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5
4 * Copyright (C) 1995-2006 Brian Paul
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22 * Library for glut using mesa fbdev driver
23 *
24 * Written by Sean D'Epagnier (c) 2006
25 */
26
27 #include <errno.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <termios.h>
35 #include <inttypes.h>
36
37 #include <sys/ioctl.h>
38 #include <sys/poll.h>
39 #include <sys/kd.h>
40
41 #include <linux/keyboard.h>
42 #include <linux/fb.h>
43 #include <linux/vt.h>
44
45 #include <GL/glut.h>
46
47 #include "internal.h"
48
49 #define MOUSEDEV "/dev/gpmdata"
50
51 #ifdef HAVE_GPM
52 #include <gpm.h>
53 int GpmMouse;
54 #endif
55
56 int CurrentVT;
57 int ConsoleFD = -1;
58
59 int KeyboardModifiers;
60
61 int MouseX, MouseY;
62 int NumMouseButtons;
63
64 double MouseSpeed = 0;
65
66 int KeyRepeatMode = GLUT_KEY_REPEAT_DEFAULT;
67
68 /* only display the mouse if there is a registered callback for it */
69 int MouseEnabled = 0;
70
71 static int OldKDMode = -1;
72 static int OldMode = KD_TEXT;
73 static struct vt_mode OldVTMode;
74 static struct termios OldTermios;
75
76 static int KeyboardLedState;
77
78 static int MouseFD;
79
80 static int kbdpipe[2];
81
82 #define MODIFIER(mod) \
83 KeyboardModifiers = release ? KeyboardModifiers & ~mod \
84 : KeyboardModifiers | mod;
85
86 /* signal handler attached to SIGIO on keyboard input, vt
87 switching and modifiers is handled in the signal handler
88 other keypresses read from a pipe that leaves the handler
89 if a program locks up the glut loop, you can still switch
90 vts and kill it without Alt-SysRq hack */
91 static void KeyboardHandler(int sig)
92 {
93 int release, labelval;
94 unsigned char code;
95 struct kbentry entry;
96 static int lalt; /* only left alt does vt switch */
97
98 if(read(ConsoleFD, &code, 1) != 1)
99 return;
100
101 release = code & 0x80;
102
103 entry.kb_index = code & 0x7F;
104 entry.kb_table = 0;
105
106 if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
107 sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
108 exit(0);
109 }
110
111 labelval = entry.kb_value;
112
113 switch(labelval) {
114 case K_SHIFT:
115 case K_SHIFTL:
116 MODIFIER(GLUT_ACTIVE_SHIFT);
117 return;
118 case K_CTRL:
119 MODIFIER(GLUT_ACTIVE_CTRL);
120 return;
121 case K_ALT:
122 lalt = !release;
123 case K_ALTGR:
124 MODIFIER(GLUT_ACTIVE_ALT);
125 return;
126 }
127
128 if(lalt && !release) {
129 /* VT switch, we must do it */
130 int vt = -1;
131 struct vt_stat st;
132 if(labelval >= K_F1 && labelval <= K_F12)
133 vt = labelval - K_F1 + 1;
134
135 if(labelval == K_LEFT)
136 if(ioctl(ConsoleFD, VT_GETSTATE, &st) >= 0)
137 vt = st.v_active - 1;
138
139 if(labelval == K_RIGHT)
140 if(ioctl(ConsoleFD, VT_GETSTATE, &st) >= 0)
141 vt = st.v_active - 1;
142
143 if(vt != -1) {
144 if(Swapping)
145 VTSwitch = vt;
146 else
147 if(ioctl(ConsoleFD, VT_ACTIVATE, vt) < 0)
148 sprintf(exiterror, "Error switching console\n");
149 return;
150 }
151 }
152 write(kbdpipe[1], &code, 1);
153 }
154
155 static void LedModifier(int led, int release)
156 {
157 static int releaseflag = K_CAPS | K_NUM | K_HOLD;
158 if(release)
159 releaseflag |= led;
160 else
161 if(releaseflag & led) {
162 KeyboardLedState ^= led;
163 releaseflag &= ~led;
164 }
165 ioctl(ConsoleFD, KDSKBLED, KeyboardLedState);
166 ioctl(ConsoleFD, KDSETLED, 0x80);
167 }
168
169 #define READKEY read(kbdpipe[0], &code, 1)
170 static int ReadKey(void)
171 {
172 int release, labelval, labelvalnoshift;
173 unsigned char code;
174 int specialkey = 0;
175 struct kbentry entry;
176
177 if(READKEY != 1)
178 return 0;
179 if(code == 0)
180 return 0;
181
182 /* stdin input escape code based */
183 if(ConsoleFD == 0) {
184 KeyboardModifiers = 0;
185 altset:
186 if(code == 27 && READKEY == 1) {
187 switch(code) {
188 case 79: /* function key */
189 READKEY;
190 if(code == 50) {
191 READKEY;
192 shiftfunc:
193 KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
194 specialkey = GLUT_KEY_F1 + code - 53;
195 READKEY;
196 } else {
197 READKEY;
198 specialkey = GLUT_KEY_F1 + code - 80;
199 }
200 break;
201 case 91:
202 READKEY;
203 switch(code) {
204 case 68:
205 specialkey = GLUT_KEY_LEFT; break;
206 case 65:
207 specialkey = GLUT_KEY_UP; break;
208 case 67:
209 specialkey = GLUT_KEY_RIGHT; break;
210 case 66:
211 specialkey = GLUT_KEY_DOWN; break;
212 case 53:
213 specialkey = GLUT_KEY_PAGE_UP; READKEY; break;
214 case 54:
215 specialkey = GLUT_KEY_PAGE_DOWN; READKEY; break;
216 case 49:
217 specialkey = GLUT_KEY_HOME; READKEY; break;
218 case 52:
219 specialkey = GLUT_KEY_END; READKEY; break;
220 case 50:
221 READKEY;
222 if(code != 126)
223 goto shiftfunc;
224 specialkey = GLUT_KEY_INSERT;
225 break;
226 case 51:
227 code = '\b'; goto stdkey;
228 case 91:
229 READKEY;
230 specialkey = GLUT_KEY_F1 + code - 65;
231 break;
232 default:
233 return 0;
234 }
235 break;
236 default:
237 KeyboardModifiers |= GLUT_ACTIVE_ALT;
238 goto altset;
239 }
240 }
241 stdkey:
242 if(specialkey) {
243 if(SpecialFunc)
244 SpecialFunc(specialkey, MouseX, MouseY);
245 } else {
246 if(code >= 1 && code <= 26 && code != '\r') {
247 KeyboardModifiers |= GLUT_ACTIVE_CTRL;
248 code += 'a' - 1;
249 }
250 if((code >= 43 && code <= 34) || (code == 60)
251 || (code >= 62 && code <= 90) || (code == 94)
252 || (code == 95) || (code >= 123 && code <= 126))
253 KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
254
255 if(KeyboardFunc)
256 KeyboardFunc(code, MouseX, MouseY);
257 }
258 return 1;
259 }
260
261 /* linux kbd reading */
262 release = code & 0x80;
263 code &= 0x7F;
264
265 if(KeyRepeatMode == GLUT_KEY_REPEAT_OFF) {
266 static char keystates[128];
267 if(release)
268 keystates[code] = 0;
269 else {
270 if(keystates[code])
271 return 1;
272 keystates[code] = 1;
273 }
274 }
275
276 entry.kb_index = code;
277 entry.kb_table = 0;
278
279 if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
280 sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
281 exit(0);
282 }
283
284 labelvalnoshift = entry.kb_value;
285
286 if(KeyboardModifiers & GLUT_ACTIVE_SHIFT)
287 entry.kb_table |= K_SHIFTTAB;
288
289 if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
290 sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
291 exit(0);
292 }
293
294 labelval = entry.kb_value;
295
296 switch(labelvalnoshift) {
297 case K_CAPS:
298 LedModifier(LED_CAP, release);
299 return 0;
300 case K_NUM:
301 LedModifier(LED_NUM, release);
302 return 0;
303 case K_HOLD: /* scroll lock suspends glut */
304 LedModifier(LED_SCR, release);
305 while(KeyboardLedState & LED_SCR) {
306 usleep(10000);
307 ReadKey();
308 }
309 return 0;
310 }
311
312 /* we could queue keypresses here */
313 if(KeyboardLedState & LED_SCR)
314 return 0;
315
316 if(labelval >= K_F1 && labelval <= K_F12)
317 specialkey = GLUT_KEY_F1 + labelval - K_F1;
318 else
319 switch(labelvalnoshift) {
320 case K_LEFT:
321 specialkey = GLUT_KEY_LEFT; break;
322 case K_UP:
323 specialkey = GLUT_KEY_UP; break;
324 case K_RIGHT:
325 specialkey = GLUT_KEY_RIGHT; break;
326 case K_DOWN:
327 specialkey = GLUT_KEY_DOWN; break;
328 case K_PGUP:
329 specialkey = GLUT_KEY_PAGE_UP; break;
330 case K_PGDN:
331 specialkey = GLUT_KEY_PAGE_DOWN; break;
332 case K_FIND:
333 specialkey = GLUT_KEY_HOME; break;
334 case K_SELECT:
335 specialkey = GLUT_KEY_END; break;
336 case K_INSERT:
337 specialkey = GLUT_KEY_INSERT; break;
338 case 127:
339 labelval = '\b'; break;
340 case K_ENTER:
341 case K_ENTER - 1: /* keypad enter */
342 labelval = '\n'; break;
343 }
344
345 /* dispatch callback */
346 if(specialkey) {
347 if(release) {
348 if(SpecialUpFunc)
349 SpecialUpFunc(specialkey, MouseX, MouseY);
350 } else
351 if(SpecialFunc)
352 SpecialFunc(specialkey, MouseX, MouseY);
353 } else {
354 char c = labelval;
355
356 if(KeyboardLedState & LED_CAP) {
357 if(c >= 'A' && c <= 'Z')
358 c += 'a' - 'A';
359 else
360 if(c >= 'a' && c <= 'z')
361 c += 'A' - 'a';
362 }
363 if(release) {
364 if(KeyboardUpFunc)
365 KeyboardUpFunc(c, MouseX, MouseY);
366 } else
367 if(KeyboardFunc)
368 KeyboardFunc(c, MouseX, MouseY);
369 }
370 return 1;
371 }
372
373 void glutIgnoreKeyRepeat(int ignore)
374 {
375 KeyRepeatMode = ignore ? GLUT_KEY_REPEAT_OFF : GLUT_KEY_REPEAT_ON;
376 }
377
378 void glutSetKeyRepeat(int repeatMode)
379 {
380 KeyRepeatMode = repeatMode;
381 }
382
383 void glutForceJoystickFunc(void)
384 {
385 }
386
387 static void HandleMousePress(int button, int pressed)
388 {
389 if(TryMenu(button, pressed))
390 return;
391
392 if(MouseFunc)
393 MouseFunc(button, pressed ? GLUT_DOWN : GLUT_UP, MouseX, MouseY);
394 }
395
396 static int ReadMouse(void)
397 {
398 int l, r, m;
399 static int ll, lm, lr;
400 signed char dx, dy;
401
402 #ifdef HAVE_GPM
403 if(GpmMouse) {
404 Gpm_Event event;
405 struct pollfd pfd;
406 pfd.fd = gpm_fd;
407 pfd.events = POLLIN;
408 if(poll(&pfd, 1, 1) != 1)
409 return 0;
410
411 if(Gpm_GetEvent(&event) != 1)
412 return 0;
413
414 l = event.buttons & GPM_B_LEFT;
415 m = event.buttons & GPM_B_MIDDLE;
416 r = event.buttons & GPM_B_RIGHT;
417
418 /* gpm is weird in that it gives a button number when the button
419 is released, with type set to GPM_UP, this is only a problem
420 if it is the last button released */
421
422 if(event.type & GPM_UP)
423 if(event.buttons == GPM_B_LEFT || event.buttons == GPM_B_MIDDLE ||
424 event.buttons == GPM_B_RIGHT || event.buttons == GPM_B_FOURTH)
425 l = m = r = 0;
426
427 dx = event.dx;
428 dy = event.dy;
429 } else
430 #endif
431 {
432 char data[4];
433
434 if(MouseFD == -1)
435 return 0;
436
437 if(fcntl(MouseFD, F_SETFL, O_NONBLOCK) == -1) {
438 close(MouseFD);
439 MouseFD = -1;
440 return 0;
441 }
442
443 if(read(MouseFD, data, 4) != 4)
444 return 0;
445
446 l = ((data[0] & 0x20) >> 3);
447 m = ((data[3] & 0x10) >> 3);
448 r = ((data[0] & 0x10) >> 4);
449
450 dx = (((data[0] & 0x03) << 6) | (data[1] & 0x3F));
451 dy = (((data[0] & 0x0C) << 4) | (data[2] & 0x3F));
452 }
453
454 MouseX += dx * MouseSpeed;
455 if(MouseX < 0)
456 MouseX = 0;
457 else
458 if(MouseX >= VarInfo.xres)
459 MouseX = VarInfo.xres - 1;
460
461 MouseY += dy * MouseSpeed;
462 if(MouseY < 0)
463 MouseY = 0;
464 else
465 if(MouseY >= VarInfo.yres)
466 MouseY = VarInfo.yres - 1;
467
468 if(l != ll)
469 HandleMousePress(GLUT_LEFT_BUTTON, l);
470 if(m != lm)
471 HandleMousePress(GLUT_MIDDLE_BUTTON, m);
472 if(r != lr)
473 HandleMousePress(GLUT_RIGHT_BUTTON, r);
474
475 ll = l, lm = m, lr = r;
476
477 if(dx || dy) {
478 if(l || m || r) {
479 if(MotionFunc)
480 MotionFunc(MouseX, MouseY);
481 } else
482 if(PassiveMotionFunc)
483 PassiveMotionFunc(MouseX, MouseY);
484
485 EraseCursor();
486
487 if(ActiveMenu)
488 Redisplay = 1;
489 else
490 SwapCursor();
491 }
492
493 return 1;
494 }
495
496 void ReceiveInput(void)
497 {
498 if(ConsoleFD != -1)
499 while(ReadKey());
500
501 if(MouseEnabled)
502 while(ReadMouse());
503 }
504
505 static void VTSwitchHandler(int sig)
506 {
507 struct vt_stat st;
508 switch(sig) {
509 case SIGUSR1:
510 ioctl(ConsoleFD, VT_RELDISP, 1);
511 Active = 0;
512 #ifdef MULTIHEAD
513 VisiblePoll = 1;
514 TestVisible();
515 #else
516 VisibleSwitch = 1;
517 Visible = 0;
518 #endif
519 break;
520 case SIGUSR2:
521 ioctl(ConsoleFD, VT_GETSTATE, &st);
522 if(st.v_active)
523 ioctl(ConsoleFD, VT_RELDISP, VT_ACKACQ);
524
525 /* this is a hack to turn the cursor off */
526 ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo);
527
528 RestoreColorMap();
529
530 Active = 1;
531 Visible = 1;
532 VisibleSwitch = 1;
533
534 Redisplay = 1;
535
536 break;
537 }
538 }
539
540 void InitializeVT(int usestdin)
541 {
542 struct termios tio;
543 struct vt_mode vt;
544 char console[128];
545
546 signal(SIGIO, SIG_IGN);
547
548 /* save old terminos settings */
549 if (tcgetattr(0, &OldTermios) < 0) {
550 sprintf(exiterror, "tcgetattr failed\n");
551 exit(0);
552 }
553
554 tio = OldTermios;
555
556 /* terminos settings for straight-through mode */
557 tio.c_lflag &= ~(ICANON | ECHO | ISIG);
558 tio.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
559 tio.c_iflag |= IGNBRK;
560
561 tio.c_cc[VMIN] = 0;
562 tio.c_cc[VTIME] = 0;
563
564 if (tcsetattr(0, TCSANOW, &tio) < 0) {
565 sprintf(exiterror, "tcsetattr failed\n");
566 exit(0);
567 }
568
569 if(fcntl(0, F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
570 sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
571 exit(0);
572 }
573
574 Active = 1;
575
576 if(usestdin) {
577 ConsoleFD = 0;
578 return;
579 }
580
581 /* detect the current vt if it was not specified */
582 if(CurrentVT == 0) {
583 int fd = open("/dev/tty", O_RDWR | O_NDELAY, 0);
584 struct vt_stat st;
585 if(fd == -1) {
586 sprintf(exiterror, "Failed to open /dev/tty\n");
587 exit(0);
588 }
589 if(ioctl(fd, VT_GETSTATE, &st) == -1) {
590 fprintf(stderr, "Could not detect current vt, specify with -vt\n");
591 fprintf(stderr, "Defaulting to stdin input\n");
592 ConsoleFD = 0;
593 close(fd);
594 return;
595 } else
596 CurrentVT = st.v_active;
597
598 close(fd);
599 }
600
601 /* open the console tty */
602 sprintf(console, "/dev/tty%d", CurrentVT);
603 ConsoleFD = open(console, O_RDWR | O_NDELAY, 0);
604 if (ConsoleFD < 0) {
605 sprintf(exiterror, "error couldn't open %s,"
606 " defaulting to stdin \n", console);
607 ConsoleFD = 0;
608 return;
609 }
610
611 signal(SIGUSR1, VTSwitchHandler);
612 signal(SIGUSR2, VTSwitchHandler);
613
614 if (ioctl(ConsoleFD, VT_GETMODE, &OldVTMode) < 0) {
615 sprintf(exiterror,"Failed to grab %s, defaulting to stdin\n", console);
616 close(ConsoleFD);
617 ConsoleFD = 0;
618 return;
619 }
620
621 vt = OldVTMode;
622
623 vt.mode = VT_PROCESS;
624 vt.waitv = 0;
625 vt.relsig = SIGUSR1;
626 vt.acqsig = SIGUSR2;
627 if (ioctl(ConsoleFD, VT_SETMODE, &vt) < 0) {
628 sprintf(exiterror, "error: ioctl(VT_SETMODE) failed: %s\n",
629 strerror(errno));
630 close(ConsoleFD);
631 ConsoleFD = 0;
632 exit(1);
633 }
634
635 if (ioctl(ConsoleFD, KDGKBMODE, &OldKDMode) < 0) {
636 sprintf(exiterror, "Warning: ioctl KDGKBMODE failed!\n");
637 OldKDMode = K_XLATE;
638 }
639
640 /* use SIGIO so VT switching can work if the program is locked */
641 signal(SIGIO, KeyboardHandler);
642
643 pipe(kbdpipe);
644
645 if(fcntl(kbdpipe[0], F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
646 sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
647 exit(0);
648 }
649
650 fcntl(0, F_SETOWN, getpid());
651
652 if(ioctl(ConsoleFD, KDGETMODE, &OldMode) < 0)
653 sprintf(exiterror, "Warning: Failed to get terminal mode\n");
654
655 #ifdef HAVE_GPM
656 if(!GpmMouse)
657 #endif
658 if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0)
659 sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
660
661 if (ioctl(ConsoleFD, KDSKBMODE, K_MEDIUMRAW) < 0) {
662 sprintf(exiterror, "ioctl KDSKBMODE failed!\n");
663 tcsetattr(0, TCSANOW, &OldTermios);
664 exit(0);
665 }
666
667 if( ioctl(ConsoleFD, KDGKBLED, &KeyboardLedState) < 0) {
668 sprintf(exiterror, "ioctl KDGKBLED failed!\n");
669 exit(0);
670 }
671 }
672
673 void RestoreVT(void)
674 {
675 if(ConsoleFD < 0)
676 return;
677
678 if (tcsetattr(0, TCSANOW, &OldTermios) < 0)
679 fprintf(stderr, "tcsetattr failed\n");
680
681 /* setting the mode to text from graphics restores the colormap*/
682 if(
683 #ifdef HAVE_GPM
684 GpmMouse ||
685 #endif
686 ConsoleFD == 0)
687 if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0) {
688 sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
689 goto skipioctl; /* no need to fail twice */
690 }
691
692 if(ioctl(ConsoleFD, KDSETMODE, OldMode) < 0)
693 fprintf(stderr, "ioctl KDSETMODE failed!\n");
694
695 skipioctl:
696
697 if(ConsoleFD == 0)
698 return;
699
700 /* restore keyboard state */
701 if (ioctl(ConsoleFD, VT_SETMODE, &OldVTMode) < 0)
702 fprintf(stderr, "Failed to set vtmode\n");
703
704 if (ioctl(ConsoleFD, KDSKBMODE, OldKDMode) < 0)
705 fprintf(stderr, "ioctl KDSKBMODE failed!\n");
706
707 close(ConsoleFD);
708 }
709
710 void InitializeMouse(void)
711 {
712 #ifdef HAVE_GPM
713 if(!GpmMouse)
714 #endif
715 {
716 const char *mousedev = getenv("MOUSE");
717 if(!mousedev)
718 mousedev = MOUSEDEV;
719 if((MouseFD = open(mousedev, O_RDONLY)) >= 0) {
720 if(!MouseSpeed)
721 MouseSpeed = 1;
722 NumMouseButtons = 3;
723 return;
724 }
725 }
726 #ifdef HAVE_GPM
727 {
728 Gpm_Connect conn;
729 int c;
730 conn.eventMask = ~0; /* Want to know about all the events */
731 conn.defaultMask = 0; /* don't handle anything by default */
732 conn.minMod = 0; /* want everything */
733 conn.maxMod = ~0; /* all modifiers included */
734 if(Gpm_Open(&conn, 0) != -1) {
735 if(!MouseSpeed)
736 MouseSpeed = 8;
737 NumMouseButtons = 3;
738 return;
739 }
740 fprintf(stderr, "Cannot open gpmctl.\n");
741 }
742 #endif
743 fprintf(stderr,"Cannot open %s.\n"
744 "Continuing without Mouse\n", MOUSEDEV);
745 }
746
747 void CloseMouse(void)
748 {
749 #ifdef HAVE_GPM
750 if(GpmMouse) {
751 if(NumMouseButtons)
752 Gpm_Close();
753 } else
754 #endif
755 if(MouseFD >= 0)
756 close(MouseFD);
757 }