b1362245f2f176d264803d3ac238b73e1a05c15e
[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';
228 goto stdkey;
229 case 91:
230 READKEY;
231 specialkey = GLUT_KEY_F1 + code - 65;
232 break;
233 default:
234 return 0;
235 }
236 break;
237 default:
238 KeyboardModifiers |= GLUT_ACTIVE_ALT;
239 goto altset;
240 }
241 }
242 stdkey:
243 if(specialkey) {
244 if(SpecialFunc)
245 SpecialFunc(specialkey, MouseX, MouseY);
246 } else {
247 if(code >= 1 && code <= 26 && code != '\r') {
248 KeyboardModifiers |= GLUT_ACTIVE_CTRL;
249 code += 'a' - 1;
250 }
251 if((code >= 43 && code <= 34) || (code == 60)
252 || (code >= 62 && code <= 90) || (code == 94)
253 || (code == 95) || (code >= 123 && code <= 126))
254 KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
255
256 if(KeyboardFunc)
257 KeyboardFunc(code, MouseX, MouseY);
258 }
259 return 1;
260 }
261
262 /* linux kbd reading */
263 release = code & 0x80;
264 code &= 0x7F;
265
266 if(KeyRepeatMode == GLUT_KEY_REPEAT_OFF) {
267 static char keystates[128];
268 if(release)
269 keystates[code] = 0;
270 else {
271 if(keystates[code])
272 return 1;
273 keystates[code] = 1;
274 }
275 }
276
277 entry.kb_index = code;
278 entry.kb_table = 0;
279
280 if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
281 sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
282 exit(0);
283 }
284
285 labelvalnoshift = entry.kb_value;
286
287 if(KeyboardModifiers & GLUT_ACTIVE_SHIFT)
288 entry.kb_table |= K_SHIFTTAB;
289
290 if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
291 sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
292 exit(0);
293 }
294
295 labelval = entry.kb_value;
296
297 switch(labelvalnoshift) {
298 case K_CAPS:
299 LedModifier(LED_CAP, release);
300 return 0;
301 case K_NUM:
302 LedModifier(LED_NUM, release);
303 return 0;
304 case K_HOLD: /* scroll lock suspends glut */
305 LedModifier(LED_SCR, release);
306 while(KeyboardLedState & LED_SCR) {
307 usleep(10000);
308 ReadKey();
309 }
310 return 0;
311 }
312
313 /* we could queue keypresses here */
314 if(KeyboardLedState & LED_SCR)
315 return 0;
316
317 if(labelvalnoshift >= K_F1 && labelvalnoshift <= K_F12)
318 specialkey = GLUT_KEY_F1 + labelvalnoshift - K_F1;
319 else
320 switch(labelvalnoshift) {
321 case K_LEFT:
322 specialkey = GLUT_KEY_LEFT; break;
323 case K_UP:
324 specialkey = GLUT_KEY_UP; break;
325 case K_RIGHT:
326 specialkey = GLUT_KEY_RIGHT; break;
327 case K_DOWN:
328 specialkey = GLUT_KEY_DOWN; break;
329 case K_PGUP:
330 specialkey = GLUT_KEY_PAGE_UP; break;
331 case K_PGDN:
332 specialkey = GLUT_KEY_PAGE_DOWN; break;
333 case K_FIND:
334 specialkey = GLUT_KEY_HOME; break;
335 case K_SELECT:
336 specialkey = GLUT_KEY_END; break;
337 case K_INSERT:
338 specialkey = GLUT_KEY_INSERT; break;
339 case K_REMOVE:
340 labelval = '\b';
341 break;
342 case K_ENTER:
343 case K_ENTER - 1: /* keypad enter */
344 labelval = '\n'; break;
345 }
346
347 /* dispatch callback */
348 if(specialkey) {
349 if(release) {
350 if(SpecialUpFunc)
351 SpecialUpFunc(specialkey, MouseX, MouseY);
352 } else
353 if(SpecialFunc)
354 SpecialFunc(specialkey, MouseX, MouseY);
355 } else {
356 char c = labelval;
357
358 if(KeyboardLedState & LED_CAP) {
359 if(c >= 'A' && c <= 'Z')
360 c += 'a' - 'A';
361 else
362 if(c >= 'a' && c <= 'z')
363 c += 'A' - 'a';
364 }
365 if(release) {
366 if(KeyboardUpFunc)
367 KeyboardUpFunc(c, MouseX, MouseY);
368 } else
369 if(KeyboardFunc)
370 KeyboardFunc(c, MouseX, MouseY);
371 }
372 return 1;
373 }
374
375 void glutIgnoreKeyRepeat(int ignore)
376 {
377 KeyRepeatMode = ignore ? GLUT_KEY_REPEAT_OFF : GLUT_KEY_REPEAT_ON;
378 }
379
380 void glutSetKeyRepeat(int repeatMode)
381 {
382 KeyRepeatMode = repeatMode;
383 }
384
385 void glutForceJoystickFunc(void)
386 {
387 }
388
389 static void HandleMousePress(int button, int pressed)
390 {
391 if(TryMenu(button, pressed))
392 return;
393
394 if(MouseFunc)
395 MouseFunc(button, pressed ? GLUT_DOWN : GLUT_UP, MouseX, MouseY);
396 }
397
398 static int ReadMouse(void)
399 {
400 int l, r, m;
401 static int ll, lm, lr;
402 signed char dx, dy;
403
404 #ifdef HAVE_GPM
405 if(GpmMouse) {
406 Gpm_Event event;
407 struct pollfd pfd;
408 pfd.fd = gpm_fd;
409 pfd.events = POLLIN;
410 if(poll(&pfd, 1, 1) != 1)
411 return 0;
412
413 if(Gpm_GetEvent(&event) != 1)
414 return 0;
415
416 l = event.buttons & GPM_B_LEFT;
417 m = event.buttons & GPM_B_MIDDLE;
418 r = event.buttons & GPM_B_RIGHT;
419
420 /* gpm is weird in that it gives a button number when the button
421 is released, with type set to GPM_UP, this is only a problem
422 if it is the last button released */
423
424 if(event.type & GPM_UP)
425 if(event.buttons == GPM_B_LEFT || event.buttons == GPM_B_MIDDLE ||
426 event.buttons == GPM_B_RIGHT || event.buttons == GPM_B_FOURTH)
427 l = m = r = 0;
428
429 dx = event.dx;
430 dy = event.dy;
431 } else
432 #endif
433 {
434 char data[4];
435
436 if(MouseFD == -1)
437 return 0;
438
439 if(fcntl(MouseFD, F_SETFL, O_NONBLOCK) == -1) {
440 close(MouseFD);
441 MouseFD = -1;
442 return 0;
443 }
444
445 if(read(MouseFD, data, 4) != 4)
446 return 0;
447
448 l = ((data[0] & 0x20) >> 3);
449 m = ((data[3] & 0x10) >> 3);
450 r = ((data[0] & 0x10) >> 4);
451
452 dx = (((data[0] & 0x03) << 6) | (data[1] & 0x3F));
453 dy = (((data[0] & 0x0C) << 4) | (data[2] & 0x3F));
454 }
455
456 MouseX += dx * MouseSpeed;
457 if(MouseX < 0)
458 MouseX = 0;
459 else
460 if(MouseX >= VarInfo.xres)
461 MouseX = VarInfo.xres - 1;
462
463 MouseY += dy * MouseSpeed;
464 if(MouseY < 0)
465 MouseY = 0;
466 else
467 if(MouseY >= VarInfo.yres)
468 MouseY = VarInfo.yres - 1;
469
470 if(l != ll)
471 HandleMousePress(GLUT_LEFT_BUTTON, l);
472 if(m != lm)
473 HandleMousePress(GLUT_MIDDLE_BUTTON, m);
474 if(r != lr)
475 HandleMousePress(GLUT_RIGHT_BUTTON, r);
476
477 ll = l, lm = m, lr = r;
478
479 if(dx || dy) {
480 if(l || m || r) {
481 if(MotionFunc)
482 MotionFunc(MouseX, MouseY);
483 } else
484 if(PassiveMotionFunc)
485 PassiveMotionFunc(MouseX, MouseY);
486
487 EraseCursor();
488
489 if(ActiveMenu)
490 Redisplay = 1;
491 else
492 SwapCursor();
493 }
494
495 return 1;
496 }
497
498 void ReceiveInput(void)
499 {
500 if(ConsoleFD != -1)
501 while(ReadKey());
502
503 if(MouseEnabled)
504 while(ReadMouse());
505 }
506
507 static void VTSwitchHandler(int sig)
508 {
509 struct vt_stat st;
510 switch(sig) {
511 case SIGUSR1:
512 ioctl(ConsoleFD, VT_RELDISP, 1);
513 Active = 0;
514 #ifdef MULTIHEAD
515 VisiblePoll = 1;
516 TestVisible();
517 #else
518 VisibleSwitch = 1;
519 Visible = 0;
520 #endif
521 break;
522 case SIGUSR2:
523 ioctl(ConsoleFD, VT_GETSTATE, &st);
524 if(st.v_active)
525 ioctl(ConsoleFD, VT_RELDISP, VT_ACKACQ);
526
527 /* this is a hack to turn the cursor off */
528 ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo);
529
530 RestoreColorMap();
531
532 Active = 1;
533 Visible = 1;
534 VisibleSwitch = 1;
535
536 Redisplay = 1;
537
538 break;
539 }
540 }
541
542 void InitializeVT(int usestdin)
543 {
544 struct termios tio;
545 struct vt_mode vt;
546 char console[128];
547
548 signal(SIGIO, SIG_IGN);
549
550 /* save old terminos settings */
551 if (tcgetattr(0, &OldTermios) < 0) {
552 sprintf(exiterror, "tcgetattr failed\n");
553 exit(0);
554 }
555
556 tio = OldTermios;
557
558 /* terminos settings for straight-through mode */
559 tio.c_lflag &= ~(ICANON | ECHO | ISIG);
560 tio.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
561 tio.c_iflag |= IGNBRK;
562
563 tio.c_cc[VMIN] = 0;
564 tio.c_cc[VTIME] = 0;
565
566 if (tcsetattr(0, TCSANOW, &tio) < 0) {
567 sprintf(exiterror, "tcsetattr failed\n");
568 exit(0);
569 }
570
571 if(fcntl(0, F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
572 sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
573 exit(0);
574 }
575
576 Active = 1;
577
578 if(usestdin) {
579 ConsoleFD = 0;
580 return;
581 }
582
583 /* detect the current vt if it was not specified */
584 if(CurrentVT == 0) {
585 int fd = open("/dev/tty", O_RDWR | O_NDELAY, 0);
586 struct vt_stat st;
587 if(fd == -1) {
588 sprintf(exiterror, "Failed to open /dev/tty\n");
589 exit(0);
590 }
591 if(ioctl(fd, VT_GETSTATE, &st) == -1) {
592 fprintf(stderr, "Could not detect current vt, specify with -vt\n");
593 fprintf(stderr, "Defaulting to stdin input\n");
594 ConsoleFD = 0;
595 close(fd);
596 return;
597 } else
598 CurrentVT = st.v_active;
599
600 close(fd);
601 }
602
603 /* open the console tty */
604 sprintf(console, "/dev/tty%d", CurrentVT);
605 ConsoleFD = open(console, O_RDWR | O_NDELAY, 0);
606 if (ConsoleFD < 0) {
607 sprintf(exiterror, "error couldn't open %s,"
608 " defaulting to stdin \n", console);
609 ConsoleFD = 0;
610 return;
611 }
612
613 signal(SIGUSR1, VTSwitchHandler);
614 signal(SIGUSR2, VTSwitchHandler);
615
616 if (ioctl(ConsoleFD, VT_GETMODE, &OldVTMode) < 0) {
617 sprintf(exiterror,"Failed to grab %s, defaulting to stdin\n", console);
618 close(ConsoleFD);
619 ConsoleFD = 0;
620 return;
621 }
622
623 vt = OldVTMode;
624
625 vt.mode = VT_PROCESS;
626 vt.waitv = 0;
627 vt.relsig = SIGUSR1;
628 vt.acqsig = SIGUSR2;
629 if (ioctl(ConsoleFD, VT_SETMODE, &vt) < 0) {
630 sprintf(exiterror, "error: ioctl(VT_SETMODE) failed: %s\n",
631 strerror(errno));
632 close(ConsoleFD);
633 ConsoleFD = 0;
634 exit(1);
635 }
636
637 if (ioctl(ConsoleFD, KDGKBMODE, &OldKDMode) < 0) {
638 sprintf(exiterror, "Warning: ioctl KDGKBMODE failed!\n");
639 OldKDMode = K_XLATE;
640 }
641
642 /* use SIGIO so VT switching can work if the program is locked */
643 signal(SIGIO, KeyboardHandler);
644
645 pipe(kbdpipe);
646
647 if(fcntl(kbdpipe[0], F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
648 sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
649 exit(0);
650 }
651
652 fcntl(0, F_SETOWN, getpid());
653
654 if(ioctl(ConsoleFD, KDGETMODE, &OldMode) < 0)
655 sprintf(exiterror, "Warning: Failed to get terminal mode\n");
656
657 #ifdef HAVE_GPM
658 if(!GpmMouse)
659 #endif
660 if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0)
661 sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
662
663 if (ioctl(ConsoleFD, KDSKBMODE, K_MEDIUMRAW) < 0) {
664 sprintf(exiterror, "ioctl KDSKBMODE failed!\n");
665 tcsetattr(0, TCSANOW, &OldTermios);
666 exit(0);
667 }
668
669 if( ioctl(ConsoleFD, KDGKBLED, &KeyboardLedState) < 0) {
670 sprintf(exiterror, "ioctl KDGKBLED failed!\n");
671 exit(0);
672 }
673 }
674
675 void RestoreVT(void)
676 {
677 if(ConsoleFD < 0)
678 return;
679
680 if (tcsetattr(0, TCSANOW, &OldTermios) < 0)
681 fprintf(stderr, "tcsetattr failed\n");
682
683 /* setting the mode to text from graphics restores the colormap*/
684 if(
685 #ifdef HAVE_GPM
686 GpmMouse ||
687 #endif
688 ConsoleFD == 0)
689 if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0) {
690 sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
691 goto skipioctl; /* no need to fail twice */
692 }
693
694 if(ioctl(ConsoleFD, KDSETMODE, OldMode) < 0)
695 fprintf(stderr, "ioctl KDSETMODE failed!\n");
696
697 skipioctl:
698
699 if(ConsoleFD == 0)
700 return;
701
702 /* restore keyboard state */
703 if (ioctl(ConsoleFD, VT_SETMODE, &OldVTMode) < 0)
704 fprintf(stderr, "Failed to set vtmode\n");
705
706 if (ioctl(ConsoleFD, KDSKBMODE, OldKDMode) < 0)
707 fprintf(stderr, "ioctl KDSKBMODE failed!\n");
708
709 close(ConsoleFD);
710 }
711
712 void InitializeMouse(void)
713 {
714 #ifdef HAVE_GPM
715 if(!GpmMouse)
716 #endif
717 {
718 const char *mousedev = getenv("MOUSE");
719 if(!mousedev)
720 mousedev = MOUSEDEV;
721 if((MouseFD = open(mousedev, O_RDONLY)) >= 0) {
722 if(!MouseSpeed)
723 MouseSpeed = 1;
724 NumMouseButtons = 3;
725 return;
726 }
727 }
728 #ifdef HAVE_GPM
729 {
730 Gpm_Connect conn;
731 int c;
732 conn.eventMask = ~0; /* Want to know about all the events */
733 conn.defaultMask = 0; /* don't handle anything by default */
734 conn.minMod = 0; /* want everything */
735 conn.maxMod = ~0; /* all modifiers included */
736 if(Gpm_Open(&conn, 0) != -1) {
737 if(!MouseSpeed)
738 MouseSpeed = 8;
739 NumMouseButtons = 3;
740 return;
741 }
742 fprintf(stderr, "Cannot open gpmctl.\n");
743 }
744 #endif
745 fprintf(stderr,"Cannot open %s.\n"
746 "Continuing without Mouse\n", MOUSEDEV);
747 }
748
749 void CloseMouse(void)
750 {
751 #ifdef HAVE_GPM
752 if(GpmMouse) {
753 if(NumMouseButtons)
754 Gpm_Close();
755 } else
756 #endif
757 if(MouseFD >= 0)
758 close(MouseFD);
759 }