454 lines
12 KiB
C++
454 lines
12 KiB
C++
|
|
|
|
#include "port.h"
|
|
#include "keyboard.h"
|
|
|
|
|
|
|
|
enum mod
|
|
{
|
|
MOD_NONE = 0,
|
|
MOD_SYM,
|
|
MOD_ALT,
|
|
MOD_SHL,
|
|
MOD_SHR,
|
|
MOD_CTRL,
|
|
MOD_LAST,
|
|
};
|
|
|
|
struct entry
|
|
{
|
|
char chr;
|
|
char symb;
|
|
enum mod mod;
|
|
};
|
|
|
|
struct list_item
|
|
{
|
|
const struct entry *p_entry;
|
|
uint32_t hold_start_time;
|
|
uint32_t last_repeat_time;
|
|
enum key_state state;
|
|
bool mods[MOD_LAST];
|
|
};
|
|
|
|
|
|
|
|
static const struct entry kbd_entries[][NUM_OF_COLS] =
|
|
{
|
|
{{KEY_F5,KEY_F10}, {KEY_F4,KEY_F9}, {KEY_F3,KEY_F8},{KEY_F2,KEY_F7}, {KEY_F1,KEY_F6}, {'`','~'},{'3','#'}, {'2','@'}},
|
|
{{KEY_BACKSPACE}, {KEY_DEL,KEY_END},{KEY_CAPS_LOCK},{KEY_TAB,KEY_HOME},{KEY_ESC,KEY_BREAK},{'4','$'},{'E'}, {'W'}},
|
|
{{'P'}, {'=','+'}, {'-','_'}, {'\\','|'}, {'/','?'}, {'R'}, {'S'}, {'1','!'}},
|
|
{{KEY_ENTER,KEY_INSERT},{'8','*'}, {'7','&'}, {'6','^'}, {'5','%'}, {'F'}, {'X'}, {'Q'}},
|
|
{{'.','>'}, {'I'}, {'U'}, {'Y'}, {'T'}, {'V'}, {';',':'}, {'A'}},
|
|
{{'L'}, {'K'}, {'J'}, {'H'}, {'G'}, {'C'}, {'\'','"'},{'Z'}},
|
|
{{'O'}, {',','<'}, {'M'}, {'N'}, {'B'}, {'D'}, {' '}, { }},
|
|
|
|
};
|
|
|
|
static const struct entry btn_entries[NUM_OF_BTNS] =
|
|
{
|
|
{.mod = MOD_ALT},
|
|
{.mod = MOD_CTRL},
|
|
{.mod = MOD_SHL},
|
|
{.mod = MOD_SHR},
|
|
{'0',')'},
|
|
{'9','('},
|
|
{']','}'},
|
|
{'[','{'},
|
|
{KEY_RIGHT},
|
|
{KEY_UP,KEY_PAGE_UP},
|
|
{KEY_DOWN,KEY_PAGE_DOWN},
|
|
{KEY_LEFT}
|
|
|
|
};
|
|
|
|
static struct {
|
|
lock_callback _lock_callback;
|
|
key_callback _key_callback;
|
|
|
|
struct list_item list[KEY_LIST_SIZE];
|
|
|
|
uint32_t last_process_time;
|
|
|
|
bool mods[MOD_LAST];
|
|
|
|
bool capslock_changed;
|
|
bool capslock;
|
|
|
|
bool numlock_changed;
|
|
bool numlock;
|
|
} self;
|
|
|
|
void output_string(char*str){
|
|
if (!self._key_callback) return;
|
|
|
|
while(*str){
|
|
self._key_callback(*str, KEY_STATE_PRESSED);
|
|
str++;
|
|
}
|
|
}
|
|
static void transition_to(struct list_item * const p_item, const enum key_state next_state)
|
|
{
|
|
bool output = true;
|
|
const struct entry * const p_entry = p_item->p_entry;
|
|
|
|
p_item->state = next_state;
|
|
|
|
if (!self._key_callback || !p_entry)
|
|
return;
|
|
|
|
char chr = p_entry->chr;
|
|
|
|
switch (p_entry->mod) {
|
|
case MOD_ALT:
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
|
|
chr = KEY_MOD_ALT;
|
|
break;
|
|
|
|
case MOD_SHL:
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
|
|
chr = KEY_MOD_SHL;
|
|
break;
|
|
|
|
case MOD_SHR:
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
|
|
chr = KEY_MOD_SHR;
|
|
break;
|
|
|
|
case MOD_SYM:
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
|
|
chr = KEY_MOD_SYM;
|
|
break;
|
|
case MOD_CTRL:
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_REPORT_MODS))
|
|
chr = KEY_MOD_CTRL;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
//toggle operation
|
|
if(chr == KEY_CAPS_LOCK && next_state == KEY_STATE_PRESSED ){
|
|
if(self.capslock == true){
|
|
self.capslock = false;
|
|
}else{
|
|
self.capslock = true;
|
|
}
|
|
self.capslock_changed = true;
|
|
}
|
|
|
|
if (reg_is_bit_set(REG_ID_CFG, CFG_USE_MODS)) {
|
|
const bool shift = (self.mods[MOD_SHL] || self.mods[MOD_SHR]);
|
|
const bool alt = self.mods[MOD_ALT] | self.numlock;
|
|
const bool ctrl = self.mods[MOD_CTRL];//shortcuts control
|
|
|
|
if (shift && (chr <'A' || chr >'Z')) {
|
|
chr = p_entry->symb;
|
|
}else if(self.capslock && (chr >= 'A' && chr <= 'Z')){
|
|
//pass
|
|
}
|
|
else if(alt){
|
|
//ctrl for operators
|
|
if(next_state == KEY_STATE_PRESSED) {
|
|
if(chr == ',' || chr == '.' || chr == ' ' || chr == 'B'){
|
|
output = false;
|
|
}
|
|
if(chr == 'I'){
|
|
output = true;
|
|
chr = KEY_INSERT;
|
|
}
|
|
}
|
|
|
|
if( next_state == KEY_STATE_RELEASED ) {
|
|
if(chr == ',' || chr == '.' || chr == ' ' || chr == 'B'){
|
|
output = false;
|
|
}
|
|
if(chr == 'I'){
|
|
output = true;
|
|
chr = KEY_INSERT;
|
|
}
|
|
}
|
|
|
|
if(next_state == KEY_STATE_RELEASED) {
|
|
if(chr ==','){
|
|
lcd_backlight_update(-LCD_BACKLIGHT_STEP);
|
|
}else if(chr =='.'){
|
|
lcd_backlight_update(LCD_BACKLIGHT_STEP);
|
|
}else if(chr == ' '){
|
|
//loop update keyboard backlight
|
|
kbd_backlight_update_offset();
|
|
}else if(chr == 'B'){
|
|
show_bat_segs();
|
|
}
|
|
}
|
|
}
|
|
else if (!shift && (chr >= 'A' && chr <= 'Z')) {
|
|
chr = (chr + ' ');// uppercase to lowercase for a to z
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (chr != 0 && output==true) {
|
|
if(next_state == KEY_STATE_HOLD){
|
|
if( (chr >= 32 && chr <= 127) || chr == KEY_ENTER || chr == KEY_TAB || chr == KEY_DEL || chr == KEY_BACKSPACE || chr == KEY_UP || chr == KEY_DOWN || chr == KEY_RIGHT || chr == KEY_LEFT ) {
|
|
self._key_callback(chr, KEY_STATE_PRESSED);
|
|
}else{
|
|
self._key_callback(chr, next_state);
|
|
}
|
|
}else{
|
|
self._key_callback(chr, next_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void next_item_state(struct list_item * const p_item, const bool pressed)
|
|
{
|
|
switch (p_item->state) {
|
|
case KEY_STATE_IDLE:
|
|
if (pressed) {
|
|
if (p_item->p_entry->mod != MOD_NONE)
|
|
self.mods[p_item->p_entry->mod] = true;
|
|
|
|
if (!self.capslock_changed && self.mods[MOD_SHR] && self.mods[MOD_ALT]) {
|
|
self.capslock = true;
|
|
self.capslock_changed = true;
|
|
}
|
|
|
|
if (!self.numlock_changed && self.mods[MOD_SHL] && self.mods[MOD_ALT]) {
|
|
self.numlock = true;
|
|
self.numlock_changed = true;
|
|
}
|
|
|
|
if (!self.capslock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
|
|
self.capslock = false;
|
|
self.capslock_changed = true;
|
|
}
|
|
|
|
if (!self.numlock_changed && (self.mods[MOD_SHL] || self.mods[MOD_SHR])) {
|
|
self.numlock = false;
|
|
self.numlock_changed = true;
|
|
}
|
|
|
|
if (!self.mods[MOD_ALT]) {
|
|
self.capslock_changed = false;
|
|
self.numlock_changed = false;
|
|
}
|
|
|
|
if (self._lock_callback && (self.capslock_changed || self.numlock_changed))
|
|
self._lock_callback(self.capslock_changed, self.numlock_changed);
|
|
|
|
transition_to(p_item, KEY_STATE_PRESSED);
|
|
|
|
p_item->hold_start_time = time_uptime_ms();
|
|
p_item->last_repeat_time = 0;
|
|
}
|
|
break;
|
|
|
|
case KEY_STATE_PRESSED:
|
|
if ((time_uptime_ms() - p_item->hold_start_time) > KEY_HOLD_TIME) {
|
|
transition_to(p_item, KEY_STATE_HOLD);
|
|
} else if(!pressed) {
|
|
transition_to(p_item, KEY_STATE_RELEASED);
|
|
}
|
|
break;
|
|
|
|
case KEY_STATE_HOLD:
|
|
if (!pressed){
|
|
transition_to(p_item, KEY_STATE_RELEASED);
|
|
}else{
|
|
if ((time_uptime_ms() - p_item->hold_start_time) > KEY_HOLD_TIME) {
|
|
if(time_uptime_ms() - p_item->last_repeat_time > 100) {
|
|
transition_to(p_item, KEY_STATE_HOLD);
|
|
p_item->last_repeat_time = time_uptime_ms();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_STATE_RELEASED:
|
|
{
|
|
if (p_item->p_entry->mod != MOD_NONE)
|
|
self.mods[p_item->p_entry->mod] = false;
|
|
|
|
p_item->p_entry = NULL;
|
|
transition_to(p_item, KEY_STATE_IDLE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void keyboard_process(void)
|
|
{
|
|
struct port_config port_init;
|
|
js_bits = 0xff;
|
|
|
|
if ((time_uptime_ms() - self.last_process_time) <= KEY_POLL_TIME)
|
|
return;
|
|
|
|
port_get_config_defaults(&port_init);
|
|
|
|
for (uint8_t c = 0; c < NUM_OF_COLS; ++c) {
|
|
uint8_t col_value = 0;
|
|
port_init.direction = PORT_PIN_DIR_OUTPUT;
|
|
port_pin_set_config(col_pins[c], &port_init);
|
|
port_pin_set_output_level(col_pins[c], 0);
|
|
|
|
for (uint8_t r = 0; r < NUM_OF_ROWS; ++r) {
|
|
uint8_t pin_value = port_pin_get_input_level(row_pins[r]);
|
|
const bool pressed = (pin_value == 0);
|
|
uint8_t row_bit = (1<<r);
|
|
if(pressed){
|
|
if(c == 1 && r == 4){//enter key as fire
|
|
js_bits &= ~row_bit;
|
|
}
|
|
col_value &= ~row_bit;
|
|
}else{
|
|
if(c == 1 && r == 4){//enter key as fire
|
|
js_bits |= row_bit;
|
|
}
|
|
col_value |= row_bit;
|
|
}
|
|
const int32_t key_idx = (int32_t)((r * NUM_OF_COLS) + c);
|
|
|
|
int32_t list_idx = -1;
|
|
for (int32_t i = 0; i < KEY_LIST_SIZE; ++i) {
|
|
if (self.list[i].p_entry != &((const struct entry*)kbd_entries)[key_idx])
|
|
continue;
|
|
|
|
list_idx = i;
|
|
break;
|
|
}
|
|
|
|
if (list_idx > -1) {
|
|
next_item_state(&self.list[list_idx], pressed);
|
|
continue;
|
|
}
|
|
|
|
if (!pressed)
|
|
continue;
|
|
|
|
for (uint32_t i = 0 ; i < KEY_LIST_SIZE; ++i) {
|
|
if (self.list[i].p_entry != NULL)
|
|
continue;
|
|
|
|
self.list[i].p_entry = &((const struct entry*)kbd_entries)[key_idx];
|
|
self.list[i].state = KEY_STATE_IDLE;
|
|
next_item_state(&self.list[i], pressed);
|
|
|
|
break;
|
|
}
|
|
}
|
|
io_matrix[c] = col_value;
|
|
port_pin_set_output_level(col_pins[c], 1);
|
|
|
|
port_init.direction = PORT_PIN_DIR_INPUT;
|
|
port_init.input_pull = PORT_PIN_PULL_NONE;
|
|
port_pin_set_config(col_pins[c], &port_init);
|
|
}
|
|
|
|
#if NUM_OF_BTNS > 0
|
|
for (uint8_t b = 0; b < NUM_OF_BTNS; ++b) {
|
|
uint8_t pin_value = port_pin_get_input_level(btn_pins[b]);
|
|
const bool pressed = (pin_value == 0);
|
|
if( b < 8 ) {// read BTN1->BTN8
|
|
|
|
if(pressed){
|
|
io_matrix[b] &= ~(1 << 7);
|
|
}else{
|
|
io_matrix[b] |= ( 1 << 7);
|
|
}
|
|
}else{//c64 joystick arrow keys
|
|
//B12=left,, B11=down,B10 = up,B9 = right
|
|
uint8_t btn_bts = b-8;
|
|
if(pressed){
|
|
js_bits &= ~(1<<btn_bts);
|
|
}else{
|
|
js_bits |= (1<<btn_bts);
|
|
}
|
|
}
|
|
int8_t list_idx = -1;
|
|
for (int8_t i = 0; i < KEY_LIST_SIZE; ++i) {
|
|
if (self.list[i].p_entry != &((const struct entry*)btn_entries)[b])
|
|
continue;
|
|
|
|
list_idx = i;
|
|
break;
|
|
}
|
|
|
|
if (list_idx > -1) {
|
|
next_item_state(&self.list[list_idx], pressed);
|
|
continue;
|
|
}
|
|
|
|
if (!pressed)
|
|
continue;
|
|
|
|
for (uint8_t i = 0 ; i < KEY_LIST_SIZE; ++i) {
|
|
if (self.list[i].p_entry != NULL)
|
|
continue;
|
|
|
|
self.list[i].p_entry = &((const struct entry*)btn_entries)[b];
|
|
self.list[i].state = KEY_STATE_IDLE;
|
|
next_item_state(&self.list[i], pressed);
|
|
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
io_matrix[8] = 0xFF;
|
|
self.last_process_time = time_uptime_ms();
|
|
}
|
|
|
|
void keyboard_set_key_callback(key_callback callback)
|
|
{
|
|
self._key_callback = callback;
|
|
}
|
|
|
|
void keyboard_set_lock_callback(lock_callback callback)
|
|
{
|
|
self._lock_callback = callback;
|
|
}
|
|
|
|
bool keyboard_get_capslock(void)
|
|
{
|
|
return self.capslock;
|
|
}
|
|
|
|
bool keyboard_get_numlock(void)
|
|
{
|
|
return self.numlock;
|
|
}
|
|
|
|
void keyboard_init(void)
|
|
{
|
|
struct port_config port_init;
|
|
port_get_config_defaults(&port_init);
|
|
|
|
for (int i = 0; i < MOD_LAST; ++i)
|
|
self.mods[i] = false;
|
|
|
|
// Rows
|
|
port_init.direction = PORT_PIN_DIR_INPUT;
|
|
port_init.input_pull = PORT_PIN_PULL_UP;
|
|
for (uint32_t i = 0; i < NUM_OF_ROWS; ++i)
|
|
port_pin_set_config(row_pins[i], &port_init);
|
|
|
|
// Cols
|
|
port_init.direction = PORT_PIN_DIR_INPUT;
|
|
port_init.input_pull = PORT_PIN_PULL_NONE;
|
|
for(uint32_t i = 0; i < NUM_OF_COLS; ++i)
|
|
port_pin_set_config(col_pins[i], &port_init);
|
|
|
|
// Btns
|
|
#if NUM_OF_BTNS > 0
|
|
port_init.direction = PORT_PIN_DIR_INPUT;
|
|
port_init.input_pull = PORT_PIN_PULL_UP;
|
|
for(uint32_t i = 0; i < NUM_OF_BTNS; ++i)
|
|
port_pin_set_config(btn_pins[i], &port_init);
|
|
#endif
|
|
}
|