/* ______ ___ ___
* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* BeOS keyboard driver.
*
* By Jason Wilkins, rewritten by Angelo Mottola to use the unified
* pckeys API.
*
* See readme.txt for copyright information.
*/
#include <stdlib.h>
#include "bealleg.h"
#include "allegro/internal/aintern.h"
#include "allegro/platform/aintbeos.h"
#if !defined ALLEGRO_BEOS && !defined ALLEGRO_HAIKU
#error something is wrong with the makefile
#endif
#define KEY_ID_PC101 0 // the docs say it should be 0x83ab, but they lie
#define KEY_SEMAPHORE_NAME "keyboard driver waiting..."
#define KEY_THREAD_PERIOD 33333 // microseconds, 1/30th of a second
#define KEY_THREAD_NAME "keyboard driver"
#define KEY_THREAD_PRIORITY 60 // above average
#define BE_KEY_PAUSE 16
#define BE_KEY_NUMLOCK 34
#define BE_KEY_CAPSLOCK 59
#define BE_KEY_SCROLOCK 15
#define PREFIX_I "al-bkey INFO: "
#define PREFIX_W "al-bkey WARNING: "
#define PREFIX_E "al-bkey ERROR: "
static uint16 keyboard_id = (uint16)(-1);
static volatile int keyboard_thread_running = FALSE;
static thread_id keyboard_thread_id = -1;
static sem_id waiting_for_input = -1;
static const int be_to_pc[128] = {
// Scancode Be Key
-1, // 0 (not used)
0x1, // 1 ESC
0x3b, // 2 F1
0x3c, // 3 F2
0x3d, // 4 F3
0x3e, // 5 F4
0x3f, // 6 F5
0x40, // 7 F6
0x41, // 8 F7
0x42, // 9 F8
0x43, // 10 F9
0x44, // 11 F10
0x57, // 12 F11
0x58, // 13 F12
0x54, // 14 Print Screen / SysRq
0x46, // 15 Scroll Lock
0, // 16 Pause / Break
0x29, // 17 Tilde
0x2, // 18 '1'
0x3, // 19 '2'
0x4, // 20 '3'
0x5, // 21 '4'
0x6, // 22 '5'
0x7, // 23 '6'
0x8, // 24 '7'
0x9, // 25 '8'
0xa, // 26 '9'
0xb, // 27 '0'
0xc, // 28 '-'
0xd, // 29 '='
0xe, // 30 Backspace
0x5200, // 31 Insert
0x4700, // 32 Home
0x4900, // 33 Page Up
0x45, // 34 Num Lock
0x3500, // 35 Pad '/'
0x37, // 36 Pad '*'
0x4a, // 37 Pad '-'
0xf, // 38 Tab
0x10, // 39 'q'
0x11, // 40 'w'
0x12, // 41 'e'
0x13, // 42 'r'
0x14, // 43 't'
0x15, // 44 'y'
0x16, // 45 'u'
0x17, // 46 'i'
0x18, // 47 'o'
0x19, // 48 'p'
0x1a, // 49 '['
0x1b, // 50 ']'
0x35, // 51 '\'
0x5300, // 52 Delete
0x4f00, // 53 End
0x5100, // 54 Page Down
0x47, // 55 Pad '7'
0x48, // 56 Pad '8'
0x49, // 57 Pad '9'
0x4e, // 58 Pad '+'
0x3a, // 59 Caps Lock
0x1e, // 60 'a'
0x1f, // 61 's'
0x20, // 62 'd'
0x21, // 63 'f'
0x22, // 64 'g'
0x23, // 65 'h'
0x24, // 66 'j'
0x25, // 67 'k'
0x26, // 68 'l'
0x27, // 69 ';'
0x28, // 70 '''
0x1c, // 71 Enter
0x4b, // 72 Pad '4'
0x4c, // 73 Pad '5'
0x4d, // 74 Pad '6'
0x2a, // 75 Left Shift
0x2c, // 76 'z'
0x2d, // 77 'x'
0x2e, // 78 'c'
0x2f, // 79 'v'
0x30, // 80 'b'
0x31, // 81 'n'
0x32, // 82 'm'
0x33, // 83 ','
0x34, // 84 '.'
0x35, // 85 '/'
0x36, // 86 Right Shift
0x4800, // 87 Up
0x4f, // 88 Pad '1'
0x50, // 89 Pad '2'
0x51, // 90 Pad '3'
0x1c00, // 91 Pad Enter
0x1d, // 92 Left Control
0x38, // 93 Left Alt
0x39, // 94 Space
0x3800, // 95 Right Alt
0x1d00, // 96 Right Control
0x4b00, // 97 Left
0x5000, // 98 Down
0x4d00, // 99 Right
0x52, // 100 Pad '0'
0x53, // 101 Pad Del
0x5b, // 102 Left Windows Key
0x5c, // 103 Right Windows Key
0x5d, // 104 Menu
-1, // 105 (not used)
-1, // 106 (not used)
-1, // 107 (not used)
-1, // 108 (not used)
-1, // 109 (not used)
-1, // 110 (not used)
-1, // 111 (not used)
-1, // 112 (not used)
-1, // 113 (not used)
-1, // 114 (not used)
-1, // 115 (not used)
-1, // 116 (not used)
-1, // 117 (not used)
-1, // 118 (not used)
-1, // 119 (not used)
-1, // 120 (not used)
-1, // 121 (not used)
-1, // 122 (not used)
-1, // 123 (not used)
-1, // 124 (not used)
-1, // 125 (not used)
-1, // 126 (not used)
-1 // 127 (not used)
};
/* keyboard_thread:
*/
static int32 keyboard_thread(void *keyboard_started)
{
key_info key_info_old;
key_info key_info_new;
get_key_info(&key_info_old);
if (key_info_old.modifiers & B_CAPS_LOCK)
_key_shifts |= KB_CAPSLOCK_FLAG;
if (key_info_old.modifiers & B_SCROLL_LOCK)
_key_shifts |= KB_SCROLOCK_FLAG;
if (key_info_old.modifiers & B_NUM_LOCK)
_key_shifts |= KB_NUMLOCK_FLAG;
release_sem(*(sem_id *)keyboard_started);
while(keyboard_thread_running) {
int i;
snooze(KEY_THREAD_PERIOD);
if (!_be_focus_count)
continue;
get_key_info(&key_info_new);
if (three_finger_flag &&
(_key_shifts & KB_CTRL_FLAG) &&
(_key_shifts & KB_ALT_FLAG) &&
(key_info_new.key_states[52 / 8] & (52 % 8)) ) {
_be_terminate(keyboard_thread_id, true);
}
for (i=0; i<128; i++) {
int new_key_pressed;
int old_key_pressed;
int scancode;
new_key_pressed = key_info_new.key_states[i >> 3] &
(1 << (7 - (i % 8)));
old_key_pressed = key_info_old.key_states[i >> 3] &
(1 << (7 - (i % 8)));
if (new_key_pressed != old_key_pressed) {
scancode = be_to_pc[i];
if (scancode != -1) {
if (i == BE_KEY_PAUSE) {
/* pause key special case */
_handle_key_press(0, KEY_PAUSE);
_handle_key_release(KEY_PAUSE);
continue;
}
if ((i == BE_KEY_CAPSLOCK) ||
(i == BE_KEY_SCROLOCK) ||
(i == BE_KEY_NUMLOCK)) {
/* caps lock, scroll lock, and num lock special cases */
_handle_pckey(scancode);
_handle_pckey(scancode | 0x80);
continue;
}
if (scancode & 0xff00) {
/* extended code */
_handle_pckey(0xe0);
scancode >>= 8;
}
/* handle normal key press/release */
if (!new_key_pressed)
scancode |= 0x80;
_handle_pckey(scancode);
}
}
}
key_info_old = key_info_new;
}
/* XXX commented out due to conflicting TRACE in Haiku
TRACE(PREFIX_I "keyboard thread exited\n");
*/
return 0;
}
/* set_default_key_repeat:
*/
static inline bool set_default_key_repeat(void)
{
int32 rate;
bigtime_t delay;
if (get_key_repeat_rate(&rate) != B_OK) {
return false;
}
if (get_key_repeat_delay(&delay) != B_OK) {
return false;
}
set_keyboard_rate((delay / 1000), (10000 / rate));
return true;
}
/* be_key_init:
*/
extern "C" int be_key_init(void)
{
sem_id keyboard_started;
_pckeys_init();
if (get_keyboard_id(&keyboard_id) == B_ERROR) {
goto cleanup;
}
if (keyboard_id != KEY_ID_PC101) {
goto cleanup;
}
waiting_for_input = create_sem(0, "waiting for input...");
if (waiting_for_input < B_NO_ERROR) {
goto cleanup;
}
keyboard_started = create_sem(0, "starting keyboard thread...");
if (keyboard_started < B_NO_ERROR) {
goto cleanup;
}
keyboard_thread_id = spawn_thread(keyboard_thread, KEY_THREAD_NAME,
KEY_THREAD_PRIORITY, &keyboard_started);
if (keyboard_thread_id < 0) {
goto cleanup;
}
keyboard_thread_running = TRUE;
resume_thread(keyboard_thread_id);
acquire_sem(keyboard_started);
delete_sem(keyboard_started);
if(!set_default_key_repeat()) {
goto cleanup;
}
return 0;
cleanup: {
if (keyboard_started > 0) {
delete_sem(keyboard_started);
}
be_key_exit();
return 1;
}
}
/* be_key_exit:
*/
extern "C" void be_key_exit(void)
{
if (keyboard_thread_id > 0) {
keyboard_thread_running = FALSE;
wait_for_thread(keyboard_thread_id, &ignore_result);
keyboard_thread_id = -1;
}
if (waiting_for_input > 0) {
delete_sem(waiting_for_input);
waiting_for_input = -1;
}
keyboard_id = (uint16)(-1);
}
/* be_key_set_leds:
* Sets keyboard leds.
*/
extern "C" void be_key_set_leds(int leds)
{
uint32 modifiers;
modifiers = 0;
_key_shifts &= ~(KB_CAPSLOCK_FLAG | KB_SCROLOCK_FLAG | KB_NUMLOCK_FLAG);
if (leds & KB_CAPSLOCK_FLAG) {
modifiers |= B_CAPS_LOCK;
_key_shifts |= KB_CAPSLOCK_FLAG;
}
if (leds & KB_SCROLOCK_FLAG) {
modifiers |= B_SCROLL_LOCK;
_key_shifts |= KB_SCROLOCK_FLAG;
}
if (leds & KB_NUMLOCK_FLAG) {
modifiers |= B_NUM_LOCK;
_key_shifts |= KB_NUMLOCK_FLAG;
}
set_keyboard_locks(modifiers);
}
/* be_key_set_rate:
* Sets keyboard repeat rate and delay.
*/
extern "C" void be_key_set_rate(int delay, int repeat)
{
if (delay < 250)
delay = 250;
else if (delay < 500)
delay = 500;
else if (delay < 750)
delay = 750;
else
delay = 1000;
set_key_repeat_delay((bigtime_t)(delay * 1000));
if (repeat > 0)
set_key_repeat_rate(10000 / repeat);
}
/* be_key_wait_for_input:
*/
extern "C" void be_key_wait_for_input(void)
{
acquire_sem(waiting_for_input);
}
/* be_key_stop_waiting_for_input:
*/
extern "C" void be_key_stop_waiting_for_input(void)
{
release_sem(waiting_for_input);
}
extern "C" void be_key_suspend(void)
{
suspend_thread(keyboard_thread_id);
}
extern "C" void be_key_resume(void)
{
resume_thread(keyboard_thread_id);
}