Logo Search packages:      
Sourcecode: ardour version File versions

physical_keyboard.cc

/*
    Copyright (C) 2001 Paul Davis

    Parts based on source code taken from the "kbd" suite that is

    Copyright (C) 1993 Risto Kankunen
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: physical_keyboard.cc,v 1.15 2003/01/26 05:36:21 trutkin Exp $
*/

#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <signal.h>
#include <fcntl.h>
#include <termios.h>
#include <linux/kd.h>
#include <linux/keyboard.h>
#include <linux/vt.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <poll.h>
#include <string.h>

#include <pbd/atomic.h>
#include <pbd/error.h>
#include <pbd/failed_constructor.h>

#include "i18n.h"
#include "physical_keyboard.h"
#include "compat.h"

extern void die (void);

PhysicalKeyboard::PhysicalKeyboard ()

{
      if (manage_hardware()) {
            throw failed_constructor ();
      }

      keyboard_state = 0;
      isproxy = false;
}

PhysicalKeyboard::PhysicalKeyboard (int socketfd)
{
      if (manage_hardware()) {
            throw failed_constructor ();
      }

      if (unmanage_hardware()) {
            throw failed_constructor ();
      }

      fd = socketfd;
      keyboard_state = 0;
      isproxy = true;
}

PhysicalKeyboard::~PhysicalKeyboard () 
{
      unmanage_hardware ();
}

int
PhysicalKeyboard::manage_hardware ()

{
      struct termios settings;

      if ((fd = getfd()) < 0) {
            return -1;
      }

      if (get_mode ()) {
            return -1;
      }

      if (tcgetattr(fd, &old) == -1) {
            error << compose(_("tcgetattr failed (%1)"), strerror (errno)) << endmsg;
            return -1;
      }

      if (ioctl(fd, KDSKBMODE, K_MEDIUMRAW)) {
            error << _("Cannot set keyboard into \"medium-raw\" mode.\n If you are running under X, this may be because\n the X server has changed the access permissions on /dev/console") << endmsg;
            return -1;
      }

      if (tcgetattr(fd, &settings) == -1) {
            error << compose(_("tcgetattr failed (%1)"), strerror (errno)) << endmsg;
            return -1;
      }

      settings.c_lflag &= ~ (ICANON | ECHO | ISIG);
      settings.c_iflag = 0;
      settings.c_cc[VMIN] = 1;
      settings.c_cc[VTIME] = 0;

      if (tcsetattr(fd, TCSAFLUSH, &settings) == -1) {
            error << compose(_("tcsetattr failed (%1)"), strerror (errno)) << endmsg;
            return -1;
      }

      if (fcntl (fd, F_SETFL, fcntl (fd, F_GETFL) | O_NONBLOCK)) {
            error << _("cannot set nonblock on tty fd") << endmsg;
            return -1;
      }

      if (get_keybindings ()) {
            return -1;
      }

      return 0;
}

int
PhysicalKeyboard::unmanage_hardware ()
{
      if (!isproxy) {
            if (ioctl(fd, KDSKBMODE, oldkbmode)) {
                  error << compose(_("cannot restore keyboard mode (%1)"), strerror (errno)) << endmsg;
                  return -1;
            }
            
            if (tcsetattr(fd, 0, &old) == -1) {
                  error << compose(_("tcsetattr failed (%1)"), strerror (errno)) << endmsg;
                  return -1;
            }
      }

      close (fd);
      return 0;
}

bool
PhysicalKeyboard::is_a_console(int fd) 
{
    char arg;

    arg = 0;
    return (ioctl(fd, KDGKBTYPE, &arg) == 0 && ((arg == KB_101) || (arg == KB_84)));
}

int
PhysicalKeyboard::open_a_console(const char *fnam) 
{
      int fd;
      
      fd = open(fnam, O_RDONLY);
      
      if (fd < 0 && errno == EACCES) {
            fd = open (fnam, O_WRONLY);
      }

      if (fd < 0)
            return -1;

      if (!is_a_console(fd)) {
            close(fd);
            return -1;
      }

      return fd;
}

int 
PhysicalKeyboard::getfd() 
{
      int fd;
      
      fd = open_a_console("/dev/tty");
      if (fd >= 0)
            return fd;
      
      fd = open_a_console("/dev/tty0");
      if (fd >= 0)
            return fd;
      
      fd = open_a_console("/dev/console");
      if (fd >= 0)
            return fd;
      
      for (fd = 0; fd < 3; fd++)
            if (is_a_console(fd))
                  return fd;
      
      error << _("Couldnt get a file descriptor referring to the console")          << endmsg;
      
      return -1;
}


int
PhysicalKeyboard::get_mode(void) 
{
      if (ioctl(fd, KDGKBMODE, &oldkbmode)) {
            error << compose(_("KDGKBMODE failed (%1)"), strerror (errno)) << endmsg;
            return -1;
      }
      return 0;
}

int 
PhysicalKeyboard::get_keybindings ()
{
      int i, j;
      struct kbentry ke;

      /* check the plain keymap */

      ke.kb_index = 0;
      ke.kb_table = 0;

      j = ioctl(fd, KDGKBENT, (unsigned long)&ke);

      if (j && errno != EINVAL) {
            error << compose(_("KDGKBENT" "failed (%1)"), strerror (errno)) << endmsg;
            return -1;
      }
      if (j || ke.kb_value == K_NOSUCHMAP) {
            error << _("the plain keymap is undefined") << endmsg;
            return -1;
      }

      /* use the plain keymap */

      for (i = 0; i < NR_KEYS; i++) {
            ke.kb_index = i;
            ke.kb_table = 0;
            
            if (ioctl(fd, KDGKBENT, (unsigned long)&ke)) {
                  error << _("cannot lookup keycode ") << i << endmsg;
            } else {
                  keys[i] = ke.kb_value;
            }
      }

      return 0;
}
void
    PhysicalKeyboard::print_key(unsigned char key) {
      int binding;
      int keycode = key & 0x7f;
      bool press = !(key & (1<<7));

      binding = keys[keycode];
      cerr << "JHALL: ";
      if (press) {
            cerr << "key_pressed: ";
      } else {
            cerr << "key_released";
      }
      cerr << " Binding Type: ";
      switch (KTYP(binding)) {
            case KT_LETTER:
            cerr << "KT_LETTER ";
            break;
            case KT_LATIN:
            cerr << "KT_LATIN ";
            break;
            case KT_FN:
            cerr << "KT_FN";
            break;
            case KT_SPEC:
            cerr << "KT_SPEC ";
            break;
            case KT_PAD:
            cerr << "KT_PAD ";
            break;
            case KT_DEAD:
            cerr << "KT_DEAD ";
            break;
            case KT_CONS:
            cerr << "KT_CONS ";
            break;
            case KT_CUR:
            cerr << "KT_CUR ";
            break;
            case KT_SHIFT:
            cerr << "KT_SHIFT ";
            break;
            case KT_META:
            cerr << "KT_META ";
            break;
            case KT_ASCII:
            cerr << "KT_ASCII ";
            break;
            case KT_LOCK:
            cerr << "KT_LOCK ";
            break;
            case KT_SLOCK:
            cerr << "KT_SLOCK ";
            break;
            case 13: // CONFIG_SPEAKUP
            cerr << "KT_SPKUP ";
            break;
            default:
            cerr << "KT_UNKNOWN_KEYTYPE ";
      }
      if (keyboard_state & PhysicalKeyboard::Alt_L) {
            cerr << "Alt_L, ";
      }
      if (keyboard_state & PhysicalKeyboard::Alt_R) {
            cerr << "Alt_R, ";
      }
      if (keyboard_state & PhysicalKeyboard::Control_L) {
            cerr << "Control_L, ";
      }
      if (keyboard_state & PhysicalKeyboard::Control_R) {
            cerr << "Control_R, ";
      }
      if (keyboard_state & PhysicalKeyboard::Shift_L) {
            cerr << "Shift_L, ";
      }
      if (keyboard_state & PhysicalKeyboard::Shift_R) {
            cerr << "Shift_R, ";
      }
      cerr << "Key: " << KVAL(binding) << endl;
}

int
PhysicalKeyboard::process_key (unsigned char key)
{
      int binding;
      int keycode = key & 0x7f;
      bool press = !(key & (1<<7));
      int retcode = 0;

      binding = keys[keycode];
      EMIT_SIGNAL key_event (key);

      switch (KTYP(binding)) {
      case KT_SHIFT:
            switch (binding) {
            case K_SHIFT:
                  if (press) {
                        keyboard_state |= Shift_L;
                  } else {
                        keyboard_state &= ~Shift_L;
                  }
                  break;
            case K_CTRL:
                  if (press) {
                        keyboard_state |= Control_L;
                  } else {
                        keyboard_state &= ~Control_L;
                  }
                  break;
            case K_ALT:
                  if (press) {
                        keyboard_state |= Alt_L;
                  } else {
                        keyboard_state &= ~Alt_L;
                  }
                  break;
            case K_ALTGR:
                  break;
            case K_SHIFTL:
                  if (press) {
                        keyboard_state |= Shift_L;
                  } else {
                        keyboard_state &= ~Shift_L;
                  }
                  break;
            case K_SHIFTR:
                  if (press) {
                        keyboard_state |= Shift_R;
                  } else {
                        keyboard_state &= ~Shift_R;
                  }
                  break;
            case K_CTRLL:
                  if (press) {
                        keyboard_state |= Control_L;
                  } else {
                        keyboard_state &= ~Control_L;
                  }
                  break;
            case K_CTRLR:
                  if (press) {
                        keyboard_state |= Control_R;
                  } else {
                        keyboard_state &= ~Control_R;
                  }
                  break;
            case K_CAPSSHIFT:
            case KT_META:
            case KT_ASCII:
            case KT_LOCK:
            case KT_SLOCK:
                  break;
            }
            break;

      case KT_FN:
            switch (binding) {
            case K_F1:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (1);
                        return 0;
                  }
                  break;

            case K_F2:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (2);
                        return 0;
                  }
                  break;
            case K_F3:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (3);
                        return 0;
                  }
                  break;
            case K_F4:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (4);
                        return 0;
                  }
                  break;
            case K_F5:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (5);
                        return 0;
                  }
                  break;
            case K_F6:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (6);
                        return 0;
                  }
                  break;
            case K_F7:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (7);
                        return 0;
                  }
                  break;
            case K_F8:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (8);
                        return 0;
                  }
                  break;
            case K_F9:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (9);
                        return 0;
                  }
                  break;
            case K_F10:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (10);
                        return 0;
                  }
                  break;
            case K_F11:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (11);
                        return 0;
                  }
                  break;
            case K_F12:
                  if (keyboard_state & PhysicalKeyboard::Alt) {
                        switch_vt (12);
                        return 0;
                  }
                  break;
            }
            
            if (press) retcode = EMIT_SIGNAL key_press   (keyboard_state, binding);
            else       retcode = EMIT_SIGNAL key_release (keyboard_state, binding);
            break;

      case KT_LATIN:
      case KT_LETTER:
      case KT_SPEC:
            if (press) retcode = EMIT_SIGNAL key_press   (keyboard_state, KVAL(binding));
            else       retcode = EMIT_SIGNAL key_release (keyboard_state, KVAL(binding));
            break;

      case KT_PAD:
      case KT_DEAD:
      case KT_CONS:
            break;
      case KT_CUR:
            switch (binding) {
            case K_UP:
                  if (press) retcode = EMIT_SIGNAL key_press (keyboard_state, K_UP);
                  else       retcode = EMIT_SIGNAL key_release (keyboard_state, K_UP);
                  break;
            case K_DOWN:
                  if (press) retcode = EMIT_SIGNAL key_press (keyboard_state, K_DOWN);
                  else       retcode = EMIT_SIGNAL key_release (keyboard_state, K_DOWN);
                  break;
            case K_LEFT:
                  if (press) retcode = EMIT_SIGNAL key_press (keyboard_state, K_LEFT);
                  else       retcode = EMIT_SIGNAL key_release (keyboard_state, K_LEFT);
                  break;
            case K_RIGHT:
                  if (press) retcode = EMIT_SIGNAL key_press (keyboard_state, K_RIGHT);
                  else       retcode = EMIT_SIGNAL key_release (keyboard_state, K_RIGHT);
                  break;
            }
            break;

            
      /* 0x0d is KT_SPEAKUP */

      }

      return retcode;
}

int
PhysicalKeyboard::main ()
{
      struct pollfd pfd;
      int status = -1;

      while (1) {

            pfd.fd = fd;
            pfd.events = POLLIN | POLLERR | POLLHUP;

            if (poll (&pfd, 1, 1000) < 0) {
                  if (errno == EINTR) {
                        // this happens mostly when run
                        // under gdb, or when exiting due to a signal
                        continue;
                  }

                  error << compose(_("keyboard: poll call failed (%1)"), strerror (errno)) << endmsg;
                  break;
            }
            
            if (pfd.revents & (POLLERR|POLLERR)) {
                  error << _("keyboard: poll reports error.") << endmsg;
                  break;
            }

            if (pfd.revents == 0) {
                  // timed out, such as when the device is paused
                  continue;
            }

            if ((status = handle_input ()) != 0) {
                  break;
            }
      }

      return status;
}

int
PhysicalKeyboard::handle_input ()

{
      unsigned char buf[16];
      int i, n;

      while (1) {
            n = read (fd, buf, sizeof(buf));
            
            if (n == 0) {
                  break;
            }
            
            if (n < 0) {
                  if (errno != EAGAIN) {
                        error << compose(_("keyboard: read error (%1)"), strerror (errno)) << endmsg;
                        return -1;
                  } else {
                        break;
                  }
            }
            
            for (i = 0; i < n; i++) {
                  n = process_key (buf[i]);
                  if (n < 0) {
                        return -1;
                  } else if (n > 0) {
                        return 1;
                  }
            }
      }

      return 0;
}     

void
PhysicalKeyboard::switch_vt (int which)

{
      int fd = getfd();

      if (ioctl (fd, VT_ACTIVATE, which)) {
            error << compose(_("cannot switch to VT %1"), which) << endmsg;
      } else {
            if (ioctl (fd, VT_WAITACTIVE, which)) {
                  error << compose(_("cannot wait for VT %1 to become active"), which) << endmsg;
            }
      }

      close (fd);
}

Generated by  Doxygen 1.6.0   Back to index