genode/repos/os/include/terminal/scancode_tracker.h
Norman Feske 455bd9396e gems/terminal: support 'CHARACTER' events
This patch adds the handling of 'CHARACTER' events as emitted by the
input-filter's character generator (<chargen>). To avoid interpreting
press/release events twice (at the input filter and by the terminal's
built-in scancode tracker), the terminal's scancode tracker can be
explicitly disabled via <config> <keyboard layout="none"/> </config>.
In the future, the terminal's built-in scancode tracker will be
removed.

The use of the terminal with the input filter is illustrated by the
'terminal_echo.run' script.

Issue #2264
2017-02-27 15:37:49 +01:00

204 lines
5.2 KiB
C++

/*
* \brief State machine for translating scan codes to characters
* \author Norman Feske
* \date 2011-06-06
*/
/*
* Copyright (C) 2011-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _TERMINAL__SCANCODE_TRACKER_H_
#define _TERMINAL__SCANCODE_TRACKER_H_
#include <input/keycodes.h>
#include <terminal/read_buffer.h>
namespace Terminal { class Scancode_tracker; }
/**
* State machine that translates keycode sequences to terminal characters
*/
class Terminal::Scancode_tracker
{
private:
/**
* Tables containing the scancode-to-character mapping
*/
unsigned char const *_keymap;
unsigned char const *_shift;
unsigned char const *_altgr;
unsigned char const *_control;
/**
* Current state of modifer keys
*/
bool _mod_shift;
bool _mod_control;
bool _mod_altgr;
/**
* Currently pressed key, or 0 if no normal key (one that can be
* encoded in a single 'char') is pressed
*/
unsigned char _last_character;
/**
* Currently pressed special key (a key that corresponds to an escape
* sequence), or no if no special key is pressed
*/
char const *_last_sequence;
/**
* Convert keycode to terminal character
*/
unsigned char _keycode_to_latin1(int keycode)
{
if (keycode >= 112) return 0;
unsigned ch = _keymap[keycode];
if (ch < 32)
return ch;
/* all ASCII-to-ASCII table start at index 32 */
if (_mod_shift || _mod_control || _mod_altgr) {
ch -= 32;
/*
* 'ch' is guaranteed to be in the range 0..223. So it is safe to
* use it as index into the ASCII-to-ASCII tables.
*/
if (_mod_shift)
return _shift[ch];
if (_mod_control)
return _control[ch];
if (_altgr && _mod_altgr)
return _altgr[ch];
}
return ch;
}
public:
/**
* Constructor
*
* \param keymap table for keycode-to-character mapping
* \param shift table for character-to-character mapping used when
* Shift is pressed
* \param altgr table for character-to-character mapping with AltGr
* is pressed
*/
Scancode_tracker(unsigned char const *keymap,
unsigned char const *shift,
unsigned char const *altgr,
unsigned char const *control)
:
_keymap(keymap),
_shift(shift),
_altgr(altgr),
_control(control),
_mod_shift(false),
_mod_control(false),
_mod_altgr(false),
_last_character(0),
_last_sequence(0)
{ }
/**
* Submit key event to state machine
*
* \param press true on press event, false on release event
*/
void submit(int keycode, bool press)
{
/* track modifier keys */
switch (keycode) {
case Input::KEY_LEFTSHIFT:
case Input::KEY_RIGHTSHIFT:
_mod_shift = press;
break;
case Input::KEY_LEFTCTRL:
case Input::KEY_RIGHTCTRL:
_mod_control = press;
break;
case Input::KEY_RIGHTALT:
_mod_altgr = press;
default:
break;
}
/* reset information about the currently pressed key */
_last_character = 0;
_last_sequence = 0;
if (!press) return;
/* convert key codes to ASCII */
_last_character = _keycode_to_latin1(keycode);
/* handle special key to be represented by an escape sequence */
if (!_last_character) {
switch (keycode) {
case Input::KEY_DOWN: _last_sequence = "\E[B"; break;
case Input::KEY_UP: _last_sequence = "\E[A"; break;
case Input::KEY_RIGHT: _last_sequence = "\E[C"; break;
case Input::KEY_LEFT: _last_sequence = "\E[D"; break;
case Input::KEY_HOME: _last_sequence = "\E[1~"; break;
case Input::KEY_INSERT: _last_sequence = "\E[2~"; break;
case Input::KEY_DELETE: _last_sequence = "\E[3~"; break;
case Input::KEY_END: _last_sequence = "\E[4~"; break;
case Input::KEY_PAGEUP: _last_sequence = "\E[5~"; break;
case Input::KEY_PAGEDOWN: _last_sequence = "\E[6~"; break;
case Input::KEY_F1: _last_sequence = "\E[[A"; break;
case Input::KEY_F2: _last_sequence = "\E[[B"; break;
case Input::KEY_F3: _last_sequence = "\E[[C"; break;
case Input::KEY_F4: _last_sequence = "\E[[D"; break;
case Input::KEY_F5: _last_sequence = "\E[[E"; break;
case Input::KEY_F6: _last_sequence = "\E[17~"; break;
case Input::KEY_F7: _last_sequence = "\E[18~"; break;
case Input::KEY_F8: _last_sequence = "\E[19~"; break;
case Input::KEY_F9: _last_sequence = "\E[20~"; break;
case Input::KEY_F10: _last_sequence = "\E[21~"; break;
case Input::KEY_F11: _last_sequence = "\E[23~"; break;
case Input::KEY_F12: _last_sequence = "\E[24~"; break;
}
}
}
/**
* Output currently pressed key to read buffer
*/
void emit_current_character(Read_buffer &read_buffer)
{
if (_last_character)
read_buffer.add(_last_character);
if (_last_sequence)
read_buffer.add(_last_sequence);
}
/**
* Return true if there is a currently pressed key
*/
bool valid() const
{
return (_last_sequence || _last_character);
}
};
#endif /* _TERMINAL__SCANCODE_TRACKER_H_ */