add compose support

This commit is contained in:
Charlotte 🦝 Delenk 2022-10-07 08:15:56 +01:00
parent b99fab1281
commit e49414745e
13 changed files with 47771 additions and 2 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
#include "compose.h"
#include <stdint.h>
#include "action.h"
#include "keycode.h"
#include "neo2.h"
#include "print.h"
#include "quantum_keycodes.h"
#include "unicode/unicode.h"
static int charsWritten, currentState;
static void resetState(void) {
print("resetState()\n");
charsWritten = 0;
currentState = 0;
}
static void finishCompose(const char* result) {
uprintf("Finishing compose with result: %s (%d chars written)\n", result, charsWritten);
while(charsWritten) {
tap_code(KC_BACKSPACE);
charsWritten--;
}
send_unicode_string(result);
resetState();
}
typedef struct {
uint16_t first_edge_idx;
uint16_t edge_count;
} compose_node_t;
typedef struct {
const char * value;
uint16_t c;
uint16_t next_node;
} compose_edge_t;
#include "composeTable.h"
static void checkComposeState(void) {
print("checkComposeState()\n");
if(currentState * sizeof(compose_node_t) > sizeof(nodes)) {
finishCompose("[error in compose]");
}
}
static const compose_edge_t *findEdge(uint16_t keycode) {
checkComposeState();
uint32_t uc = keystroke_to_unicode(keycode);
uprintf("Resolved keycode %d: %ld\n", keycode, uc);
uint16_t match1 = (uint16_t)uc; // yes only bmp support, keyboard doesnt have any astral keycodes anyways
uint16_t match2 = 0;
if(keycode < 256) {
match2 = 0xD800 + keycode;
print("(low keycode)\n");
} else if(keycode == KC_LEAD) {
match2 = 0xD800;
print("(leader key)\n");
}
for(int i = nodes[currentState].first_edge_idx; i < nodes[currentState].first_edge_idx + nodes[currentState].edge_count; i++) {
if(compose_edges[i].c == match1 || compose_edges[i].c == match2) {
uprintf("found matching node: %d -> %d\n", currentState, i);
return &compose_edges[i];
}
}
print("no matching edge found.\n");
return NULL;
}
// yes this breaks with emoji
static size_t charcount_utf8(const char * s) {
size_t n = 0;
while(*s)
if((*(s++) & 0xC0) != 0x80)
n++; // only count non-continuation characters
return n;
}
static void transEdge(const compose_edge_t *edge) {
uprintf("Transitioning to node %d\n", edge->next_node);
if(edge->value) {
uprintf("new node has value %s\n", edge->value);
finishCompose(edge->value);
if(edge->next_node != 0) // not necessarily the final product
charsWritten = charcount_utf8(edge->value);
}
currentState = edge->next_node;
checkComposeState();
}
static bool isCombining(uint32_t uc) {
// yes it is not all of them, but all of them that matter for us
if(uc >= 0x300 && uc < 0x370)
return true;
if(uc == 0x20d7)
return true;
return false;
}
void process_compose(uint16_t keycode, keyrecord_t *record) {
if(!record->event.pressed)
return;
const compose_edge_t * edge = findEdge(keycode);
if(edge == NULL) { // no matching edge found, reset and try again
print("No matching edge found, reset.\n");
resetState();
edge = findEdge(keycode);
}
if(edge == NULL) {
print("No compose edge found.\n");
return; // nothing found
}
// before we can advance the compose state, we need to prepare certain keys (dead keys, the leader key) because otherwise our deletion will delete too much (which of course is something we do not want)
if(isCombining(keystroke_to_unicode(keycode))) {
print("keycode refers to deadkey, writing space\n");
tap_code(KC_SPACE);
} else if(keycode == KC_LEAD) {
print("keycode refers to leader key, writing compose symbol\n");
send_unicode_string("");
}
charsWritten++;
transEdge(edge);
}

View file

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
#include "action.h"
void process_compose(uint16_t keycode, keyrecord_t *record);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,290 @@
data = {
' ': [(1, 'space')],
'!': [(3, 'k')],
'"': [(3, ',')],
'#': [(3, 'ü')],
'$': [(2, '6'), (3, 'ö')],
'%': [(3, 'm'), (3, 'p0')],
'&': [(3, 'q')],
"'": [(3, '.')],
"(": [(3, 'n')],
")": [(3, 'r')],
"*": [(1, 'kp_asterisk'), (2, 'kp_asterisk'), (3, 'o'), (4, '0')],
"+": [(1, 'kp_plus'), (2, 'kp_plus'), (3, 'b'), (4, 'q')],
",": [(1, 'kp_dot'), (3, 'kp_dot'), (4, 'd')],
"-": [(1, '-'), (1, 'kp_minus'), (2, 'kp_minus'), (3, 't'), (4, '-')],
".": [(1, '.'), (2, 'kp_dot'), (4, 'y')],
"/": [(1, 'kp_slash'), (2, 'kp_slash'), (3, 'i'), (4, '9')],
"0": [(1, '0'), (1, 'p0'), (4, ' ')],
"1": [(1, '1'), (1, 'p1'), (4, 'm')],
"2": [(1, '2'), (1, 'p2'), (4, ',')],
"3": [(1, '3'), (1, 'p3'), (4, '.')],
"4": [(1, '4'), (1, 'p4'), (4, 'n')],
"5": [(1, '5'), (1, 'p5'), (4, 'r')],
"6": [(1, '6'), (1, 'p6'), (4, 't')],
"7": [(1, '7'), (1, 'p7'), (4, 'h')],
"8": [(1, '8'), (1, 'p8'), (4, 'g')],
"9": [(1, '9'), (1, 'p9'), (4, 'f')],
":": [(3, 'd'), (3, 'p5'), (4, 'b')],
";": [(3, 'j'), (4, 'j')],
"<": [(3, 'h')],
"=": [(3, 'f'), (3, 'num_lock')],
">": [(3, 'g')],
'?': [(3, 's')],
'@': [(3, 'y')],
'[': [(3, 'l')],
'\\': [(3, 'u')],
']': [(3, 'c')],
'^': [(3, 'w')],
'_': [(3, 'v')],
'`': [(3, 'z')],
'{': [(3, 'a')],
'|': [(3, 'ä')],
'}': [(3, 'e')],
'~': [(3, 'p')],
'\u00a0': [(5, ' ')],
'¡': [(4, 'k')],
'¢': [(3, '6')],
'£': [(4, '6')],
'¤': [(4, '7')],
'¥': [(3, '7')],
'§': [(2, '2')],
'ª': [(4, '1')],
'«': [(2, '5')],
'¬': [(6, '1')],
'\u00ad': [(6, '-')],
'°': [(2, '1')],
'±': [(3, 'kp_plus')],
'²': [(3, '2')],
'³': [(3, '3')],
'·': [(4, '5')],
'¹': [(3, '1')],
'º': [(4, '2')],
'»': [(2, '4')],
'¿': [(4, 's')],
'Ä': [(2, 'ä')],
'Ö': [(2, 'ö')],
'×': [(4, 'kp_asterisk'), (6, 'k')],
'Ü': [(2, 'ü')],
'ß': [(1, 'ß')],
'ä': [(1, 'ä')],
'ö': [(1, 'ö')],
'÷': [(3, 'kp_slash')],
'ü': [(1, 'ü')],
'ſ': [(3, 'ß')],
'Γ': [(6, 'g')],
'Δ': [(6, 'd')],
'Θ': [(6, 'j')],
'Λ': [(6, 'l')],
'Ξ': [(6, 'x')],
'Π': [(6, 'p')],
'Σ': [(6, 's')],
'Φ': [(6, 'f')],
'Ψ': [(6, 'h')],
'Ω': [(6, 'w')],
'α': [(5, 'a')],
'β': [(5, 'b')],
'γ': [(5, 'g')],
'δ': [(5, 'd')],
'ε': [(5, 'e')],
'ζ': [(5, 'z')],
'η': [(5, 'ä')],
'θ': [(5, 'j')],
'ι': [(5, 'i')],
'κ': [(5, 'k')],
'λ': [(5, 'l')],
'μ': [(5, 'm')],
'ν': [(5, 'n')],
'ξ': [(5, 'x')],
'ο': [(5, 'o')],
'π': [(5, 'p')],
'ρ': [(5, 'r')],
'τ': [(5, 't')],
'ς': [(5, 'ß')],
'σ': [(5, 's')],
'υ': [(5, 'y')],
'φ': [(5, 'f')],
'χ': [(5, 'c')],
'ψ': [(5, 'h')],
'ω': [(5, 'w')],
'ϑ': [(5, '.')],
'ϕ': [(5, 'q')],
'ϰ': [(5, '7')],
'ϱ': [(5, ',')],
'ϵ': [(5, 'ö')],
'': [(2, 'ß')],
'\u2011': [(5, '-')],
'\u2013': [(2, ',')],
'\u2014': [(2, '-')],
'': [(3, '9')],
'': [(3, '0')],
'': [(3, '8')],
'': [(2, '9')],
'': [(2, '0')],
'': [(2, '8')],
'': [(2, 'p9')],
'': [(2, '.')],
'': [(2, 'p6')],
'': [(3, 'x')],
'\u202f': [(6, ' ')],
'': [(5, 'p0')],
'': [(5, 'kp_dot')],
'': [(6, 'kp_dot')],
'': [(3, '5')],
'': [(3, '4')],
'\u2044': [(4, 'kp_slash')],
'': [(5, '0')],
'': [(5, '1')],
'': [(5, '2')],
'': [(5, '3')],
'': [(2, '7'), (2, 'p5')],
'': [(6, 'c')],
'': [(2, '3')],
'': [(6, 'n')],
'': [(4, '3')],
'': [(6, 'q')],
'': [(6, 'r')],
'': [(6, 'z')],
'': [(6, 'ä')],
'': [(3, 'p4')],
'': [(3, 'p8')],
'': [(3, 'p6'), (6, '7')],
'': [(3, 'p2')],
'': [(3, 'p1')],
'': [(3, 'p7')],
'': [(6, '.')],
'': [(3, 'p3')],
'': [(6, 'b')],
'': [(6, ',')],
'': [(6, 'm')],
'': [(6, 'a')],
'': [(6, 't')],
'': [(6, 'e')],
'': [(6, '0')],
'': [(6, 'y')],
'': [(6, 'o')],
'\u2212': [(3, 'kp_minus'), (4, 'ß')],
'': [(4, 'kp_plus')],
'': [(6, 'kp_plus')],
'': [(4, 'kp_minus')],
'': [(6, 'ß')],
'': [(6, 'v')],
'': [(6, '9')],
'': [(6, '8')],
'': [(6, '5')],
'\u2223': [(6, 'kp_slash')],
'': [(6, '6')],
'': [(6, '3')],
'': [(6, '2')],
'': [(5, 'p8'), (6, 'ö')],
'': [(5, 'p2'), (6, 'ü')],
'': [(6, 'i')],
'': [(6, 'kp_minus')],
'': [(5, 'num_lock')],
'': [(4, 'num_lock')],
'': [(6, 'num_lock')],
'': [(5, 'p1')],
'': [(5, 'p3')],
'': [(5, 'p7')],
'': [(5, 'p9')],
'': [(5, 'p4'), (6, 'u')],
'': [(5, 'p6')],
'': [(6, 'p4')],
'': [(6, 'p6')],
'': [(5, 'kp_plus')],
'': [(5, 'kp_minus')],
'': [(6, 'kp_asterisk')],
'': [(5, 'kp_asterisk')],
'': [(6, '4')],
'': [(5, 'p5')],
'': [(6, 'p5')],
'': [(6, 'p8')],
'': [(6, 'p2')],
'': [(3, 'kp_asterisk')],
'': [(5, 'kp_slash')],
'': [(6, 'p7')],
'': [(6, 'p9')],
'': [(6, 'p1')],
'': [(6, 'p3')],
'': [(2, 'p0')],
'': [(6, 'p0')],
'': [(2, 'p3')],
'': [(2, 'p4')],
'': [(2, 'p2')],
'': [(2, 'p1')],
'': [(5, '4')],
'': [(5, '5')],
'': [(5, '6')],
'': [(2, 'p7')],
'': [(2, 'p8')],
'': [(5, '8')],
'': [(5, '9')]
}
for c in range(0x41, 0x5B):
c = chr(c)
data[c] = [(2, c.lower())]
data[c.lower()] = [(1, c.lower())]
trans = {
'x': 'q',
'v': 'w',
'l': 'e',
'c': 'r',
'w': 't',
'k': 'y',
'h': 'u',
'g': 'i',
'f': 'o',
'q': 'p',
'ß': 'lbrc',
'u': 'a',
'i': 's',
'a': 'd',
'e': 'f',
'o': 'g',
's': 'h',
'n': 'j',
'r': 'k',
't': 'l',
'd': 'scln',
'y': 'quot',
'ü': 'z',
'ö': 'x',
'ä': 'c',
'p': 'v',
'z': 'b',
'b': 'n',
',': 'comm',
'.': 'dot',
'j': 'slsh',
'-': 'mins',
' ': 'space'
}
keys = {}
def insert_key(key, layer, char):
if key in trans:
key = trans[key]
if key not in keys:
keys[key] = [0, 0, 0, 0, 0, 0]
if keys[key][layer] != 0:
raise Exception(f"duplicate key/layer combo: {key},{layer}={char}, current value: {chr(keys[key][layer])}")
keys[key][layer] = ord(char)
for (char, value) in data.items():
for (layer, key) in value:
insert_key(key, layer - 1, char)
print("const uint16_t keycode_to_char[][6] = {")
for (key, value) in keys.items():
print(f" [KC_{key.upper()}] = {{")
for char in value:
print(f" {hex(char)},")
print(" },")
print("};")

View file

@ -0,0 +1,482 @@
const uint16_t keycode_to_char[][6] = {
[KC_SPACE] = {
0x20,
0x0,
0x0,
0x30,
0xa0,
0x202f,
},
[KC_Y] = {
0x6b,
0x4b,
0x21,
0xa1,
0x3ba,
0xd7,
},
[KC_COMM] = {
0x0,
0x2013,
0x22,
0x32,
0x3f1,
0x21d2,
},
[KC_Z] = {
0xfc,
0xdc,
0x23,
0x0,
0x0,
0x222a,
},
[KC_6] = {
0x36,
0x24,
0xa2,
0xa3,
0x26a5,
0x2225,
},
[KC_X] = {
0xf6,
0xd6,
0x24,
0x0,
0x3f5,
0x2229,
},
[KC_M] = {
0x6d,
0x4d,
0x25,
0x31,
0x3bc,
0x21d4,
},
[KC_P0] = {
0x30,
0x2423,
0x25,
0x0,
0x2030,
0x25a1,
},
[KC_P] = {
0x71,
0x51,
0x26,
0x2b,
0x3d5,
0x211a,
},
[KC_DOT] = {
0x2e,
0x2022,
0x27,
0x33,
0x3d1,
0x21a6,
},
[KC_J] = {
0x6e,
0x4e,
0x28,
0x34,
0x3bd,
0x2115,
},
[KC_K] = {
0x72,
0x52,
0x29,
0x35,
0x3c1,
0x211d,
},
[KC_KP_ASTERISK] = {
0x2a,
0x2a,
0x22c5,
0xd7,
0x2299,
0x2297,
},
[KC_G] = {
0x6f,
0x4f,
0x2a,
0x0,
0x3bf,
0x2208,
},
[KC_0] = {
0x30,
0x201d,
0x2019,
0x2a,
0x2080,
0x2205,
},
[KC_KP_PLUS] = {
0x2b,
0x2b,
0xb1,
0x2213,
0x2295,
0x2214,
},
[KC_N] = {
0x62,
0x42,
0x2b,
0x3a,
0x3b2,
0x21d0,
},
[KC_KP_DOT] = {
0x2c,
0x2e,
0x2c,
0x0,
0x2032,
0x2033,
},
[KC_SCLN] = {
0x64,
0x44,
0x3a,
0x2c,
0x3b4,
0x394,
},
[KC_MINS] = {
0x2d,
0x2014,
0x0,
0x2d,
0x2011,
0xad,
},
[KC_KP_MINUS] = {
0x2d,
0x2d,
0x2212,
0x2216,
0x2296,
0x2238,
},
[KC_L] = {
0x74,
0x54,
0x2d,
0x36,
0x3c4,
0x2202,
},
[KC_QUOT] = {
0x79,
0x59,
0x40,
0x2e,
0x3c5,
0x2207,
},
[KC_KP_SLASH] = {
0x2f,
0x2f,
0xf7,
0x2044,
0x2300,
0x2223,
},
[KC_S] = {
0x69,
0x49,
0x2f,
0x0,
0x3b9,
0x222b,
},
[KC_9] = {
0x39,
0x201c,
0x2018,
0x2f,
0x27e9,
0x221d,
},
[KC_1] = {
0x31,
0xb0,
0xb9,
0xaa,
0x2081,
0xac,
},
[KC_P1] = {
0x31,
0x2666,
0x2194,
0x0,
0x2264,
0x230a,
},
[KC_2] = {
0x32,
0xa7,
0xb2,
0xba,
0x2082,
0x2228,
},
[KC_P2] = {
0x32,
0x2665,
0x2193,
0x0,
0x222a,
0x22c3,
},
[KC_3] = {
0x33,
0x2113,
0xb3,
0x2116,
0x2083,
0x2227,
},
[KC_P3] = {
0x33,
0x2660,
0x21cc,
0x0,
0x2265,
0x230b,
},
[KC_4] = {
0x34,
0xbb,
0x203a,
0x0,
0x2640,
0x22a5,
},
[KC_P4] = {
0x34,
0x2663,
0x2190,
0x0,
0x2282,
0x2286,
},
[KC_5] = {
0x35,
0xab,
0x2039,
0xb7,
0x2642,
0x2221,
},
[KC_P5] = {
0x35,
0x20ac,
0x3a,
0x0,
0x22b6,
0x22b7,
},
[KC_P6] = {
0x36,
0x2023,
0x2192,
0x0,
0x2283,
0x2287,
},
[KC_7] = {
0x37,
0x20ac,
0xa5,
0xa4,
0x3f0,
0x2192,
},
[KC_P7] = {
0x37,
0x2714,
0x2195,
0x0,
0x226a,
0x2308,
},
[KC_U] = {
0x68,
0x48,
0x3c,
0x37,
0x3c8,
0x3a8,
},
[KC_8] = {
0x38,
0x201e,
0x201a,
0x0,
0x27e8,
0x221e,
},
[KC_P8] = {
0x38,
0x2718,
0x2191,
0x0,
0x2229,
0x22c2,
},
[KC_I] = {
0x67,
0x47,
0x3e,
0x38,
0x3b3,
0x393,
},
[KC_P9] = {
0x39,
0x2020,
0x0,
0x0,
0x226b,
0x2309,
},
[KC_O] = {
0x66,
0x46,
0x3d,
0x39,
0x3c6,
0x3a6,
},
[KC_SLSH] = {
0x6a,
0x4a,
0x3b,
0x3b,
0x3b8,
0x398,
},
[KC_NUM_LOCK] = {
0x0,
0x0,
0x3d,
0x2260,
0x2248,
0x2261,
},
[KC_H] = {
0x73,
0x53,
0x3f,
0xbf,
0x3c3,
0x3a3,
},
[KC_E] = {
0x6c,
0x4c,
0x5b,
0x0,
0x3bb,
0x39b,
},
[KC_A] = {
0x75,
0x55,
0x5c,
0x0,
0x0,
0x2282,
},
[KC_R] = {
0x63,
0x43,
0x5d,
0x0,
0x3c7,
0x2102,
},
[KC_T] = {
0x77,
0x57,
0x5e,
0x0,
0x3c9,
0x3a9,
},
[KC_W] = {
0x76,
0x56,
0x5f,
0x0,
0x0,
0x221a,
},
[KC_B] = {
0x7a,
0x5a,
0x60,
0x0,
0x3b6,
0x2124,
},
[KC_D] = {
0x61,
0x41,
0x7b,
0x0,
0x3b1,
0x2200,
},
[KC_C] = {
0xe4,
0xc4,
0x7c,
0x0,
0x3b7,
0x2135,
},
[KC_F] = {
0x65,
0x45,
0x7d,
0x0,
0x3b5,
0x2203,
},
[KC_V] = {
0x70,
0x50,
0x7e,
0x0,
0x3c0,
0x3a0,
},
[KC_LBRC] = {
0xdf,
0x1e9e,
0x17f,
0x2212,
0x3c2,
0x2218,
},
[KC_Q] = {
0x78,
0x58,
0x2026,
0x0,
0x3be,
0x39e,
},
};

View file

@ -3,6 +3,8 @@
#include QMK_KEYBOARD_H
#include "keymap_steno.h"
#include "raw_hid.h"
#include "neo2.h"
#include "compose.h"
enum {
_MAIN,
_NUMPAD,
@ -144,7 +146,7 @@ static void send_unicode_chunk(const char * data, size_t length) {
#define UNI_CHUNKSIZE (RAW_EPSIZE - 3)
void send_unicode_string(const char * str) {
/*void send_unicode_string(const char * str) {
size_t length = strlen(str);
while(length > UNI_CHUNKSIZE) {
send_unicode_chunk(str, UNI_CHUNKSIZE);
@ -152,7 +154,7 @@ void send_unicode_string(const char * str) {
length -= UNI_CHUNKSIZE;
}
send_unicode_chunk(str, length);
}
}*/
void register_unicode(uint32_t codepoint) {
uint8_t buffer[4];
@ -184,4 +186,10 @@ void register_unicode(uint32_t codepoint) {
}
send_unicode_chunk((char *)buffer, size);
}
void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
track_neo2_modifier_state(keycode, record);
process_compose(keycode, record);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,250 @@
import re
from keysymdef import keysymdef
import json
keysym_regex = re.compile("^<([a-zA-Z0-9_]+)>$")
unicode_regex = re.compile("^<U([0-9a-fA-F]{4,6})>$")
keysym_map = {}
special_keysyms = {
"dead_abovedot": 0x307,
"dead_acute": 0x301,
"dead_belowdot": 0x323,
"dead_caron": 0x30C,
"dead_macron": 0x304,
"dead_stroke": 0x338,
"KP_0": "0xD800 + KC_P0",
"KP_1": "0xD800 + KC_P1",
"KP_2": "0xD800 + KC_P2",
"KP_3": "0xD800 + KC_P3",
"KP_4": "0xD800 + KC_P4",
"KP_5": "0xD800 + KC_P5",
"KP_6": "0xD800 + KC_P6",
"KP_7": "0xD800 + KC_P7",
"KP_8": "0xD800 + KC_P8",
"KP_9": "0xD800 + KC_P9",
"KP_Divide": "0xD800 + KC_KP_SLASH",
"KP_Add": "0xD800 + KC_KP_PLUS",
"KP_Equal": "0xD800 + KC_KP_EQUAL",
"KP_Space": 0x20,
"KP_Subtract": "0xD800 + KC_KP_MINUS",
"KP_Multiply": "0xD800 + KC_KP_ASTERISK",
"Multi_key": 0xD800,
"dead_abovering": 0x30A,
"dead_breve": 0x306,
"dead_cedilla": 0x327,
"dead_dasia": 0x314,
"dead_psili": 0x313,
"dead_circumflex": 0x304,
"dead_diaeresis": 0x308,
"dead_horn": 0x31B,
"dead_tilde": 0x303,
"dead_belowbreve": 0x32e,
"dead_belowcircumflex": 0x32d,
"dead_belowcomma": 0x326,
"dead_belowdiaeresis": 0x324,
"dead_belowmacron": 0x331,
"dead_belowring": 0x325,
"dead_belowtilde": 0x330,
"dead_grave": 0x300,
"dead_hook": 0x31b,
"underbar": 0x5f,
"dead_doubleacute": 0x30B,
"dead_doublegrave": 0x30F,
"dead_invertedbreve": 0x311,
"dead_iota": 0x345,
"dead_ogonek": 0x328,
"dead_semivoiced_sound": 0x309a,
"dead_voiced_sound": 0x3099,
"enfilledcircbullet": 0x2022,
"leftcaret": 0x3c,
"leftshoe": 0x2282,
"rightcaret": 0x3e,
"rightshoe": 0x2283,
"Tab": 9,
"hebrew_beth": 0x05D1,
"hebrew_daleth": 0x05D3,
"hebrew_gimmel": 0x05D2,
"hebrew_kuf": 0x5E7,
"hebrew_samekh": 0x5E1,
"hebrew_taf": 0x5ea,
"hebrew_teth": 0x5d8,
"hebrew_zadi": 0x5e6,
"hebrew_zayin": 0x5d6,
}
nodes = {
"children": {}
}
def ensure_node(path):
if path == ():
return nodes
parent = ensure_node(path[:-1])
if path[-1] not in parent["children"]:
parent["children"][path[-1]] = {"children": {}}
return parent["children"][path[-1]]
def create_node(path, result):
path = tuple(path)
node = ensure_node(path)
node["result"] = result
def walk_tree(base=None, pre_order=False):
if base is None:
base = nodes
if pre_order:
yield base
for child in base["children"].values():
yield from walk_tree(child, pre_order)
if not pre_order:
yield base
def walk_tree_2(base=None):
if base is None:
base = nodes
for k, v in base["children"].items():
yield from walk_tree_2(v)
yield (base, k, v)
def walk_leaves():
for node in walk_tree():
if len(node["children"]) == 0:
yield node
for (name, _, value) in keysymdef:
if value is None:
if name in special_keysyms:
value = special_keysyms[name]
else:
continue
keysym_map[name] = value
def parse_keysym(keysym: str):
unicode_match = unicode_regex.search(keysym)
if unicode_match is not None:
return int(unicode_match.group(1), 16)
normal_match = keysym_regex.search(keysym)
if normal_match is not None:
return keysym_map[normal_match.group(1)]
with open("keyboards/rkb1/keymaps/simple/XCompose") as fin:
for line in fin:
line = line.strip()
(keysyms, result) = line.split(":", 1)
keysyms = keysyms.strip()
result = result.strip()
keys = [parse_keysym(keysym) for keysym in keysyms.split()]
output = eval(result)
create_node(keys, output)
# ensure that every leaf can continue compose sequences
#for leaf in walk_leaves():
# if "result" not in leaf:
# continue
# if len(leaf["result"]) != 1:
# continue
# if ord(leaf["result"]) not in nodes["children"]:
# continue
# print(f"found recursable leaf {leaf}")
# leaf["children"] = nodes["children"][ord(leaf["result"])]["children"]
def freeze_dict(s):
kv_pairs = []
for key in sorted(s, key=lambda a: str(a)):
value = s[key]
if isinstance(value, dict):
value = freeze_dict(value)
kv_pairs.append((key, value))
return tuple(kv_pairs)
# merge identical outcomes
def merge_identical():
seen_nodes = {}
for parent, key, node in walk_tree_2():
frozen_node = freeze_dict(node)
if frozen_node in seen_nodes:
replacement_node = seen_nodes[frozen_node]
parent["children"][key] = replacement_node
else:
seen_nodes[frozen_node] = node
merge_identical()
# this flattens the tree
flat_nodes = []
edges = []
relocations = {}
seen = set()
for node in walk_tree(pre_order=True):
if len(node["children"]) == 0:
continue
if id(node) in seen:
continue
first_edge_idx = len(edges)
edge_count = len(node["children"])
for c, edge in node["children"].items():
value = edge["result"] if "result" in edge else None
edge_obj = {
"value": value,
"c": c
}
if id(edge) in relocations:
relocations[id(edge)].append(edge_obj)
else:
relocations[id(edge)] = [edge_obj]
edges.append(edge_obj)
flat_nodes.append({
"first_edge_idx": first_edge_idx,
"edge_count": edge_count,
"id": id(node)
})
seen.add(id(node))
# fixup relocations
for i, node in enumerate(flat_nodes):
if node["id"] not in relocations:
continue
for fixup in relocations[node["id"]]:
fixup["next_node"] = i
print("const static compose_node_t nodes[] = {")
for node in flat_nodes:
print(" {")
print(f" .first_edge_idx = {node['first_edge_idx']},")
print(f" .edge_count = {node['edge_count']},")
print(" },")
print("};")
def escape_str(x):
s = ""
for c in x.encode("utf-8"):
s += f"\\x{(hex(c)[2:]).zfill(2)}"
return s
print("const static compose_edge_t compose_edges[] = {")
for edge in edges:
print(" {")
print(f" .c = {edge['c']},")
if edge["value"] is not None:
escaped = escape_str(edge["value"])
print(f" .value = \"{escaped}\",")
if "next_node" in edge:
print(f" .next_node = {edge['next_node']},")
print(" },")
print("};")

View file

@ -0,0 +1,52 @@
#include "neo2.h"
#include "keycode.h"
#include "quantum.h"
static bool pressingLsft, pressingRsft, pressingCaps, pressingNUHS, pressingNUBS, pressingRalt, lockingShift, lockingMod4;
void track_neo2_modifier_state(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case KC_LEFT_SHIFT:
pressingLsft = record->event.pressed;
lockingShift ^= pressingLsft && pressingRsft;
break;
case KC_RIGHT_SHIFT:
pressingRsft = record->event.pressed;
lockingShift ^= pressingLsft && pressingRsft;
break;
case KC_CAPS:
pressingCaps = record->event.pressed;
break;
case KC_NUHS:
pressingNUHS = record->event.pressed;
break;
case KC_NUBS:
pressingNUBS = record->event.pressed;
lockingMod4 ^= pressingNUBS && pressingRalt;
break;
case KC_RALT:
pressingRalt = record->event.pressed;
lockingMod4 ^= pressingNUBS && pressingRalt;
break;
}
}
static int getLayer(void) {
bool mod2 = lockingShift ^ pressingLsft ^ pressingRsft;
bool mod3 = pressingCaps | pressingNUHS;
bool mod4 = lockingMod4 ^ pressingNUBS ^ pressingRalt;
if (mod4 && mod3) return 5;
if (mod4) return 3;
if (mod3 && mod2) return 4;
if (mod3) return 2;
if (mod2) return 1;
return 0;
}
#include "keyToCharTable.h"
uint32_t keystroke_to_unicode(uint16_t keycode) {
size_t elements = sizeof(keycode_to_char) / (6 * sizeof(uint16_t));
if (keycode > elements) {
return 0;
}
return (uint32_t)keycode_to_char[keycode][getLayer()];
}

View file

@ -0,0 +1,7 @@
#pragma once
#include "quantum/action.h"
#include <stdint.h>
void track_neo2_modifier_state(uint16_t keycode, keyrecord_t *record);
uint32_t keystroke_to_unicode(uint16_t keycode);

View file

@ -0,0 +1 @@
SRC += neo2.c compose.c

View file

@ -0,0 +1,82 @@
#pragma once
#include <stdint.h>
template <int entries>
struct BitSet {
BitSet<entries / 2> upper;
BitSet<entries / 2> lower;
bool operator[](size_t index) {
if (index < (entries / 2)) return lower[index];
return upper[index - (entries / 2)];
}
};
template <>
struct BitSet<64> {
uint64_t data;
bool operator[](size_t index) const {
return (bool)((data >> index) & 1);
}
};
template <>
struct BitSet<32> {
uint32_t data;
bool operator[](size_t index) const {
return (bool)((data >> index) & 1);
}
};
template <>
struct BitSet<16> {
uint16_t data;
bool operator[](size_t index) const {
return (bool)((data >> index) & 1);
}
};
template <>
struct BitSet<8> {
uint8_t data;
bool operator[](size_t index) const {
return (bool)((data >> index) & 1);
}
};
template <>
struct BitSet<1> {
bool data;
bool operator[](size_t index) const {
return data;
}
};
template <typename T, int keyBits, int entryBits>
struct SparseTable {
BitSet<1 << entryBits> presentEntries;
SparseTable<T, keyBits - entryBits, entryBits> entries[0];
const T *operator[](size_t index) const {
size_t subindex = (index >> (keyBits - entryBits)) & ((1 << entryBits) - 1);
if (!presentEntries[subindex]) return nullptr;
size_t realIndex = 0;
for (int i = 0; i < subindex; i++)
if (presentEntries[subindex]) realIndex++;
return entries[realIndex][index & ((1 << (keyBits - entryBits)) - 1)];
}
};
template <typename T, int entryBits>
struct SparseTable<T, 0, entryBits> {
T value;
const T *operator[](size_t index) const {
return &value;
}
};