Конфигурация вакидзаси

В прошлых постах (про идею, про сборку) рассказывал про вакидзаси, но так и не написал статью про то, как пользоваться клавиатурой. На момент написания статьи она собрана, запрограммирована, работает.

Главное к клавиатуре — раскладка. Я пока не определился с ней до конца. Очень важно, чтобы было легко её настраивать. В этой статье расскажу, как это делалось.

Как обрабатываются аккорды

Функция void exec_chord(chord currc) вызывается для исполнения аккорда. С точки зрения прошивки, аккорд (chord) — число из 11 битов. Каждый бит соответствует одной кнопке. Минимально доступный размер числа, 1 байт, слишком маленький. Следующий, 2 байта, слишком большой, но ладно уж.

typedef uint16_t chord;

Идея обработки аккордов проста: берём аккорд, сравниваем с аккордами из раскладки, что есть, вот его берём и исполняем. Рассматривал другой подход: есть массив, а индексы соответствуют аккордам, но я его посчитал дурацким.

Специально для этого в си есть конструкция switch. В целом, примерно так должна выглядеть обработка:

void exec_chord(chord currc) {
  switch (currc) {
    case 0b10000000000: {
      // ...
      break;
    }
    case 0b01000000000: {
      // ...
      break;
    }
    ...
  }
}

Вообще, реальный код сложнее, но я опускаю подробности. Статья не про это. Если интересна реальность, прошу пожаловать в репозиторий на гитхабе (смотреть файл layout.h).

Ну так вот, в итоге получается очень много case-выражений. Мне вручную их писать не хотелось, значит надо это дело передать кому-нибудь.

Первая итерация: m4

Эта итерация началась задолго до того, как я начал собственно делать клавиатуру. Также она началась решительно сразу после того, как я бросил свой макропроцессор Агидель.

m4 — доисторический макропроцессора, развитие макропроцессор m3 (ожидаемо, улучшенная версия m4 называется m5), установленный на все UNIX-системы (а другими пользоваться мне не позволяет религия).

На сайте GNU есть хороший мануал.

Суть такая: есть файл, в котором я своими макросами описываю раскладку, а потом она магически самораскрывается в код. Я преуспел в этой итерации. Вот такой код раскрывался в то, что надо:

start_layout

mode_latin
        single_(O, A, T, E,
                H, S, N, I,
                      SPACE, DOT, COMMA)
shifted_single_(O, A, T, E,
                H, S, N, I,
                      _, _capslock, _)
 symbol_single_(GRAVE, APOSTROPHE, EQUAL, MINUS)
end_mode

end_layout

Красиво и круто. Но язык m4 мудрёный очень, код на нём превращался в кашу решительно сразу. Вот исходный код некоторых макросов:

define(`single_count', `10')
define(`single_helper',
       `if_key($1, `match(eval(BV($2), 2, 11),
`send_scancode (KEY_$1);') LF')')
define(`single_helper2',
`ifelse($#, `1', `single_helper($1, single_count)',
`single_helper($1, single_count)define(`single_count', decr(single_count))dnl
single_helper2(shift($*))')')

define(`single_', `single_helper2($*)')

Вторая итерация: графический редактор

Все любят графические редакторы! Решил сделать его в формате веб-приложения. Стек: Ruby, Sinatra, Bootstrap, Typescript. В целом, кое-что сделал:

Назвал эту программу остроумно: Wakizashi sharpener. Эту программу было интересно разрабатывать. Я узнал много нового и впервые что-то написал на тайпскрипте. Потом я на нём, кстати написал, симулятор вакидзаси и ввод цифр бинарно.

Потом мой импульс любви к графическим редакторам сошёл на нет.

Третья итерация: непонятно что

В это время приступил к собственно разработке вакидзаси. Воспользовался старыми макросами первой итерации, и потом их продукт редактировал вручную. Понял, что так нельзя. Делать ещё больше непонятных макросов m4 не хотел, поэтому решил выбрать другой формат для конфига.

Четвёртая итерация: YAML

YAML Ain’t Markup Language не язык разметки, это как JSON, но круче. Отличается чистеньким синтаксисом как в питончике, на отступах, но при этом в него можно смело вставлять кусочки на JSON.

В общем, вот такая штука:

---
name: Wakizashi Default Latin+Cyrillic
layers:
  latin:
    single:
      normal: [
      o, a, p, e,
      h, s, n, i,
      ' ', ',', '.'
      ]
      shift: [
      O, A, P, E,
      H, S, N, I,
      ]
      symbol: [
      8, 4, 2, 1,
      ':', '"', "'", ";"
      ]
      function: [
      mod_ctrl,    mod_altl,    mod_shift, mod_super,
      _left_arrow, _down_arrow, _up_arrow, _right_arrow
      ]
      macro: [
      _, _, _, _,
      'a ', 'in ', 'that ', 'be '
      ]
    vertical:
      normal: [r, l, d, u]
      shift: [R, L, D, U]
      symbol: [
      '-', _, '(', ')'
      ]
      function: [
      _page_down, _page_up, _tab, _esc
      ]
      macro: []
      control: []
    # ...

Программка на руби её хитро превращала в код на си. Работало, юзал. Специальные символы оборачивались в кавычки, ещё были специальные соглашения:

Проблема: не понятно, что чему соответствует.

Пятая итерация: CSV

Раз не получалось использовать конфиг как шпаргалку, начал рисовать шпаргалку. Нарисовал пару строчек (хотя вернее сказать «написал»). Присмотрелся. Да это же CSV!

Comma separated values — самый примитивный формат представления табличных данных. Каждая строчка файла — строчка таблицы. Столбики отделяются чем-нибудь. Из названия понятно, что обычно это запятая. Я взял же таб, который \t, потому что он невидимый более-менее и при отрисовке всё выглядит довольно таблично.

chord	no	t1	t2	t3	t23
// Single presses
0000		 	,	.
0000

1000	o	O	8	mod_shift
    	h	H	:	_left_arrow

0100	a	A	4	mod_altl
    	s	S	"	_up_arrow

0010	p	P	2	mod_ctrl
    	n	N	'	_down_arrow

0001	e	E	1	mod_super
    	i	I	;	_right_arrow

// Vertical presses
1000	r	R	-	_page_down
1000

0100	l	L		_page_up
0100

0010	d	D	(	_tab
0010

0001	u	U	)	_esc
0001

Чтобы писать ещё меньше текста, сделал так, что если в первом столбике только одна строчка аккорда, то справа описывается сразу два аккорда: верхний и нижний.

Формат настолько очевидный, что программу для обработки написал буквально в течение дня (а для прошлых итераций я корячился заметное время).

Также такой конфиг можно использовать как шпаргалку!

Каждый слой раскладки (латинский и кириллский) описывается в отдельном файле (на момент создания файла за кириллский пока не взялся), а всё вместе собирается мейкфайлом (тоже крутая технология, как выяснилось).

Как заключение

Какая итерация понравилась анонимному читателю больше всего? Мне, понятное дело, последняя. Кстати, почти весь конфиг написал с вакидзаси. Столкнулся с резкой неэргономичностью некоторых кнопок. Но это мелочь, конечно же. Я очень доволен тем, как всё получилось :)