Skip to content

Instantly share code, notes, and snippets.

@EtiTheSpirit
Last active June 10, 2024 18:51
Show Gist options
  • Save EtiTheSpirit/965f141f5a030cc04c36372d3a625ac1 to your computer and use it in GitHub Desktop.
Save EtiTheSpirit/965f141f5a030cc04c36372d3a625ac1 to your computer and use it in GitHub Desktop.
Enables QMK keyboard firmware to type sitelen pona natively.
// clang-format off
// Derived from the AHK script and upgraded by akesi seli San (Xan) for use in QMK keyboard firmware, written in C.
// This was written for the ZSA Moonlander but anything using QMK works. I don't know about ZMK. Never used it.
//
// THIS CODE IS INCOMPLETE; IT IS A SNIPPET FOR YOU TO PUT INTO YOUR OWN FIRMWARE SOURCE. IT WILL NOT BE EASY TO SETUP AND
// IS NOT PLUG AND PLAY. SETUP AND C KNOWLEDGE ARE BOTH REQUIRED, DONE SPECIFICALLY FOR YOUR OWN USE CASE. HANDLE WITH CARE.
//
// Major changes, and how this works:
// - This now includes ALL words, including nimisin from Fairfax HD's font.
// These words can be toggled via macros, as not all fonts support them.
// - Typing in sitelen pona now requires the use of "sitelen pona lock" (for lack of a better name), which needs a dedicated button to swap modes.
// YOU MUST ASSIGN THIS BUTTON A KEYCODE ENUM VALUE AND PUT IT ON YOUR KEYBOARD. You are expected to know how to add custom buttons to your keyboard.
// Pressing SPACE or ENTER will type the character.
// Pressing a modifier key with SPACE (for which modifier, see macros below) can be used to append compound/stacked/scaled characters
// Pressing the [ and ] keys (by default) creates a cartouche. Holding shift or alt changes this to a right and left extension respectively. Again, see macros below.
// Pressing ESC (by default) CANCELS the current character and turns off sitelen pona lock. Again, again, see macros below.
//
// - Fairfax HD was the primary focus of this modification and thus several macros later on enable or disable the use of font-specific features. Please handle with care.
// NOTE: THIS REQUIRES THE USE OF WINCOMPOSE OR ANOTHER UNICODE INPUT SOLUTION.
// FOR MORE INFORMATION, PLEASE READ: https://docs.qmk.fm/features/unicode
// TL;DR / EASY WAY:
// 1: Install WinCompose at http://wincompose.info/
// 2: Go to config.h and append: #define UNICODE_SELECTED_MODES UC_WINC // (* this may be different if you are running the latest QMK, I am running it old, see https://docs.qmk.fm/features/unicode )
// 3: APPEND THIS CODE TO process_record_user (see https://docs.qmk.fm/understanding_qmk#process-record ) ...
/*
bool allowContinue = process_record_sitelen_pona(keycode, record);
if (!allowContinue) {
return false; // Sitelen pona handled this, discard it from the normal keyboard workflow and stop now.
}
*/
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//// REQUIRED SETTINGS
// Please fill in the following statement to the keycode that should toggle "sitelen pona lock"
#define SITELEN_PONA_TOGGLE_KEYCODE SPEC_NP_EMU
// ^ For me, SPEC_NP_EMU is a custom keycode. THIS DEFAULT OPTION WILL NOT WORK FOR YOU. IT IS MINE, FOR MY OWN CODE, IN MY OWN KEYBOARD.
// YOU MUST ASSIGN THIS BUTTON A KEYCODE ENUM VALUE AND PUT IT ON YOUR KEYBOARD. You are expected to know how to add custom buttons to your keyboard.
// The default code uses this as a toggle in firmware and prevents it from being sent to the system.
// THIS MEANS IF YOU ASSIGN IT TO AN ACTUAL USABLE KEY, YOU WILL EFFECTIVELY "LOCK OUT" THAT KEY AND BE UNABLE TO USE IT FOR TYPING.
///////////////////////////////////////////////////////////////////////////
//// RECOMMENDED SETTINGS
#define SITELEN_PONA_CANCEL_KEYCODE KC_ESC
// Optional, set to 0 to disable it. If enabled, pressing this key will "cancel" sitelen pona lock
// This is something I added for me mostly because I would compulsively hit esc if I didn't want to type sp any more, or if I made a mistake.
// This discards the buffer and turns off the lock.
// ALLOW_STACKED_AND_SCALED_COMPOUNDS allows U+200D, U+F1995, and U+F1996 to be typed. These create standard, stacked, and scaled compound glyphs respectively.
#define SITELEN_PONA_ALLOW_STACKED_AND_SCALED_COMPOUNDS
// Holding any of the provided modifiers and pressing space will type the character (U+200D, U+F1995, U+F1996) needed to create a standard, stacked, or scaled compound.
#define SITELEN_PONA_COMPOUND_INTERSECT_KEYCODE KC_LSFT // Standard compounds.
#define SITELEN_PONA_COMPOUND_STACKED_KEYCODE KC_LCTRL // Stacked compounds.
#define SITELEN_PONA_COMPOUND_SCALED_KEYCODE KC_LALT // Scaled compounds.
// "Radicals" are special characters that surround or modify others. In this case, it includes
// - Cartouches (the boxes around names)
// - Glyph extensions (left and right), particularly useful with words like "pi". They can be grown by typing other sitelen pona, or with spaces.
#define SITELEN_PONA_RADICAL_OPEN KC_LBRACKET // Open a radical. Without modifiers, this starts a cartouche.
#define SITELEN_PONA_RADICAL_CLOSE KC_RBRACKET // Close a radical.
//
#define SITELEN_PONA_GLYPH_EXTEND_LEFT_KEYCODE KC_LALT // Holding this causes the above keys to start or stop a left extension.
#define SITELEN_PONA_GLYPH_EXTEND_RIGHT_KEYCODE KC_LSFT // Holding this causes the above keys to start or stop a right extension. This is what you use for stuff like pi
// ^ These two MUST be modifier keys!
///////////////////////////////////////////////////////////////////////////
//// FAIRFAX-SPECIFIC SETTINGS
// SETTINGS:
#define SITELEN_PONA_ALLOW_UNDEFINED_UCSUR_CODEPOINTS
// If DISABLED (commented out), only characters in [F1900−F19FF SITELEN PONA] and [F1C80−F1C9F SITELEN PONA RADICALS] can be used. These are universal.
// If DEFINED (not commented out), special characters (primarily nimisin, but some are official now) greater than FFA00 can be typed
#define SITELEN_PONA_ALLOW_FAIRFAX_ALTERNATE_SITELEN
// REQUIRES: ALLOW_UNDEFINED_UCSUR_CODEPOINTS (if undefined, this does nothing, hence why this is on by default)
// If DISABLED (commented out), only characters representing the primary sitelen pona glyph can be used.
// If ENABLED (not commented out), alternate characters (often typed by appending a number, i.e. akesi2 or a2) can be used. These are a feature primarily of Fairfax HD.
// #define SITELEN_PONA_ALLOW_FAIRFAX_ALT1
// ^ REQUIRES: ALLOW_FAIRFAX_ALTERNATE_SITELEN (if undefined, this does nothing)
// This fixes a mistake(?) made by the creator of Fairfax HD where some alternate sitelen (see above) would randomly end with 1 instead of 2, which is very rare.
// I assume this is a mistake. Uncommenting this will adjust it so you have to type with a 2+ afterward, like normal.
// This affects: olin, epiku, soko
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////
//// INTERNALS ////
#define SITELEN_PONA_COMPOUND_UNICODE "200D"
#define SITELEN_PONA_STACKED_COMPOUND_UNICODE "F1995"
#define SITELEN_PONA_SCALED_COMPOUND_UNICODE "F1996"
#define SITELEN_PONA_INTERPUNCT_UNICODE "F199C"
#define SITELEN_PONA_COLON_UNICODE "F199D"
#define SITELEN_PONA_CARTOUCHE_START_UNICODE "F1990"
#define SITELEN_PONA_CARTOUCHE_END_UNICODE "F1991"
#define SITELEN_PONA_LEFT_EXTEND_START_UNICODE "F199A"
#define SITELEN_PONA_RIGHT_EXTEND_START_UNICODE "F1997"
#define SITELEN_PONA_LEFT_EXTEND_END_UNICODE "F199B"
#define SITELEN_PONA_RIGHT_EXTEND_END_UNICODE "F1998"
// Handle the defines:
// This chain may or may not allow...
// FF_EXT "Fairfax Extended" - Used to declare alt variants i.e. a2, a3
// SP_NDEF "sitelen pona, Undefined" - Used to declare values out of UCSUR range like kiki
// FF_NDEF "Fairfax, Undefined" - Both of the above simultaneously (only works if both macros are defined simultaneously)
#ifdef SITELEN_PONA_ALLOW_UNDEFINED_UCSUR_CODEPOINTS
#define SP_NDEF(x, y) {x, y},
#ifdef SITELEN_PONA_ALLOW_FAIRFAX_ALTERNATE_SITELEN
#ifdef SITELEN_PONA_ALLOW_FAIRFAX_ALT1
#define _ALLOW_FAIRFAX_ALT1
#endif
#define FF_EXT(x, y) {x, y},
#define FF_NDEF(x, y) {x, y},
#else
#define FF_EXT(x, y)
#define FF_NDEF(x, y)
#endif
#else
#define SP_NDEF(x, y)
#define FF_NDEF(x, y)
#define FF_EXT(x, y)
#endif
#define XRT_SEND_UNICODE(s) unicode_input_start(); SEND_STRING(s); unicode_input_finish()
#define SITELEN_PONA_LARGEST_TP_WORD_SIZE 48
struct TokiPonaWordBinding {
char word[SITELEN_PONA_LARGEST_TP_WORD_SIZE];
const char* uni;
} TokiPonaWordBinding;
char sitelenPonaCurrentBuffer[SITELEN_PONA_LARGEST_TP_WORD_SIZE + 1];
int sitelenPonaCurrentBufferPos = 0;
#ifndef _XAN_PERSONAL_ALREADY_DEFINED_SITELEN_PONA_LOCK
bool sitelenPonaLock = false;
#endif
const struct TokiPonaWordBinding sitelenPonaLookup[] = {
{"a", "F1900"},
FF_EXT ("a2", "FFB11")
FF_EXT ("a3", "FFD60")
FF_EXT ("a4", "FFD61")
FF_EXT ("a5", "FFD62")
SP_NDEF ("aka", "FFB3A")
{"akesi", "F1901"},
FF_EXT ("akesi2", "FFD63")
SP_NDEF ("akesiv", "FFAFF")
FF_NDEF ("akesiv2", "FFD64")
SP_NDEF ("ako", "FFB73")
FF_NDEF ("ako2", "FFB2B")
SP_NDEF ("aku", "FFD87")
{"ala", "F1902"},
{"ali", "F1904"},
{"alasa", "F1903"},
{"ale", "F1904"},
SP_NDEF ("alente", "FFD80")
SP_NDEF ("alu", "FFB40")
SP_NDEF ("ana", "FFDB1")
SP_NDEF ("ani", "FFD88")
{"anpa", "F1905"},
SP_NDEF ("anta", "FFD89")
{"ante", "F1906"},
SP_NDEF ("antikontitutonelema", "FFDCE")
#ifdef _ALLOW_FAIRFAX_ALT1
FF_NDEF ("antikontitutonelema1", "FFDCF")
#else
FF_NDEF ("antikontitutonelema2", "FFDCF")
#endif
{"anu", "F1907"},
{"apeja", "F19A1"},
SP_NDEF ("apelo", "FFD8A")
SP_NDEF ("api", "FFDBB")
FF_NDEF ("api2", "FFDBC")
FF_NDEF ("api3", "FFDBD")
FF_NDEF ("api4", "FFDBA")
SP_NDEF ("awase", "FFB41")
{"awen", "F1908"},
{"e", "F1909"},
SP_NDEF ("eki", "FFB3A")
SP_NDEF ("elen", "FFD8B")
SP_NDEF ("eliki", "FFDAC")
{"en", "F190A"},
SP_NDEF ("ene", "FFDB2")
SP_NDEF ("eni", "FFDB2")
SP_NDEF ("enepi", "FFDAD")
SP_NDEF ("enko", "FFB43")
FF_NDEF ("enko2", "FFB44")
FF_NDEF ("enko3", "FFB45")
{"epiku", "F1983"},
SP_NDEF ("epikule", "FFB8B")
#ifdef _ALLOW_FAIRFAX_ALT1
FF_EXT ("epiku1", "FFAAB")
#else
FF_EXT ("epiku2", "FFAAB")
#endif
{"esun", "F190B"},
SP_NDEF ("ete", "FFACC")
FF_NDEF ("ete2", "FFACD")
SP_NDEF ("ewe", "FFAD9")
SP_NDEF ("i", "FFB46")
{"ijo", "F190C"},
{"ike", "F190D"},
SP_NDEF ("iki", "FFB47")
FF_NDEF ("iki2", "FFB0B")
{"ilo", "F190E"},
{"insa", "F190F"},
SP_NDEF ("ipi", "FFB48")
FF_NDEF ("ipi2", "FFB0D")
SP_NDEF ("itomi", "FFB49")
SP_NDEF ("iseki", "FFD8C")
SP_NDEF ("isipin", "FFAC0")
SP_NDEF ("ja", "FFD2F")
{"jaki", "F1910"},
SP_NDEF ("jaku", "FFB4D")
SP_NDEF ("jalan", "FFAD1")
SP_NDEF ("jami", "FFB31")
SP_NDEF ("jans", "FFB8E")
{"jan", "F1911"},
{"jasima", "F197F"},
SP_NDEF ("je", "FFB4E")
FF_NDEF ("je2", "FFB29")
{"jelo", "F1912"},
SP_NDEF ("jew", "FFB8F")
{"jo", "F1913"},
SP_NDEF ("jonke", "FFB4F")
SP_NDEF ("jule", "FFD81")
SP_NDEF ("jume", "FFB74")
{"kala", "F1914"},
FF_EXT ("kala2", "FFAA0")
{"kalama", "F1915"},
SP_NDEF ("kalamarr", "FFAFE") // AGREED MATEY
SP_NDEF ("kalamawala", "FFADA")
SP_NDEF ("kalapisituwi", "FFD8D")
SP_NDEF ("kalijopilale", "FFBA0")
{"kama", "F1916"},
SP_NDEF ("kan", "FFACF")
SP_NDEF ("kana", "FFD8E")
SP_NDEF ("kankuli", "FFDE1")
SP_NDEF ("kapa", "FFB51")
SP_NDEF ("kapesi", "FFAC1")
FF_NDEF ("kapesi2", "FFAC2")
{"kasi", "F1917"},
SP_NDEF ("ke", "FFADB")
FF_NDEF ("ke2", "FFB24")
{"ken", "F1918"},
SP_NDEF ("kepa", "FFE50")
{"kepeken", "F1919"},
{"kepen", "F1919"}, // Feels wrong to include here but it's defined so...
SP_NDEF ("kese", "FFADF")
SP_NDEF ("ki", "FFB55")
SP_NDEF ("kijosin", "FFBA1")
SP_NDEF ("kiki", "FFAC3")
FF_NDEF ("kiki2", "FFD6D")
FF_NDEF ("kiki3", "FFD6E")
FF_NDEF ("kiki4", "FFD6F")
{"kili", "F191A"},
{"kijetesantakalu", "F1980"},
FF_EXT ("kijetesantakalu2", "FFDC8")
SP_NDEF ("kikolo", "FFD8F")
{"kin", "F1979"},
FF_EXT ("kin2", "FFB15")
SP_NDEF ("kikulo", "FFD90")
{"kipisi", "F197B"},
SP_NDEF ("kisa", "FFB57")
{"kiwen", "F191B"},
{"ko", "F191C"},
{"kokosila", "F1984"},
FF_EXT ("kokosila2", "FFAAE")
SP_NDEF ("kolo", "FFDBE")
{"kon", "F191D"},
SP_NDEF ("koni", "FFD92")
SP_NDEF ("kokoliko", "FFD91")
SP_NDEF ("konsi", "FFB75")
SP_NDEF ("konwe", "FFB58")
SP_NDEF ("kosan", "FFB59")
SP_NDEF ("kosikosa", "FFD93")
{"ku", "F1988"},
FF_EXT ("ku2", "FFD76")
FF_EXT ("ku3", "FFD77")
FF_EXT ("ku4", "FFD78")
FF_EXT ("ku5", "FFD79")
FF_EXT ("ku6", "FFD7A")
FF_EXT ("ku7", "FFD7B")
SP_NDEF ("kulaso", "FFDCA")
{"kule", "F191E"},
SP_NDEF ("kulepiku", "FFB8A")
SP_NDEF ("kulijo", "FFAE1")
FF_NDEF ("kulijo2", "FFDBF")
SP_NDEF ("kulu", "FFB5D")
{"kulupu", "F191F"},
SP_NDEF ("kuntu", "FFAD0")
{"kute", "F1920"},
SP_NDEF ("kutopoma", "FFB77")
{"la", "F1921"},
{"lanpan", "F1985"},
FF_EXT ("lanpan2", "FFAB0")
FF_EXT ("lanpan3", "FFAB1")
SP_NDEF ("lansan", "FFBAB")
SP_NDEF ("lato", "FFDB4")
{"lape", "F1922"},
{"laso", "F1923"},
{"lawa", "F1924"},
{"leko", "F197C"},
{"len", "F1925"},
{"lete", "F1926"},
{"li", "F1927"},
SP_NDEF ("lijokuku", "FFB78")
FF_NDEF ("lijokuku2", "FFB79")
SP_NDEF ("likujo", "FFAE2")
{"lili", "F1928"},
{"linja", "F1929"},
SP_NDEF ("linluwi", "FFAC4")
FF_NDEF ("linluwi2", "FFBA5")
FF_NDEF ("linluwi3", "FFD6B")
FF_NDEF ("linluwi4", "FFD6C")
{"lipu", "F192A"},
SP_NDEF ("lisana", "FFDC0")
FF_NDEF ("lisana2", "FFDC1")
{"loje", "F192B"},
SP_NDEF ("lo", "FFB7A")
SP_NDEF ("loka", "FFAD1")
SP_NDEF ("lokon", "FFB5E")
{"lon", "F192C"},
SP_NDEF ("lu", "FFB7A")
{"luka", "F192D"},
{"lukin", "F192E"},
{"lupa", "F192F"},
{"ma", "F1930"},
{"mama", "F1931"},
SP_NDEF ("mamasi", "FFBA9")
SP_NDEF ("mamasina", "FFBA9")
{"majuna", "F19A2"},
FF_EXT ("majuna2", "FFAB3")
{"mani", "F1932"},
SP_NDEF ("masalo", "FFD94")
SP_NDEF ("masu", "FFDB3")
SP_NDEF ("masenta", "FFDCD")
SP_NDEF ("matula", "FFD95")
SP_NDEF ("me", "FFB7C")
{"meli", "F1933"},
FF_EXT ("meli2", "FFAA1")
FF_EXT ("meli3", "FFD19")
SP_NDEF ("melome", "FFB32")
{"meso", "F1982"},
FF_EXT ("meso2", "FFAAA")
{"mi", "F1934"},
FF_EXT ("mi2", "FFB05")
SP_NDEF ("mijomi", "FFB36")
SP_NDEF ("miko", "FFE51")
FF_NDEF ("miko2", "FFE52")
SP_NDEF ("misa", "FFAD2")
FF_NDEF ("misa2", "FFAD3")
FF_NDEF ("misa3", "FFAD4")
FF_NDEF ("misa4", "FFAD5")
FF_NDEF ("misa5", "FFAD6")
FF_NDEF ("misa6", "FFAD7")
{"misikeke", "F1987"},
FF_EXT ("misikeke2", "FFD6A")
{"mije", "F1935"},
FF_EXT ("mije2", "FFAA2")
FF_EXT ("mije3", "FFD18")
{"moku", "F1936"},
{"moli", "F1937"},
FF_EXT ("moli2", "FFD68")
SP_NDEF ("molusa", "FFB5F")
{"monsi", "F1938"},
{"monsuta", "F197D"},
{"mu", "F1939"},
FF_EXT ("mu2", "F1939")
SP_NDEF ("mulapisu", "FFAC8")
{"mun", "F193A"},
{"musi", "F193B"},
FF_EXT ("musi2", "FFD2E")
{"mute", "F193C"},
FF_EXT ("mute2", "FFD69")
SP_NDEF ("nalanja", "FFDB6")
#ifdef _ALLOW_FAIRFAX_ALT1
FF_NDEF ("nalanja1", "FFD82")
#else
FF_NDEF ("nalanja2", "FFD82")
#endif
{"namako", "F1978"},
FF_EXT ("namako2", "FFAA8")
{"nanpa", "F193D"},
{"nasin", "F193F"},
{"nasa", "F193E"},
SP_NDEF ("natu", "FFB3A")
SP_NDEF ("neja", "FFAE3")
SP_NDEF ("nele", "FFB7D")
{"nena", "F1940"},
{"n", "F1986"},
FF_EXT ("n2", "FFB1A")
{"ni", "F1941"},
FF_EXT ("ni2", "FFB02")
FF_EXT ("ni3", "FFB01")
FF_EXT ("ni4", "FFB00")
FF_EXT ("ni5", "FFD72")
FF_EXT ("ni6", "FFD71")
FF_EXT ("ni7", "FFD70")
FF_EXT ("ni8", "FFD73")
{"nimi", "F1942"},
SP_NDEF ("nimisin", "FFD96")
SP_NDEF ("nja", "FFB60")
{"noka", "F1943"},
SP_NDEF ("nowi", "FFD97")
SP_NDEF ("nu", "FFB7E")
SP_NDEF ("nuwa", "FFB7F")
{"o", "F1944"},
FF_EXT ("o2", "FFB13")
SP_NDEF ("ojuta", "FFD83") // We can't add this, otherwise steve jobs will die!
SP_NDEF ("oke", "FFAE4")
SP_NDEF ("okepuma", "FFB61")
SP_NDEF ("oki", "FFB84")
{"oko", "F197A"},
SP_NDEF ("olala", "FFD98")
{"olin", "F1945"},
#ifdef _ALLOW_FAIRFAX_ALT1
FF_EXT ("olin1", "FFD75")
FF_EXT ("olin2", "FFAA3")
#else
FF_EXT ("olin2", "FFD75")
FF_EXT ("olin3", "FFAA3")
#endif
SP_NDEF ("omekalike", "FFD84")
SP_NDEF ("omekapo", "FFB3B")
SP_NDEF ("omen", "FFB62")
{"ona", "F1946"},
FF_EXT ("ona2", "FFB09")
SP_NDEF ("oni", "FFB63")
FF_NDEF ("oni2", "FFB64")
SP_NDEF ("opasan", "FFD99")
{"open", "F1947"},
SP_NDEF ("owe", "FFB65")
SP_NDEF ("pa", "FFB66")
SP_NDEF ("pakola", "FFD9A")
FF_NDEF ("pakola2", "FFD9B")
{"pan", "F194B"},
SP_NDEF ("panke", "FFD9C")
{"pakala", "F1948"},
{"pake", "F19A0"},
{"palisa", "F194A"},
{"pali", "F1949"},
{"pana", "F194C"},
SP_NDEF ("pasila", "FFB67")
SP_NDEF ("pata", "FFAE5")
SP_NDEF ("penpo", "FFDC9")
SP_NDEF ("peta", "FFB69")
SP_NDEF ("peto", "FFAE6")
SP_NDEF ("pika", "FFB6A")
FF_NDEF ("pika2", "FFD9D")
{"pilin", "F194E"},
{"pimeja", "F194F"},
SP_NDEF ("pingo", "FFAFD") // ?
{"pini", "F1950"},
{"pipi", "F1951"},
SP_NDEF ("pipo", "FFB6B")
SP_NDEF ("pipolo", "FFD9E")
SP_NDEF ("po", "FFAE7")
{"poka", "F1952"},
{"poki", "F1953"},
{"pona", "F1954"},
SP_NDEF ("polinpin", "FFAE8")
SP_NDEF ("pomotolo", "FFAE9")
SP_NDEF ("poni", "FFB80") // It is registered as "poni1" but I think that is a mistake too because there is no "poni"
SP_NDEF ("poni1", "FFB80")
FF_NDEF ("poni2", "FFD85")
FF_NDEF ("poni3", "FFDC2")
FF_NDEF ("poni4", "FFDC3")
FF_NDEF ("poni5", "FFDC4")
FF_NDEF ("poni6", "FFDC5")
FF_NDEF ("poni7", "FFDC6")
FF_NDEF ("poni8", "FFDC7")
{"powe", "F19A3"},
{"pi", "F194D"},
{"pu", "F1955"},
SP_NDEF ("pulaso", "FFDCC")
SP_NDEF ("puwa", "FFB3C")
SP_NDEF ("saja", "FFDB0")
{"sama", "F1956"},
SP_NDEF ("samu", "FFAC9")
SP_NDEF ("san", "FFAEB")
SP_NDEF ("sapelipope", "FFD9F")
{"seli", "F1957"},
{"selo", "F1958"},
{"seme", "F1959"},
{"sewi", "F195A"},
FF_EXT ("sewi2", "FFAA4")
{"sijelo", "F195B"},
SP_NDEF ("sikako", "FFDA0")
{"sike", "F195C"},
SP_NDEF ("sikomo", "FFB6C")
SP_NDEF ("silapa", "FFDA1")
FF_NDEF ("silapa1", "FFDF0")
FF_NDEF ("silapa2", "FFDF1")
FF_NDEF ("silapa3", "FFDF2")
{"sin", "F195D"},
{"sina", "F195E"},
FF_EXT ("sina2", "FFB07")
{"sinpin", "F195F"},
SP_NDEF ("sipi", "FFB81")
SP_NDEF ("sipije", "FFDA2")
SP_NDEF ("sisi", "FFDA3")
{"sitelen", "F1960"},
SP_NDEF ("siwala", "FFDA4")
SP_NDEF ("slape", "FFB8D")
SP_NDEF ("snoweli", "FFDA5")
{"soko", "F1981"},
#ifdef _ALLOW_FAIRFAX_ALT1
FF_EXT ("soko1", "FFD65")
FF_EXT ("soko2", "FFD66")
#else
FF_EXT ("soko2", "FFD65")
FF_EXT ("soko3", "FFD66")
#endif
{"sona", "F1961"},
SP_NDEF ("soni", "FFDA6")
SP_NDEF ("soto", "FFAEC")
{"soweli", "F1962"},
SP_NDEF ("sowoli", "FFB86")
SP_NDEF ("su", "FFB2F")
FF_NDEF ("su2", "FFB3D")
SP_NDEF ("sulaso", "FFDCB")
{"suli", "F1963"},
{"suno", "F1964"},
{"supa", "F1965"},
SP_NDEF ("sutopatikuna", "FFAFB")
{"suwi", "F1966"},
SP_NDEF ("ta", "FFDA7")
FF_NDEF ("ta2", "FFE53")
SP_NDEF ("taki", "FFAED")
FF_NDEF ("taki2", "FFAEE")
{"tan", "F1967"},
{"taso", "F1968"},
SP_NDEF ("tasun", "FFDA8")
#ifdef _ALLOW_FAIRFAX_ALT1
FF_NDEF ("tasun1", "FFDF5")
FF_NDEF ("tasun2", "FFDF6")
FF_NDEF ("tasun3", "FFDF7")
#else
FF_NDEF ("tasun2", "FFDF5")
FF_NDEF ("tasun3", "FFDF6")
FF_NDEF ("tasun4", "FFDF7")
#endif
SP_NDEF ("taasun", "FFDF8")
SP_NDEF ("taaasun", "FFDF9")
{"tawa", "F1969"},
SP_NDEF ("te", "FFAF0")
SP_NDEF ("teje", "FFAEF")
SP_NDEF ("teken", "FFDB9")
{"telo", "F196A"},
{"ten", "F196B"},
FF_NDEF ("ten2", "FFBAD")
{"tenpo", "F196B"},
SP_NDEF ("to", "FFAF1")
SP_NDEF ("tokana", "FFB70")
FF_NDEF ("tokana2", "FFD9A")
FF_NDEF ("tokana3", "FFDAA")
{"toki", "F196C"},
{"tomo", "F196D"},
SP_NDEF ("tona", "FFBAE")
{"tonsi", "F197E"},
FF_EXT ("tonsi2", "FFD08")
FF_EXT ("tonsi3", "FFD1A")
FF_EXT ("tonsi4", "FFD1B")
SP_NDEF ("topo", "FFDB5")
{"tu", "F196E"},
SP_NDEF ("tuli", "FFAEB")
SP_NDEF ("tuwa", "FFE54")
SP_NDEF ("umesu", "FFAF2")
FF_NDEF ("umesu2", "FFDB8")
// https://youtu.be/lGJBUauU-CE?t=141
// o lukin a!
// a
// u n p a
{"unpa", "F196F"},
SP_NDEF ("unu", "FFACA")
SP_NDEF ("usawi", "FFAF6")
{"uta", "F1970"},
FF_EXT ("uta2", "FFAA5")
{"utala", "F1971"},
SP_NDEF ("wa", "FFACB")
FF_NDEF ("wa2", "FFB1F")
SP_NDEF ("waken", "FFBAA")
SP_NDEF ("waleja", "FFAD8")
{"walo", "F1972"},
{"wan", "F1973"},
{"waso", "F1974"},
SP_NDEF ("wasoweli", "FFAF7")
{"wawa", "F1975"},
SP_NDEF ("wawajete", "FFB71")
SP_NDEF ("we", "FFDB7")
{"weka", "F1976"},
SP_NDEF ("wekama", "FFB3E")
SP_NDEF ("wi", "FFB82")
FF_NDEF ("wi2", "FFB0F")
SP_NDEF ("wiju", "FFDAF")
SP_NDEF ("wiki", "FFBAC")
{"wile", "F1977"},
FF_EXT ("wile2", "FFAA6")
SP_NDEF ("wiwi", "FFDAB")
SP_NDEF ("wowujiti", "FFD86")
SP_NDEF ("wuwojiti", "FFD86")
// src: https://www.kreativekorp.com/software/fonts/fairfaxponahd/
// I guess y got manifested into existence.
SP_NDEF ("yupekosi", "FFAFC")
SP_NDEF ("yutu", "FFB8C")
};
// String equality on custom string pointers.
static int strEqu(const char* a, const char* b, int maxLength) {
for (int i = 0; i <= maxLength; i++) {
char aNow = a[i];
char bNow = b[i];
if (aNow != bNow) {
return 0; // Break immediately.
} else if (aNow == 0 && bNow == 0) {
return 1; // Only if both are zero do we ever return true.
}
}
return 0; // Otherwise it's not right.
}
// Returns the hex unicode string for the currently typed toki pona word stored in buffer,
// or 0 if no such character exists.
static const char* getUnicodeOfCurrentSitelen(void) {
if (sitelenPonaCurrentBufferPos == 0) return 0;
const int length = sizeof(sitelenPonaLookup) / sizeof(TokiPonaWordBinding);
for (int i = 0; i < length; ++i) {
struct TokiPonaWordBinding word = sitelenPonaLookup[i];
if (strEqu(word.word, sitelenPonaCurrentBuffer, sitelenPonaCurrentBufferPos)) {
return word.uni;
}
}
return 0;
}
// Pushes a character into the sitelen pona buffer.
static void pushSitelenPona(char kc) {
if (sitelenPonaCurrentBufferPos < SITELEN_PONA_LARGEST_TP_WORD_SIZE) {
sitelenPonaCurrentBuffer[sitelenPonaCurrentBufferPos] = kc;
sitelenPonaCurrentBufferPos++;
}
}
// Clears the sitelen pona buffer, which tracks the toki pona word being typed.
static void clearSitelenPonaBuffer(void) {
for (int i = 0; i <= SITELEN_PONA_LARGEST_TP_WORD_SIZE; ++i) {
// Note above: <= not <
sitelenPonaCurrentBuffer[i] = '\0';
}
sitelenPonaCurrentBufferPos = 0;
}
// Checks the toki pona buffer, and if a word is recognized, types it.
static void sendSitelenPonaIfKnown(void) {
sitelenPonaCurrentBuffer[SITELEN_PONA_LARGEST_TP_WORD_SIZE] = '\0'; // Ensure null ending.
const char* uni = getUnicodeOfCurrentSitelen();
if (uni) {
XRT_SEND_UNICODE(uni);
}
else {
// If it was unrecognized, just send the actual string that was buffered.
// This is part of why the null ending is enforced.
SEND_STRING((char*)(&sitelenPonaCurrentBuffer));
}
clearSitelenPonaBuffer();
}
static bool process_record_sitelen_pona(uint16_t keycode, keyrecord_t* record) {
int modState = get_mods();
bool compound = false;
bool stacked = false;
bool scaled = false;
// bool shift = modState & (MOD_BIT(KC_LSFT) | MOD_BIT(KC_RSFT));
#ifdef SITELEN_PONA_ALLOW_STACKED_AND_SCALED_COMPOUNDS
#ifdef SITELEN_PONA_COMPOUND_INTERSECT_KEYCODE
compound = modState & MOD_BIT(SITELEN_PONA_COMPOUND_INTERSECT_KEYCODE);
#endif
#ifdef SITELEN_PONA_COMPOUND_STACKED_KEYCODE
stacked = modState & MOD_BIT(SITELEN_PONA_COMPOUND_STACKED_KEYCODE);
#endif
#ifdef SITELEN_PONA_COMPOUND_SCALED_KEYCODE
scaled = modState & MOD_BIT(SITELEN_PONA_COMPOUND_SCALED_KEYCODE);
#endif
#endif
bool extendLeft = false;
bool extendRight = false;
#ifdef SITELEN_PONA_GLYPH_EXTEND_LEFT_KEYCODE
extendLeft = modState & MOD_BIT(SITELEN_PONA_GLYPH_EXTEND_LEFT_KEYCODE);
#endif
#ifdef SITELEN_PONA_GLYPH_EXTEND_RIGHT_KEYCODE
extendRight = modState & MOD_BIT(SITELEN_PONA_GLYPH_EXTEND_RIGHT_KEYCODE);
#endif
bool isPressed = (bool)(record->event.pressed);
if (keycode == SITELEN_PONA_TOGGLE_KEYCODE && isPressed) {
sendSitelenPonaIfKnown();
sitelenPonaLock = !sitelenPonaLock;
return false;
}
if (sitelenPonaLock && isPressed) {
switch (keycode) {
case KC_ENTER:
if (sitelenPonaCurrentBufferPos != 0) {
sendSitelenPonaIfKnown();
}
return true;
case KC_SPACE:
// Cause spacebar to type TP words.
if (sitelenPonaCurrentBufferPos != 0) {
sendSitelenPonaIfKnown();
if (!compound && !stacked && !scaled) return false;
}
if (compound || stacked || scaled) {
if (compound) {
XRT_SEND_UNICODE(SITELEN_PONA_COMPOUND_UNICODE);
} else if (stacked) {
XRT_SEND_UNICODE(SITELEN_PONA_STACKED_COMPOUND_UNICODE);
} else if (scaled) {
XRT_SEND_UNICODE(SITELEN_PONA_SCALED_COMPOUND_UNICODE);
}
return false;
}
return true;
case KC_SCOLON:
sendSitelenPonaIfKnown();
XRT_SEND_UNICODE(SITELEN_PONA_COLON_UNICODE);
return false;
case KC_DOT:
sendSitelenPonaIfKnown();
XRT_SEND_UNICODE(SITELEN_PONA_INTERPUNCT_UNICODE);
return false;
case SITELEN_PONA_RADICAL_OPEN:
sendSitelenPonaIfKnown();
if ((extendLeft && extendRight) || (!extendLeft && !extendRight)) {
XRT_SEND_UNICODE(SITELEN_PONA_CARTOUCHE_START_UNICODE);
} else if (extendLeft) {
XRT_SEND_UNICODE(SITELEN_PONA_LEFT_EXTEND_START_UNICODE);
} else if (extendRight) {
XRT_SEND_UNICODE(SITELEN_PONA_RIGHT_EXTEND_START_UNICODE);
}
return false;
case SITELEN_PONA_RADICAL_CLOSE:
sendSitelenPonaIfKnown();
if ((extendLeft && extendRight) || (!extendLeft && !extendRight)) {
XRT_SEND_UNICODE(SITELEN_PONA_CARTOUCHE_END_UNICODE);
} else if (extendLeft) {
XRT_SEND_UNICODE(SITELEN_PONA_LEFT_EXTEND_END_UNICODE);
} else if (extendRight) {
XRT_SEND_UNICODE(SITELEN_PONA_RIGHT_EXTEND_END_UNICODE);
}
return false;
case SITELEN_PONA_CANCEL_KEYCODE:
clearSitelenPonaBuffer();
sitelenPonaLock = false;
return false;
}
const int ASCII_OFFSET = 97 - KC_A; // 93 + 4 = 97
const int NUMBER_OFFSET_RAW = 0x31; // '1' is 0x31
bool isAZ = keycode >= KC_A && keycode <= KC_Z;
bool isNum = keycode >= KC_1 && keycode <= KC_0;
if (isAZ || isNum) {
char c;
if (isAZ) {
c = (char)(keycode + ASCII_OFFSET);
} else if (isNum) {
if (keycode == KC_0) {
c = '0'; // hardcode this because it's reverse order in ASCII (0 is before 1, rather than 1234567890)
} else {
c = (char)(keycode - KC_1 + NUMBER_OFFSET_RAW);
}
}
//if (isAZ && shift) {
// c -= 0x20; // Capitalize
//}
pushSitelenPona(c);
return false;
}
}
return true;
}
// clang-format on
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment