mesa/src/glut/fbdev/input.c
Sean D'Epagnier 288d4b6ebe Modularized code into multiple files.
Added gamemode stubs, iconify support, joystick stubs.

Fixed color index mode, added 8 bit alpha blending, enabled cmaps even
with 15, 16, 24, and 32 depths as some fbdev drivers need this.

Fixed compiling with -ansi

Using SIGIO for keyboard input, so that vt switching is enabled even
if the program is locked.

Fixed numerous small bugs.
2006-08-05 08:24:29 +00:00

715 lines
16 KiB
C

/*
* Mesa 3-D graphics library
* Version: 6.5
* Copyright (C) 1995-2006 Brian Paul
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Library for glut using mesa fbdev driver
*
* Written by Sean D'Epagnier (c) 2006
*/
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/kd.h>
#include <linux/keyboard.h>
#include <linux/fb.h>
#include <linux/vt.h>
#include <GL/glut.h>
#include "internal.h"
#define MOUSEDEV "/dev/gpmdata"
#ifdef HAVE_GPM
#include <gpm.h>
int GpmMouse;
#endif
int CurrentVT;
int ConsoleFD = -1;
int KeyboardModifiers;
int MouseX, MouseY;
int NumMouseButtons;
double MouseSpeed = 0;
int KeyRepeatMode = GLUT_KEY_REPEAT_DEFAULT;
/* only display the mouse if there is a registered callback for it */
int MouseEnabled = 0;
static int OldKDMode = -1;
static int OldMode;
static struct vt_mode OldVTMode;
static struct termios OldTermios;
static int KeyboardLedState;
static int MouseFD;
static int kbdpipe[2];
#define MODIFIER(mod) \
KeyboardModifiers = release ? KeyboardModifiers & ~mod \
: KeyboardModifiers | mod;
/* signal handler attached to SIGIO on keyboard input, vt
switching and modifiers is handled in the signal handler
other keypresses read from a pipe that leaves the handler
if a program locks up the glut loop, you can still switch
vts and kill it without Alt-SysRq hack */
static void KeyboardHandler(int sig)
{
int release, labelval;
unsigned char code;
struct kbentry entry;
if(read(ConsoleFD, &code, 1) != 1)
return;
release = code & 0x80;
entry.kb_index = code & 0x7F;
entry.kb_table = 0;
if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
exit(0);
}
labelval = entry.kb_value;
switch(labelval) {
case K_SHIFT:
case K_SHIFTL:
MODIFIER(GLUT_ACTIVE_SHIFT);
return;
case K_CTRL:
MODIFIER(GLUT_ACTIVE_CTRL);
return;
case K_ALT:
case K_ALTGR:
MODIFIER(GLUT_ACTIVE_ALT);
return;
}
if(!release && labelval >= K_F1 && labelval <= K_F12)
if(KeyboardModifiers & GLUT_ACTIVE_ALT) {
/* VT switch, we must do it */
if(ioctl(ConsoleFD, VT_ACTIVATE, labelval - K_F1 + 1) < 0)
sprintf(exiterror, "Error switching console\n");
return;
}
write(kbdpipe[1], &code, 1);
}
static void LedModifier(int led, int release)
{
static int releaseflag = K_CAPS | K_NUM | K_HOLD;
if(release)
releaseflag |= led;
else
if(releaseflag & led) {
KeyboardLedState ^= led;
releaseflag &= ~led;
}
ioctl(ConsoleFD, KDSKBLED, KeyboardLedState);
ioctl(ConsoleFD, KDSETLED, 0x80);
}
#define READKEY read(kbdpipe[0], &code, 1)
static int ReadKey(void)
{
int release, labelval;
unsigned char code;
int specialkey = 0;
struct kbentry entry;
if(READKEY != 1)
return 0;
if(code == 0)
return 0;
/* stdin input escape code based */
if(ConsoleFD == 0) {
KeyboardModifiers = 0;
altset:
if(code == 27 && READKEY == 1) {
switch(code) {
case 79: /* function key */
READKEY;
if(code == 50) {
READKEY;
shiftfunc:
KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
specialkey = GLUT_KEY_F1 + code - 53;
READKEY;
} else {
READKEY;
specialkey = GLUT_KEY_F1 + code - 80;
}
break;
case 91:
READKEY;
switch(code) {
case 68:
specialkey = GLUT_KEY_LEFT; break;
case 65:
specialkey = GLUT_KEY_UP; break;
case 67:
specialkey = GLUT_KEY_RIGHT; break;
case 66:
specialkey = GLUT_KEY_DOWN; break;
case 53:
specialkey = GLUT_KEY_PAGE_UP; READKEY; break;
case 54:
specialkey = GLUT_KEY_PAGE_DOWN; READKEY; break;
case 49:
specialkey = GLUT_KEY_HOME; READKEY; break;
case 52:
specialkey = GLUT_KEY_END; READKEY; break;
case 50:
READKEY;
if(code != 126)
goto shiftfunc;
specialkey = GLUT_KEY_INSERT;
break;
case 51:
code = '\b'; goto stdkey;
case 91:
READKEY;
specialkey = GLUT_KEY_F1 + code - 65;
break;
default:
return 0;
}
break;
default:
KeyboardModifiers |= GLUT_ACTIVE_ALT;
goto altset;
}
}
stdkey:
if(specialkey) {
if(SpecialFunc)
SpecialFunc(specialkey, MouseX, MouseY);
} else {
if(code >= 1 && code <= 26) {
KeyboardModifiers |= GLUT_ACTIVE_CTRL;
code += 'a' - 1;
}
if((code >= 43 && code <= 34) || (code == 60)
|| (code >= 62 && code <= 90) || (code == 94)
|| (code == 95) || (code >= 123 && code <= 126))
KeyboardModifiers |= GLUT_ACTIVE_SHIFT;
if(KeyboardFunc)
KeyboardFunc(code, MouseX, MouseY);
}
return 1;
}
/* linux kbd reading */
release = code & 0x80;
code &= 0x7F;
if(KeyRepeatMode == GLUT_KEY_REPEAT_OFF) {
static char keystates[128];
if(release)
keystates[code] = 0;
else {
if(keystates[code])
return 1;
keystates[code] = 1;
}
}
entry.kb_index = code;
entry.kb_table = 0;
if(KeyboardModifiers & GLUT_ACTIVE_SHIFT)
entry.kb_table |= K_SHIFTTAB;
if (ioctl(ConsoleFD, KDGKBENT, &entry) < 0) {
sprintf(exiterror, "ioctl(KDGKBENT) failed.\n");
exit(0);
}
labelval = entry.kb_value;
switch(labelval) {
case K_CAPS:
LedModifier(LED_CAP, release);
return 0;
case K_NUM:
LedModifier(LED_NUM, release);
return 0;
case K_HOLD: /* scroll lock suspends glut */
LedModifier(LED_SCR, release);
while(KeyboardLedState & LED_SCR) {
usleep(10000);
ReadKey();
}
return 0;
}
/* we could queue keypresses here */
if(KeyboardLedState & LED_SCR)
return 0;
if(labelval >= K_F1 && labelval <= K_F12)
specialkey = GLUT_KEY_F1 + labelval - K_F1;
else
switch(labelval) {
case K_LEFT:
specialkey = GLUT_KEY_LEFT; break;
case K_UP:
specialkey = GLUT_KEY_UP; break;
case K_RIGHT:
specialkey = GLUT_KEY_RIGHT; break;
case K_DOWN:
specialkey = GLUT_KEY_DOWN; break;
case K_PGUP:
specialkey = GLUT_KEY_PAGE_UP; break;
case K_PGDN:
specialkey = GLUT_KEY_PAGE_DOWN; break;
case K_FIND:
specialkey = GLUT_KEY_HOME; break;
case K_SELECT:
specialkey = GLUT_KEY_END; break;
case K_INSERT:
specialkey = GLUT_KEY_INSERT; break;
case K_REMOVE:
labelval = '\b'; break;
case K_ENTER:
labelval = '\n'; break;
}
/* dispatch callback */
if(specialkey) {
if(release) {
if(SpecialUpFunc)
SpecialUpFunc(specialkey, MouseX, MouseY);
} else
if(SpecialFunc)
SpecialFunc(specialkey, MouseX, MouseY);
} else {
char c = labelval;
if(KeyboardLedState & LED_CAP) {
if(c >= 'A' && c <= 'Z')
c += 'a' - 'A';
else
if(c >= 'a' && c <= 'z')
c += 'A' - 'a';
}
if(release) {
if(KeyboardUpFunc)
KeyboardUpFunc(c, MouseX, MouseY);
} else
if(KeyboardFunc)
KeyboardFunc(c, MouseX, MouseY);
}
return 1;
}
void glutIgnoreKeyRepeat(int ignore)
{
KeyRepeatMode = ignore ? GLUT_KEY_REPEAT_OFF : GLUT_KEY_REPEAT_ON;
}
void glutSetKeyRepeat(int repeatMode)
{
KeyRepeatMode = repeatMode;
}
void glutForceJoystickFunc(void)
{
}
static void HandleMousePress(int button, int pressed)
{
if(TryMenu(button, pressed))
return;
if(MouseFunc)
MouseFunc(button, pressed ? GLUT_DOWN : GLUT_UP, MouseX, MouseY);
}
static int ReadMouse(void)
{
int l, r, m;
static int ll, lm, lr;
signed char dx, dy;
#ifdef HAVE_GPM
if(GpmMouse) {
Gpm_Event event;
struct pollfd pfd;
pfd.fd = gpm_fd;
pfd.events = POLLIN;
if(poll(&pfd, 1, 1) != 1)
return 0;
if(Gpm_GetEvent(&event) != 1)
return 0;
l = event.buttons & GPM_B_LEFT;
m = event.buttons & GPM_B_MIDDLE;
r = event.buttons & GPM_B_RIGHT;
/* gpm is weird in that it gives a button number when the button
is released, with type set to GPM_UP, this is only a problem
if it is the last button released */
if(event.type & GPM_UP)
if(event.buttons == GPM_B_LEFT || event.buttons == GPM_B_MIDDLE ||
event.buttons == GPM_B_RIGHT || event.buttons == GPM_B_FOURTH)
l = m = r = 0;
dx = event.dx;
dy = event.dy;
} else
#endif
{
char data[4];
if(MouseFD == -1)
return 0;
if(fcntl(MouseFD, F_SETFL, O_NONBLOCK) == -1) {
close(MouseFD);
MouseFD = -1;
return 0;
}
if(read(MouseFD, data, 4) != 4)
return 0;
l = ((data[0] & 0x20) >> 3);
m = ((data[3] & 0x10) >> 3);
r = ((data[0] & 0x10) >> 4);
dx = (((data[0] & 0x03) << 6) | (data[1] & 0x3F));
dy = (((data[0] & 0x0C) << 4) | (data[2] & 0x3F));
}
MouseX += dx * MouseSpeed;
if(MouseX < 0)
MouseX = 0;
else
if(MouseX >= VarInfo.xres)
MouseX = VarInfo.xres - 1;
MouseY += dy * MouseSpeed;
if(MouseY < 0)
MouseY = 0;
else
if(MouseY >= VarInfo.yres)
MouseY = VarInfo.yres - 1;
if(l != ll)
HandleMousePress(GLUT_LEFT_BUTTON, l);
if(m != lm)
HandleMousePress(GLUT_MIDDLE_BUTTON, m);
if(r != lr)
HandleMousePress(GLUT_RIGHT_BUTTON, r);
ll = l, lm = m, lr = r;
if(dx || dy) {
if(l || m || r) {
if(MotionFunc)
MotionFunc(MouseX, MouseY);
} else
if(PassiveMotionFunc)
PassiveMotionFunc(MouseX, MouseY);
EraseCursor();
if(ActiveMenu)
Redisplay = 1;
else
SwapCursor();
}
return 1;
}
void ReceiveInput(void)
{
if(ConsoleFD != -1)
while(ReadKey());
if(MouseEnabled)
while(ReadMouse());
}
static void VTSwitchHandler(int sig)
{
struct vt_stat st;
switch(sig) {
case SIGUSR1:
ioctl(ConsoleFD, VT_RELDISP, 1);
Active = 0;
#ifdef MULTIHEAD
VisiblePoll = 1;
TestVisible();
#else
VisibleSwitch = 1;
Visible = 0;
#endif
break;
case SIGUSR2:
ioctl(ConsoleFD, VT_GETSTATE, &st);
if(st.v_active)
ioctl(ConsoleFD, VT_RELDISP, VT_ACKACQ);
/* this is a hack to turn the cursor off */
ioctl(FrameBufferFD, FBIOPUT_VSCREENINFO, &VarInfo);
RestoreColorMap();
Active = 1;
Visible = 1;
VisibleSwitch = 1;
Redisplay = 1;
break;
}
}
void InitializeVT(int usestdin)
{
struct termios tio;
struct vt_mode vt;
char console[128];
/* terminos settings for straight-through mode */
if (tcgetattr(0, &OldTermios) < 0) {
sprintf(exiterror, "tcgetattr failed\n");
exit(0);
}
tio = OldTermios;
tio.c_lflag &= ~(ICANON | ECHO | ISIG);
tio.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
tio.c_iflag |= IGNBRK;
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 0;
if (tcsetattr(0, TCSANOW, &tio) < 0) {
sprintf(exiterror, "tcsetattr failed\n");
exit(0);
}
if(fcntl(0, F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
exit(0);
}
Active = 1;
if(usestdin) {
ConsoleFD = 0;
return;
}
/* detect the current vt if it was not specified */
if(CurrentVT == 0) {
int fd = open("/dev/tty", O_RDWR | O_NDELAY, 0);
struct vt_stat st;
if(fd == -1) {
sprintf(exiterror, "Failed to open /dev/tty\n");
exit(0);
}
if(ioctl(fd, VT_GETSTATE, &st) == -1) {
fprintf(stderr, "Could not detect current vt, specify with -vt\n");
fprintf(stderr, "Defaulting to stdin input\n");
ConsoleFD = 0;
close(fd);
return;
} else
CurrentVT = st.v_active;
close(fd);
}
/* open the console tty */
sprintf(console, "/dev/tty%d", CurrentVT);
ConsoleFD = open(console, O_RDWR | O_NDELAY, 0);
if (ConsoleFD < 0) {
sprintf(exiterror, "error couldn't open %s,"
" defaulting to stdin \n", console);
ConsoleFD = 0;
return;
}
signal(SIGUSR1, VTSwitchHandler);
signal(SIGUSR2, VTSwitchHandler);
if (ioctl(ConsoleFD, VT_GETMODE, &OldVTMode) < 0) {
sprintf(exiterror,"Failed to grab %s, defaulting to stdin\n", console);
close(ConsoleFD);
ConsoleFD = 0;
return;
}
vt = OldVTMode;
vt.mode = VT_PROCESS;
vt.waitv = 0;
vt.relsig = SIGUSR1;
vt.acqsig = SIGUSR2;
if (ioctl(ConsoleFD, VT_SETMODE, &vt) < 0) {
sprintf(exiterror, "error: ioctl(VT_SETMODE) failed: %s\n",
strerror(errno));
close(ConsoleFD);
ConsoleFD = 0;
exit(1);
}
if (ioctl(ConsoleFD, KDGKBMODE, &OldKDMode) < 0) {
sprintf(exiterror, "Warning: ioctl KDGKBMODE failed!\n");
OldKDMode = K_XLATE;
}
/* use SIGIO so VT switching can work if the program is locked */
if(ConsoleFD)
signal(SIGIO, KeyboardHandler);
pipe(kbdpipe);
if(fcntl(kbdpipe[0], F_SETFL, O_NONBLOCK | O_ASYNC) < 0) {
sprintf(exiterror, "Failed to set keyboard to non-blocking\n");
exit(0);
}
fcntl(0, F_SETOWN, getpid());
if(ioctl(ConsoleFD, KDGETMODE, &OldMode) < 0)
sprintf(exiterror, "Warning: Failed to get terminal mode\n");
#ifdef HAVE_GPM
if(!GpmMouse)
#endif
if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0)
sprintf(exiterror,"Warning: Failed to set terminal to graphics\n");
if (ioctl(ConsoleFD, KDSKBMODE, K_MEDIUMRAW) < 0) {
sprintf(exiterror, "ioctl KDSKBMODE failed!\n");
tcsetattr(0, TCSANOW, &OldTermios);
exit(0);
}
if( ioctl(ConsoleFD, KDGKBLED, &KeyboardLedState) < 0) {
sprintf(exiterror, "ioctl KDGKBLED failed!\n");
exit(0);
}
}
void RestoreVT(void)
{
if(ConsoleFD >= 0)
if (tcsetattr(0, TCSANOW, &OldTermios) < 0)
fprintf(stderr, "tcsetattr failed\n");
if(ConsoleFD > 0) {
/* restore keyboard state */
if (ioctl(ConsoleFD, VT_SETMODE, &OldVTMode) < 0)
fprintf(stderr, "Failed to set vtmode\n");
if (ioctl(ConsoleFD, KDSKBMODE, OldKDMode) < 0)
fprintf(stderr, "ioctl KDSKBMODE failed!\n");
/* if we were in text mode, switching to graphics and back restores
the colormap */
if(ioctl(ConsoleFD, KDSETMODE, KD_GRAPHICS) < 0)
fprintf(stderr, "ioctl KDSETMODE failed!\n");
if(ioctl(ConsoleFD, KDSETMODE, OldMode) < 0)
fprintf(stderr, "ioctl KDSETMODE failed!\n");
close(ConsoleFD);
}
}
void InitializeMouse(void)
{
#ifdef HAVE_GPM
if(GpmMouse) {
Gpm_Connect conn;
int c;
conn.eventMask = ~0; /* Want to know about all the events */
conn.defaultMask = 0; /* don't handle anything by default */
conn.minMod = 0; /* want everything */
conn.maxMod = ~0; /* all modifiers included */
if(Gpm_Open(&conn, 0) == -1) {
fprintf(stderr, "Cannot open gpmctl. Continuing without Mouse\n");
return;
}
if(!MouseSpeed)
MouseSpeed = 5;
} else
#endif
{
const char *mousedev = getenv("MOUSE");
if(!mousedev)
mousedev = MOUSEDEV;
if((MouseFD = open(mousedev, O_RDONLY)) < 0) {
fprintf(stderr,"Cannot open %s.\n"
"Continuing without Mouse\n", MOUSEDEV);
return;
}
if(!MouseSpeed)
MouseSpeed = 1;
}
NumMouseButtons = 3;
}
void CloseMouse(void)
{
#ifdef HAVE_GPM
if(GpmMouse) {
if(NumMouseButtons)
Gpm_Close();
} else
#endif
if(MouseFD >= 0)
close(MouseFD);
}