summaryrefslogtreecommitdiff
path: root/quantum/process_keycode
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/process_keycode')
-rw-r--r--quantum/process_keycode/process_audio.c62
-rw-r--r--quantum/process_keycode/process_audio.h11
-rw-r--r--quantum/process_keycode/process_chording.c76
-rw-r--r--quantum/process_keycode/process_chording.h32
-rw-r--r--quantum/process_keycode/process_combo.c150
-rw-r--r--quantum/process_keycode/process_combo.h59
-rw-r--r--quantum/process_keycode/process_leader.c54
-rw-r--r--quantum/process_keycode/process_leader.h39
-rw-r--r--quantum/process_keycode/process_midi.c253
-rw-r--r--quantum/process_keycode/process_midi.h56
-rw-r--r--quantum/process_keycode/process_music.c205
-rw-r--r--quantum/process_keycode/process_music.h47
-rw-r--r--quantum/process_keycode/process_printer.c270
-rw-r--r--quantum/process_keycode/process_printer.h26
-rw-r--r--quantum/process_keycode/process_printer_bb.c276
-rw-r--r--quantum/process_keycode/process_tap_dance.c166
-rw-r--r--quantum/process_keycode/process_tap_dance.h95
-rw-r--r--quantum/process_keycode/process_ucis.c149
-rw-r--r--quantum/process_keycode/process_ucis.h51
-rw-r--r--quantum/process_keycode/process_unicode.c35
-rw-r--r--quantum/process_keycode/process_unicode.h24
-rw-r--r--quantum/process_keycode/process_unicode_common.c116
-rw-r--r--quantum/process_keycode/process_unicode_common.h148
-rw-r--r--quantum/process_keycode/process_unicodemap.c72
-rw-r--r--quantum/process_keycode/process_unicodemap.h25
25 files changed, 2497 insertions, 0 deletions
diff --git a/quantum/process_keycode/process_audio.c b/quantum/process_keycode/process_audio.c
new file mode 100644
index 0000000000..0b6380ed39
--- /dev/null
+++ b/quantum/process_keycode/process_audio.c
@@ -0,0 +1,62 @@
+#include "audio.h"
+#include "process_audio.h"
+
+static float compute_freq_for_midi_note(uint8_t note)
+{
+ // https://en.wikipedia.org/wiki/MIDI_tuning_standard
+ return pow(2.0, (note - 69) / 12.0) * 440.0f;
+}
+
+bool process_audio(uint16_t keycode, keyrecord_t *record) {
+
+ if (keycode == AU_ON && record->event.pressed) {
+ audio_on();
+ return false;
+ }
+
+ if (keycode == AU_OFF && record->event.pressed) {
+ audio_off();
+ return false;
+ }
+
+ if (keycode == AU_TOG && record->event.pressed) {
+ if (is_audio_on())
+ {
+ audio_off();
+ }
+ else
+ {
+ audio_on();
+ }
+ return false;
+ }
+
+ if (keycode == MUV_IN && record->event.pressed) {
+ voice_iterate();
+ music_scale_user();
+ return false;
+ }
+
+ if (keycode == MUV_DE && record->event.pressed) {
+ voice_deiterate();
+ music_scale_user();
+ return false;
+ }
+
+ return true;
+}
+
+void process_audio_noteon(uint8_t note) {
+ play_note(compute_freq_for_midi_note(note), 0xF);
+}
+
+void process_audio_noteoff(uint8_t note) {
+ stop_note(compute_freq_for_midi_note(note));
+}
+
+void process_audio_all_notes_off(void) {
+ stop_all_notes();
+}
+
+__attribute__ ((weak))
+void audio_on_user() {} \ No newline at end of file
diff --git a/quantum/process_keycode/process_audio.h b/quantum/process_keycode/process_audio.h
new file mode 100644
index 0000000000..7ac15b7330
--- /dev/null
+++ b/quantum/process_keycode/process_audio.h
@@ -0,0 +1,11 @@
+#ifndef PROCESS_AUDIO_H
+#define PROCESS_AUDIO_H
+
+bool process_audio(uint16_t keycode, keyrecord_t *record);
+void process_audio_noteon(uint8_t note);
+void process_audio_noteoff(uint8_t note);
+void process_audio_all_notes_off(void);
+
+void audio_on_user(void);
+
+#endif \ No newline at end of file
diff --git a/quantum/process_keycode/process_chording.c b/quantum/process_keycode/process_chording.c
new file mode 100644
index 0000000000..6c6ebe300a
--- /dev/null
+++ b/quantum/process_keycode/process_chording.c
@@ -0,0 +1,76 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_chording.h"
+
+bool keys_chord(uint8_t keys[]) {
+ uint8_t keys_size = sizeof(keys)/sizeof(keys[0]);
+ bool pass = true;
+ uint8_t in = 0;
+ for (uint8_t i = 0; i < chord_key_count; i++) {
+ bool found = false;
+ for (uint8_t j = 0; j < keys_size; j++) {
+ if (chord_keys[i] == (keys[j] & 0xFF)) {
+ in++; // detects key in chord
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ if (chord_keys[i] != 0) {
+ pass = false; // makes sure rest are blank
+ }
+ }
+ return (pass && (in == keys_size));
+}
+
+bool process_chording(uint16_t keycode, keyrecord_t *record) {
+ if (keycode >= QK_CHORDING && keycode <= QK_CHORDING_MAX) {
+ if (record->event.pressed) {
+ if (!chording) {
+ chording = true;
+ for (uint8_t i = 0; i < CHORDING_MAX; i++)
+ chord_keys[i] = 0;
+ chord_key_count = 0;
+ chord_key_down = 0;
+ }
+ chord_keys[chord_key_count] = (keycode & 0xFF);
+ chord_key_count++;
+ chord_key_down++;
+ return false;
+ } else {
+ if (chording) {
+ chord_key_down--;
+ if (chord_key_down == 0) {
+ chording = false;
+ // Chord Dictionary
+ if (keys_chord((uint8_t[]){KC_ENTER, KC_SPACE})) {
+ register_code(KC_A);
+ unregister_code(KC_A);
+ return false;
+ }
+ for (uint8_t i = 0; i < chord_key_count; i++) {
+ register_code(chord_keys[i]);
+ unregister_code(chord_keys[i]);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
diff --git a/quantum/process_keycode/process_chording.h b/quantum/process_keycode/process_chording.h
new file mode 100644
index 0000000000..8c0f4862a8
--- /dev/null
+++ b/quantum/process_keycode/process_chording.h
@@ -0,0 +1,32 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_CHORDING_H
+#define PROCESS_CHORDING_H
+
+#include "quantum.h"
+
+// Chording stuff
+#define CHORDING_MAX 4
+bool chording = false;
+
+uint8_t chord_keys[CHORDING_MAX] = {0};
+uint8_t chord_key_count = 0;
+uint8_t chord_key_down = 0;
+
+bool process_chording(uint16_t keycode, keyrecord_t *record);
+
+#endif
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
new file mode 100644
index 0000000000..58d45add22
--- /dev/null
+++ b/quantum/process_keycode/process_combo.c
@@ -0,0 +1,150 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_combo.h"
+#include "print.h"
+
+
+#define COMBO_TIMER_ELAPSED -1
+
+
+__attribute__ ((weak))
+combo_t key_combos[] = {
+
+};
+
+__attribute__ ((weak))
+void process_combo_event(uint8_t combo_index, bool pressed) {
+
+}
+
+static uint8_t current_combo_index = 0;
+
+static inline void send_combo(uint16_t action, bool pressed)
+{
+ if (action) {
+ if (pressed) {
+ register_code16(action);
+ } else {
+ unregister_code16(action);
+ }
+ } else {
+ process_combo_event(current_combo_index, pressed);
+ }
+}
+
+#define ALL_COMBO_KEYS_ARE_DOWN (((1<<count)-1) == combo->state)
+#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
+#define KEY_STATE_DOWN(key) do{ combo->state |= (1<<key); } while(0)
+#define KEY_STATE_UP(key) do{ combo->state &= ~(1<<key); } while(0)
+static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record)
+{
+ uint8_t count = 0;
+ uint8_t index = -1;
+ /* Find index of keycode and number of combo keys */
+ for (const uint16_t *keys = combo->keys; ;++count) {
+ uint16_t key = pgm_read_word(&keys[count]);
+ if (keycode == key) index = count;
+ if (COMBO_END == key) break;
+ }
+
+ /* Return if not a combo key */
+ if (-1 == (int8_t)index) return false;
+
+ /* The combos timer is used to signal whether the combo is active */
+ bool is_combo_active = COMBO_TIMER_ELAPSED == combo->timer ? false : true;
+
+ if (record->event.pressed) {
+ KEY_STATE_DOWN(index);
+
+ if (is_combo_active) {
+ if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
+ send_combo(combo->keycode, true);
+ combo->timer = COMBO_TIMER_ELAPSED;
+ } else { /* Combo key was pressed */
+ combo->timer = timer_read();
+#ifdef COMBO_ALLOW_ACTION_KEYS
+ combo->prev_record = *record;
+#else
+ combo->prev_key = keycode;
+#endif
+ }
+ }
+ } else {
+ if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
+ send_combo(combo->keycode, false);
+ }
+
+ if (is_combo_active) { /* Combo key was tapped */
+#ifdef COMBO_ALLOW_ACTION_KEYS
+ record->event.pressed = true;
+ process_action(record, store_or_get_action(record->event.pressed, record->event.key));
+ record->event.pressed = false;
+ process_action(record, store_or_get_action(record->event.pressed, record->event.key));
+#else
+ register_code16(keycode);
+ send_keyboard_report();
+ unregister_code16(keycode);
+#endif
+ combo->timer = 0;
+ }
+
+ KEY_STATE_UP(index);
+ }
+
+ if (NO_COMBO_KEYS_ARE_DOWN) {
+ combo->timer = 0;
+ }
+
+ return is_combo_active;
+}
+
+bool process_combo(uint16_t keycode, keyrecord_t *record)
+{
+ bool is_combo_key = false;
+
+ for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
+ combo_t *combo = &key_combos[current_combo_index];
+ is_combo_key |= process_single_combo(combo, keycode, record);
+ }
+
+ return !is_combo_key;
+}
+
+void matrix_scan_combo(void)
+{
+ for (int i = 0; i < COMBO_COUNT; ++i) {
+ combo_t *combo = &key_combos[i];
+ if (combo->timer &&
+ combo->timer != COMBO_TIMER_ELAPSED &&
+ timer_elapsed(combo->timer) > COMBO_TERM) {
+
+ /* This disables the combo, meaning key events for this
+ * combo will be handled by the next processors in the chain
+ */
+ combo->timer = COMBO_TIMER_ELAPSED;
+
+#ifdef COMBO_ALLOW_ACTION_KEYS
+ process_action(&combo->prev_record,
+ store_or_get_action(combo->prev_record.event.pressed,
+ combo->prev_record.event.key));
+#else
+ unregister_code16(combo->prev_key);
+ register_code16(combo->prev_key);
+#endif
+ }
+ }
+}
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
new file mode 100644
index 0000000000..a5dbd788a4
--- /dev/null
+++ b/quantum/process_keycode/process_combo.h
@@ -0,0 +1,59 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_COMBO_H
+#define PROCESS_COMBO_H
+
+#include <stdint.h>
+#include "progmem.h"
+#include "quantum.h"
+
+typedef struct
+{
+ const uint16_t *keys;
+ uint16_t keycode;
+#ifdef EXTRA_EXTRA_LONG_COMBOS
+ uint32_t state;
+#elif EXTRA_LONG_COMBOS
+ uint16_t state;
+#else
+ uint8_t state;
+#endif
+ uint16_t timer;
+#ifdef COMBO_ALLOW_ACTION_KEYS
+ keyrecord_t prev_record;
+#else
+ uint16_t prev_key;
+#endif
+} combo_t;
+
+
+#define COMBO(ck, ca) {.keys = &(ck)[0], .keycode = (ca)}
+#define COMBO_ACTION(ck) {.keys = &(ck)[0]}
+
+#define COMBO_END 0
+#ifndef COMBO_COUNT
+#define COMBO_COUNT 0
+#endif
+#ifndef COMBO_TERM
+#define COMBO_TERM TAPPING_TERM
+#endif
+
+bool process_combo(uint16_t keycode, keyrecord_t *record);
+void matrix_scan_combo(void);
+void process_combo_event(uint8_t combo_index, bool pressed);
+
+#endif
diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c
new file mode 100644
index 0000000000..473906d657
--- /dev/null
+++ b/quantum/process_keycode/process_leader.c
@@ -0,0 +1,54 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_leader.h"
+
+__attribute__ ((weak))
+void leader_start(void) {}
+
+__attribute__ ((weak))
+void leader_end(void) {}
+
+// Leader key stuff
+bool leading = false;
+uint16_t leader_time = 0;
+
+uint16_t leader_sequence[5] = {0, 0, 0, 0, 0};
+uint8_t leader_sequence_size = 0;
+
+bool process_leader(uint16_t keycode, keyrecord_t *record) {
+ // Leader key set-up
+ if (record->event.pressed) {
+ if (!leading && keycode == KC_LEAD) {
+ leader_start();
+ leading = true;
+ leader_time = timer_read();
+ leader_sequence_size = 0;
+ leader_sequence[0] = 0;
+ leader_sequence[1] = 0;
+ leader_sequence[2] = 0;
+ leader_sequence[3] = 0;
+ leader_sequence[4] = 0;
+ return false;
+ }
+ if (leading && timer_elapsed(leader_time) < LEADER_TIMEOUT) {
+ leader_sequence[leader_sequence_size] = keycode;
+ leader_sequence_size++;
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/quantum/process_keycode/process_leader.h b/quantum/process_keycode/process_leader.h
new file mode 100644
index 0000000000..da7a3d2ef7
--- /dev/null
+++ b/quantum/process_keycode/process_leader.h
@@ -0,0 +1,39 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_LEADER_H
+#define PROCESS_LEADER_H
+
+#include "quantum.h"
+
+bool process_leader(uint16_t keycode, keyrecord_t *record);
+
+void leader_start(void);
+void leader_end(void);
+
+#ifndef LEADER_TIMEOUT
+ #define LEADER_TIMEOUT 200
+#endif
+#define SEQ_ONE_KEY(key) if (leader_sequence[0] == (key) && leader_sequence[1] == 0 && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0)
+#define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0)
+#define SEQ_THREE_KEYS(key1, key2, key3) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == 0 && leader_sequence[4] == 0)
+#define SEQ_FOUR_KEYS(key1, key2, key3, key4) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == 0)
+#define SEQ_FIVE_KEYS(key1, key2, key3, key4, key5) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == (key3) && leader_sequence[3] == (key4) && leader_sequence[4] == (key5))
+
+#define LEADER_EXTERNS() extern bool leading; extern uint16_t leader_time; extern uint16_t leader_sequence[5]; extern uint8_t leader_sequence_size
+#define LEADER_DICTIONARY() if (leading && timer_elapsed(leader_time) > LEADER_TIMEOUT)
+
+#endif
diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c
new file mode 100644
index 0000000000..9184feaae8
--- /dev/null
+++ b/quantum/process_keycode/process_midi.c
@@ -0,0 +1,253 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "process_midi.h"
+
+#ifdef MIDI_ENABLE
+#include "midi.h"
+
+#ifdef MIDI_BASIC
+
+void process_midi_basic_noteon(uint8_t note)
+{
+ midi_send_noteon(&midi_device, 0, note, 128);
+}
+
+void process_midi_basic_noteoff(uint8_t note)
+{
+ midi_send_noteoff(&midi_device, 0, note, 0);
+}
+
+void process_midi_all_notes_off(void)
+{
+ midi_send_cc(&midi_device, 0, 0x7B, 0);
+}
+
+#endif // MIDI_BASIC
+
+#ifdef MIDI_ADVANCED
+
+#include "timer.h"
+
+static uint8_t tone_status[MIDI_TONE_COUNT];
+
+static uint8_t midi_modulation;
+static int8_t midi_modulation_step;
+static uint16_t midi_modulation_timer;
+
+inline uint8_t compute_velocity(uint8_t setting)
+{
+ return (setting + 1) * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN + 1));
+}
+
+void midi_init(void)
+{
+ midi_config.octave = MI_OCT_2 - MIDI_OCTAVE_MIN;
+ midi_config.transpose = 0;
+ midi_config.velocity = (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN);
+ midi_config.channel = 0;
+ midi_config.modulation_interval = 8;
+
+ for (uint8_t i = 0; i < MIDI_TONE_COUNT; i++)
+ {
+ tone_status[i] = MIDI_INVALID_NOTE;
+ }
+
+ midi_modulation = 0;
+ midi_modulation_step = 0;
+ midi_modulation_timer = 0;
+}
+
+void midi_task(void)
+{
+ if (timer_elapsed(midi_modulation_timer) < midi_config.modulation_interval)
+ return;
+ midi_modulation_timer = timer_read();
+
+ if (midi_modulation_step != 0)
+ {
+ dprintf("midi modulation %d\n", midi_modulation);
+ midi_send_cc(&midi_device, midi_config.channel, 0x1, midi_modulation);
+
+ if (midi_modulation_step < 0 && midi_modulation < -midi_modulation_step) {
+ midi_modulation = 0;
+ midi_modulation_step = 0;
+ return;
+ }
+
+ midi_modulation += midi_modulation_step;
+
+ if (midi_modulation > 127)
+ midi_modulation = 127;
+ }
+}
+
+uint8_t midi_compute_note(uint16_t keycode)
+{
+ return 12 * midi_config.octave + (keycode - MIDI_TONE_MIN) + midi_config.transpose;
+}
+
+bool process_midi(uint16_t keycode, keyrecord_t *record)
+{
+ switch (keycode) {
+ case MIDI_TONE_MIN ... MIDI_TONE_MAX:
+ {
+ uint8_t channel = midi_config.channel;
+ uint8_t tone = keycode - MIDI_TONE_MIN;
+ uint8_t velocity = compute_velocity(midi_config.velocity);
+ if (record->event.pressed) {
+ uint8_t note = midi_compute_note(keycode);
+ midi_send_noteon(&midi_device, channel, note, velocity);
+ dprintf("midi noteon channel:%d note:%d velocity:%d\n", channel, note, velocity);
+ tone_status[tone] = note;
+ }
+ else {
+ uint8_t note = tone_status[tone];
+ if (note != MIDI_INVALID_NOTE)
+ {
+ midi_send_noteoff(&midi_device, channel, note, velocity);
+ dprintf("midi noteoff channel:%d note:%d velocity:%d\n", channel, note, velocity);
+ }
+ tone_status[tone] = MIDI_INVALID_NOTE;
+ }
+ return false;
+ }
+ case MIDI_OCTAVE_MIN ... MIDI_OCTAVE_MAX:
+ if (record->event.pressed) {
+ midi_config.octave = keycode - MIDI_OCTAVE_MIN;
+ dprintf("midi octave %d\n", midi_config.octave);
+ }
+ return false;
+ case MI_OCTD:
+ if (record->event.pressed && midi_config.octave > 0) {
+ midi_config.octave--;
+ dprintf("midi octave %d\n", midi_config.octave);
+ }
+ return false;
+ case MI_OCTU:
+ if (record->event.pressed && midi_config.octave < (MIDI_OCTAVE_MAX - MIDI_OCTAVE_MIN)) {
+ midi_config.octave++;
+ dprintf("midi octave %d\n", midi_config.octave);
+ }
+ return false;
+ case MIDI_TRANSPOSE_MIN ... MIDI_TRANSPOSE_MAX:
+ if (record->event.pressed) {
+ midi_config.transpose = keycode - MI_TRNS_0;
+ dprintf("midi transpose %d\n", midi_config.transpose);
+ }
+ return false;
+ case MI_TRNSD:
+ if (record->event.pressed && midi_config.transpose > (MIDI_TRANSPOSE_MIN - MI_TRNS_0)) {
+ midi_config.transpose--;
+ dprintf("midi transpose %d\n", midi_config.transpose);
+ }
+ return false;
+ case MI_TRNSU:
+ if (record->event.pressed && midi_config.transpose < (MIDI_TRANSPOSE_MAX - MI_TRNS_0)) {
+ const bool positive = midi_config.transpose > 0;
+ midi_config.transpose++;
+ if (positive && midi_config.transpose < 0)
+ midi_config.transpose--;
+ dprintf("midi transpose %d\n", midi_config.transpose);
+ }
+ return false;
+ case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX:
+ if (record->event.pressed) {
+ midi_config.velocity = keycode - MIDI_VELOCITY_MIN;
+ dprintf("midi velocity %d\n", midi_config.velocity);
+ }
+ return false;
+ case MI_VELD:
+ if (record->event.pressed && midi_config.velocity > 0) {
+ midi_config.velocity--;
+ dprintf("midi velocity %d\n", midi_config.velocity);
+ }
+ return false;
+ case MI_VELU:
+ if (record->event.pressed) {
+ midi_config.velocity++;
+ dprintf("midi velocity %d\n", midi_config.velocity);
+ }
+ return false;
+ case MIDI_CHANNEL_MIN ... MIDI_CHANNEL_MAX:
+ if (record->event.pressed) {
+ midi_config.channel = keycode - MIDI_CHANNEL_MIN;
+ dprintf("midi channel %d\n", midi_config.channel);
+ }
+ return false;
+ case MI_CHD:
+ if (record->event.pressed) {
+ midi_config.channel--;
+ dprintf("midi channel %d\n", midi_config.channel);
+ }
+ return false;
+ case MI_CHU:
+ if (record->event.pressed) {
+ midi_config.channel++;
+ dprintf("midi channel %d\n", midi_config.channel);
+ }
+ return false;
+ case MI_ALLOFF:
+ if (record->event.pressed) {
+ midi_send_cc(&midi_device, midi_config.channel, 0x7B, 0);
+ dprintf("midi all notes off\n");
+ }
+ return false;
+ case MI_SUS:
+ midi_send_cc(&midi_device, midi_config.channel, 0x40, record->event.pressed ? 127 : 0);
+ dprintf("midi sustain %d\n", record->event.pressed);
+ return false;
+ case MI_PORT:
+ midi_send_cc(&midi_device, midi_config.channel, 0x41, record->event.pressed ? 127 : 0);
+ dprintf("midi portamento %d\n", record->event.pressed);
+ return false;
+ case MI_SOST:
+ midi_send_cc(&midi_device, midi_config.channel, 0x42, record->event.pressed ? 127 : 0);
+ dprintf("midi sostenuto %d\n", record->event.pressed);
+ return false;
+ case MI_SOFT:
+ midi_send_cc(&midi_device, midi_config.channel, 0x43, record->event.pressed ? 127 : 0);
+ dprintf("midi soft %d\n", record->event.pressed);
+ return false;
+ case MI_LEG:
+ midi_send_cc(&midi_device, midi_config.channel, 0x43, record->event.pressed ? 127 : 0);
+ dprintf("midi legato %d\n", record->event.pressed);
+ return false;
+ case MI_MOD:
+ midi_modulation_step = record->event.pressed ? 1 : -1;
+ return false;
+ case MI_MODSD:
+ if (record->event.pressed) {
+ midi_config.modulation_interval++;
+ // prevent overflow
+ if (midi_config.modulation_interval == 0)
+ midi_config.modulation_interval--;
+ dprintf("midi modulation interval %d\n", midi_config.modulation_interval);
+ }
+ return false;
+ case MI_MODSU:
+ if (record->event.pressed && midi_config.modulation_interval > 0) {
+ midi_config.modulation_interval--;
+ dprintf("midi modulation interval %d\n", midi_config.modulation_interval);
+ }
+ return false;
+ };
+
+ return true;
+}
+
+#endif // MIDI_ADVANCED
+
+#endif // MIDI_ENABLE
diff --git a/quantum/process_keycode/process_midi.h b/quantum/process_keycode/process_midi.h
new file mode 100644
index 0000000000..ccac8981a6
--- /dev/null
+++ b/quantum/process_keycode/process_midi.h
@@ -0,0 +1,56 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_MIDI_H
+#define PROCESS_MIDI_H
+
+#include "quantum.h"
+
+#ifdef MIDI_ENABLE
+
+#ifdef MIDI_BASIC
+void process_midi_basic_noteon(uint8_t note);
+void process_midi_basic_noteoff(uint8_t note);
+void process_midi_all_notes_off(void);
+#endif
+
+#ifdef MIDI_ADVANCED
+typedef union {
+ uint32_t raw;
+ struct {
+ uint8_t octave :4;
+ int8_t transpose :4;
+ uint8_t velocity :4;
+ uint8_t channel :4;
+ uint8_t modulation_interval :4;
+ };
+} midi_config_t;
+
+midi_config_t midi_config;
+
+void midi_init(void);
+void midi_task(void);
+bool process_midi(uint16_t keycode, keyrecord_t *record);
+
+#define MIDI_INVALID_NOTE 0xFF
+#define MIDI_TONE_COUNT (MIDI_TONE_MAX - MIDI_TONE_MIN + 1)
+
+uint8_t midi_compute_note(uint16_t keycode);
+#endif // MIDI_ADVANCED
+
+#endif // MIDI_ENABLE
+
+#endif
diff --git a/quantum/process_keycode/process_music.c b/quantum/process_keycode/process_music.c
new file mode 100644
index 0000000000..217dca2807
--- /dev/null
+++ b/quantum/process_keycode/process_music.c
@@ -0,0 +1,205 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "process_music.h"
+
+#ifdef AUDIO_ENABLE
+#include "process_audio.h"
+#endif
+#if defined(MIDI_ENABLE) && defined(MIDI_BASIC)
+#include "process_midi.h"
+#endif
+
+#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))
+
+bool music_activated = false;
+uint8_t music_starting_note = 0x0C;
+int music_offset = 7;
+
+// music sequencer
+static bool music_sequence_recording = false;
+static bool music_sequence_recorded = false;
+static bool music_sequence_playing = false;
+static uint8_t music_sequence[16] = {0};
+static uint8_t music_sequence_count = 0;
+static uint8_t music_sequence_position = 0;
+
+static uint16_t music_sequence_timer = 0;
+static uint16_t music_sequence_interval = 100;
+
+static void music_noteon(uint8_t note) {
+ #ifdef AUDIO_ENABLE
+ process_audio_noteon(note);
+ #endif
+ #if defined(MIDI_ENABLE) && defined(MIDI_BASIC)
+ process_midi_basic_noteon(note);
+ #endif
+}
+
+static void music_noteoff(uint8_t note) {
+ #ifdef AUDIO_ENABLE
+ process_audio_noteoff(note);
+ #endif
+ #if defined(MIDI_ENABLE) && defined(MIDI_BASIC)
+ process_midi_basic_noteoff(note);
+ #endif
+}
+
+void music_all_notes_off(void) {
+ #ifdef AUDIO_ENABLE
+ process_audio_all_notes_off();
+ #endif
+ #if defined(MIDI_ENABLE) && defined(MIDI_BASIC)
+ process_midi_all_notes_off();
+ #endif
+}
+
+bool process_music(uint16_t keycode, keyrecord_t *record) {
+
+ if (keycode == MU_ON && record->event.pressed) {
+ music_on();
+ return false;
+ }
+
+ if (keycode == MU_OFF && record->event.pressed) {
+ music_off();
+ return false;
+ }
+
+ if (keycode == MU_TOG && record->event.pressed) {
+ if (music_activated)
+ {
+ music_off();
+ }
+ else
+ {
+ music_on();
+ }
+ return false;
+ }
+
+ if (music_activated) {
+
+ if (keycode == KC_LCTL && record->event.pressed) { // Start recording
+ music_all_notes_off();
+ music_sequence_recording = true;
+ music_sequence_recorded = false;
+ music_sequence_playing = false;
+ music_sequence_count = 0;
+ return false;
+ }
+
+ if (keycode == KC_LALT && record->event.pressed) { // Stop recording/playing
+ music_all_notes_off();
+ if (music_sequence_recording) { // was recording
+ music_sequence_recorded = true;
+ }
+ music_sequence_recording = false;
+ music_sequence_playing = false;
+ return false;
+ }
+
+ if (keycode == KC_LGUI && record->event.pressed && music_sequence_recorded) { // Start playing
+ music_all_notes_off();
+ music_sequence_recording = false;
+ music_sequence_playing = true;
+ music_sequence_position = 0;
+ music_sequence_timer = 0;
+ return false;
+ }
+
+ if (keycode == KC_UP) {
+ if (record->event.pressed)
+ music_sequence_interval-=10;
+ return false;
+ }
+
+ if (keycode == KC_DOWN) {
+ if (record->event.pressed)
+ music_sequence_interval+=10;
+ return false;
+ }
+
+ #define MUSIC_MODE_GUITAR
+
+ #ifdef MUSIC_MODE_CHROMATIC
+ uint8_t note = (music_starting_note + record->event.key.col + music_offset - 3)+12*(MATRIX_ROWS - record->event.key.row);
+ #elif defined(MUSIC_MODE_GUITAR)
+ uint8_t note = (music_starting_note + record->event.key.col + music_offset + 32)+5*(MATRIX_ROWS - record->event.key.row);
+ #elif defined(MUSIC_MODE_VIOLIN)
+ uint8_t note = (music_starting_note + record->event.key.col + music_offset + 32)+7*(MATRIX_ROWS - record->event.key.row);
+ #else
+ uint8_t note = (music_starting_note + SCALE[record->event.key.col + music_offset] - 3)+12*(MATRIX_ROWS - record->event.key.row);
+ #endif
+
+ if (record->event.pressed) {
+ music_noteon(note);
+ if (music_sequence_recording) {
+ music_sequence[music_sequence_count] = note;
+ music_sequence_count++;
+ }
+ } else {
+ music_noteoff(note);
+ }
+
+ if (keycode < 0xFF) // ignores all normal keycodes, but lets RAISE, LOWER, etc through
+ return false;
+ }
+
+ return true;
+}
+
+bool is_music_on(void) {
+ return (music_activated != 0);
+}
+
+void music_toggle(void) {
+ if (!music_activated) {
+ music_on();
+ } else {
+ music_off();
+ }
+}
+
+void music_on(void) {
+ music_activated = 1;
+ music_on_user();
+}
+
+void music_off(void) {
+ music_activated = 0;
+ music_all_notes_off();
+}
+
+void matrix_scan_music(void) {
+ if (music_sequence_playing) {
+ if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) {
+ music_sequence_timer = timer_read();
+ uint8_t prev_note = music_sequence[(music_sequence_position - 1 < 0)?(music_sequence_position - 1 + music_sequence_count):(music_sequence_position - 1)];
+ uint8_t next_note = music_sequence[music_sequence_position];
+ music_noteoff(prev_note);
+ music_noteon(next_note);
+ music_sequence_position = (music_sequence_position + 1) % music_sequence_count;
+ }
+ }
+}
+
+__attribute__ ((weak))
+void music_on_user() {}
+
+__attribute__ ((weak))
+void music_scale_user() {}
+
+#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC)) \ No newline at end of file
diff --git a/quantum/process_keycode/process_music.h b/quantum/process_keycode/process_music.h
new file mode 100644
index 0000000000..8dfbf041f4
--- /dev/null
+++ b/quantum/process_keycode/process_music.h
@@ -0,0 +1,47 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_MUSIC_H
+#define PROCESS_MUSIC_H
+
+#include "quantum.h"
+
+#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))
+
+bool process_music(uint16_t keycode, keyrecord_t *record);
+
+bool is_music_on(void);
+void music_toggle(void);
+void music_on(void);
+void music_off(void);
+
+void music_on_user(void);
+void music_scale_user(void);
+void music_all_notes_off(void);
+
+void matrix_scan_music(void);
+
+#ifndef SCALE
+#define SCALE (int8_t []){ 0 + (12*0), 2 + (12*0), 4 + (12*0), 5 + (12*0), 7 + (12*0), 9 + (12*0), 11 + (12*0), \
+ 0 + (12*1), 2 + (12*1), 4 + (12*1), 5 + (12*1), 7 + (12*1), 9 + (12*1), 11 + (12*1), \
+ 0 + (12*2), 2 + (12*2), 4 + (12*2), 5 + (12*2), 7 + (12*2), 9 + (12*2), 11 + (12*2), \
+ 0 + (12*3), 2 + (12*3), 4 + (12*3), 5 + (12*3), 7 + (12*3), 9 + (12*3), 11 + (12*3), \
+ 0 + (12*4), 2 + (12*4), 4 + (12*4), 5 + (12*4), 7 + (12*4), 9 + (12*4), 11 + (12*4), }
+#endif
+
+#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))
+
+#endif
diff --git a/quantum/process_keycode/process_printer.c b/quantum/process_keycode/process_printer.c
new file mode 100644
index 0000000000..613af70183
--- /dev/null
+++ b/quantum/process_keycode/process_printer.c
@@ -0,0 +1,270 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_printer.h"
+#include "action_util.h"
+
+bool printing_enabled = false;
+uint8_t character_shift = 0;
+
+void enable_printing(void) {
+ printing_enabled = true;
+ serial_init();
+}
+
+void disable_printing(void) {
+ printing_enabled = false;
+}
+
+uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29};
+
+// uint8_t keycode_to_ascii[0xFF][2];
+
+// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
+
+void print_char(char c) {
+ USB_Disable();
+ serial_send(c);
+ USB_Init();
+}
+
+void print_string(char c[]) {
+ for(uint8_t i = 0; i < strlen(c); i++)
+ print_char(c[i]);
+}
+
+void print_box_string(const char text[]) {
+ size_t len = strlen(text);
+ char out[len * 3 + 8];
+ out[0] = 0xDA;
+ for (uint8_t i = 0; i < len; i++) {
+ out[i+1] = 0xC4;
+ }
+ out[len + 1] = 0xBF;
+ out[len + 2] = '\n';
+
+ out[len + 3] = 0xB3;
+ for (uint8_t i = 0; i < len; i++) {
+ out[len + 4 + i] = text[i];
+ }
+ out[len * 2 + 4] = 0xB3;
+ out[len * 2 + 5] = '\n';
+
+
+ out[len * 2 + 6] = 0xC0;
+ for (uint8_t i = 0; i < len; i++) {
+ out[len * 2 + 7 + i] = 0xC4;
+ }
+ out[len * 3 + 7] = 0xD9;
+ out[len * 3 + 8] = '\n';
+
+ print_string(out);
+}
+
+bool process_printer(uint16_t keycode, keyrecord_t *record) {
+ if (keycode == PRINT_ON) {
+ enable_printing();
+ return false;
+ }
+ if (keycode == PRINT_OFF) {
+ disable_printing();
+ return false;
+ }
+
+ if (printing_enabled) {
+ switch(keycode) {
+ case KC_EXLM ... KC_RPRN:
+ case KC_UNDS:
+ case KC_PLUS:
+ case KC_LCBR:
+ case KC_RCBR:
+ case KC_PIPE:
+ case KC_TILD:
+ keycode &= 0xFF;
+ case KC_LSFT:
+ case KC_RSFT:
+ if (record->event.pressed) {
+ character_shift++;
+ } else {
+ character_shift--;
+ }
+ return false;
+ break;
+ }
+
+ switch(keycode) {
+ case KC_F1:
+ if (record->event.pressed) {
+ print_box_string("This is a line of text!");
+ }
+ return false;
+ case KC_ESC:
+ if (record->event.pressed) {
+ print_char(0x1B);
+ }
+ return false;
+ break;
+ case KC_SPC:
+ if (record->event.pressed) {
+ print_char(0x20);
+ }
+ return false;
+ break;
+ case KC_A ... KC_Z:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x41 + (keycode - KC_A));
+ } else {
+ print_char(0x61 + (keycode - KC_A));
+ }
+ }
+ return false;
+ break;
+ case KC_1 ... KC_0:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(shifted_numbers[keycode - KC_1]);
+ } else {
+ print_char(0x30 + ((keycode - KC_1 + 1) % 10));
+ }
+ }
+ return false;
+ break;
+ case KC_ENT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x0C);
+ } else {
+ print_char(0x0A);
+ }
+ }
+ return false;
+ break;
+ case KC_BSPC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x18);
+ } else {
+ print_char(0x1A);
+ }
+ }
+ return false;
+ break;
+ case KC_DOT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3E);
+ } else {
+ print_char(0x2E);
+ }
+ }
+ return false;
+ break;
+ case KC_COMM:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3C);
+ } else {
+ print_char(0x2C);
+ }
+ }
+ return false;
+ break;
+ case KC_SLSH:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3F);
+ } else {
+ print_char(0x2F);
+ }
+ }
+ return false;
+ break;
+ case KC_QUOT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x22);
+ } else {
+ print_char(0x27);
+ }
+ }
+ return false;
+ break;
+ case KC_GRV:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7E);
+ } else {
+ print_char(0x60);
+ }
+ }
+ return false;
+ break;
+ case KC_MINS:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x5F);
+ } else {
+ print_char(0x2D);
+ }
+ }
+ return false;
+ break;
+ case KC_EQL:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x2B);
+ } else {
+ print_char(0x3D);
+ }
+ }
+ return false;
+ break;
+ case KC_LBRC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7B);
+ } else {
+ print_char(0x5B);
+ }
+ }
+ return false;
+ break;
+ case KC_RBRC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7D);
+ } else {
+ print_char(0x5D);
+ }
+ }
+ return false;
+ break;
+ case KC_BSLS:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7C);
+ } else {
+ print_char(0x5C);
+ }
+ }
+ return false;
+ break;
+ }
+ }
+ return true;
+
+}
diff --git a/quantum/process_keycode/process_printer.h b/quantum/process_keycode/process_printer.h
new file mode 100644
index 0000000000..71d3a4b56a
--- /dev/null
+++ b/quantum/process_keycode/process_printer.h
@@ -0,0 +1,26 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_PRINTER_H
+#define PROCESS_PRINTER_H
+
+#include "quantum.h"
+
+#include "protocol/serial.h"
+
+bool process_printer(uint16_t keycode, keyrecord_t *record);
+
+#endif
diff --git a/quantum/process_keycode/process_printer_bb.c b/quantum/process_keycode/process_printer_bb.c
new file mode 100644
index 0000000000..3a00f169d8
--- /dev/null
+++ b/quantum/process_keycode/process_printer_bb.c
@@ -0,0 +1,276 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_printer.h"
+#include "action_util.h"
+
+bool printing_enabled = false;
+uint8_t character_shift = 0;
+
+#define SERIAL_PIN_DDR DDRD
+#define SERIAL_PIN_PORT PORTD
+#define SERIAL_PIN_MASK _BV(PD3)
+#define SERIAL_DELAY 52
+
+inline static
+void serial_delay(void) {
+ _delay_us(SERIAL_DELAY);
+}
+
+inline static
+void serial_high(void) {
+ SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
+}
+
+inline static
+void serial_low(void) {
+ SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
+}
+
+inline static
+void serial_output(void) {
+ SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
+}
+
+
+void enable_printing() {
+ printing_enabled = true;
+ serial_output();
+ serial_high();
+}
+
+void disable_printing() {
+ printing_enabled = false;
+}
+
+uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29};
+
+// uint8_t keycode_to_ascii[0xFF][2];
+
+// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
+
+void print_char(char c) {
+ uint8_t b = 8;
+ serial_output();
+ while( b-- ) {
+ if(c & (1 << b)) {
+ serial_high();
+ } else {
+ serial_low();
+ }
+ serial_delay();
+ }
+}
+
+void print_string(char c[]) {
+ for(uint8_t i = 0; i < strlen(c); i++)
+ print_char(c[i]);
+}
+
+bool process_printer(uint16_t keycode, keyrecord_t *record) {
+ if (keycode == PRINT_ON) {
+ enable_printing();
+ return false;
+ }
+ if (keycode == PRINT_OFF) {
+ disable_printing();
+ return false;
+ }
+
+ if (printing_enabled) {
+ switch(keycode) {
+ case KC_EXLM ... KC_RPRN:
+ case KC_UNDS:
+ case KC_PLUS:
+ case KC_LCBR:
+ case KC_RCBR:
+ case KC_PIPE:
+ case KC_TILD:
+ keycode &= 0xFF;
+ case KC_LSFT:
+ case KC_RSFT:
+ if (record->event.pressed) {
+ character_shift++;
+ } else {
+ character_shift--;
+ }
+ return false;
+ break;
+ }
+
+ switch(keycode) {
+ case KC_F1:
+ if (record->event.pressed) {
+ print_string("This is a line of text!\n\n\n");
+ }
+ return false;
+ case KC_ESC:
+ if (record->event.pressed) {
+ print_char(0x1B);
+ }
+ return false;
+ break;
+ case KC_SPC:
+ if (record->event.pressed) {
+ print_char(0x20);
+ }
+ return false;
+ break;
+ case KC_A ... KC_Z:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x41 + (keycode - KC_A));
+ } else {
+ print_char(0x61 + (keycode - KC_A));
+ }
+ }
+ return false;
+ break;
+ case KC_1 ... KC_0:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(shifted_numbers[keycode - KC_1]);
+ } else {
+ print_char(0x30 + ((keycode - KC_1 + 1) % 10));
+ }
+ }
+ return false;
+ break;
+ case KC_ENT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x0C);
+ } else {
+ print_char(0x0A);
+ }
+ }
+ return false;
+ break;
+ case KC_BSPC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x18);
+ } else {
+ print_char(0x1A);
+ }
+ }
+ return false;
+ break;
+ case KC_DOT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3E);
+ } else {
+ print_char(0x2E);
+ }
+ }
+ return false;
+ break;
+ case KC_COMM:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3C);
+ } else {
+ print_char(0x2C);
+ }
+ }
+ return false;
+ break;
+ case KC_SLSH:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x3F);
+ } else {
+ print_char(0x2F);
+ }
+ }
+ return false;
+ break;
+ case KC_QUOT:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x22);
+ } else {
+ print_char(0x27);
+ }
+ }
+ return false;
+ break;
+ case KC_GRV:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7E);
+ } else {
+ print_char(0x60);
+ }
+ }
+ return false;
+ break;
+ case KC_MINS:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x5F);
+ } else {
+ print_char(0x2D);
+ }
+ }
+ return false;
+ break;
+ case KC_EQL:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x2B);
+ } else {
+ print_char(0x3D);
+ }
+ }
+ return false;
+ break;
+ case KC_LBRC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7B);
+ } else {
+ print_char(0x5B);
+ }
+ }
+ return false;
+ break;
+ case KC_RBRC:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7D);
+ } else {
+ print_char(0x5D);
+ }
+ }
+ return false;
+ break;
+ case KC_BSLS:
+ if (record->event.pressed) {
+ if (character_shift) {
+ print_char(0x7C);
+ } else {
+ print_char(0x5C);
+ }
+ }
+ return false;
+ break;
+ }
+ }
+ return true;
+
+}
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
new file mode 100644
index 0000000000..4fd45810bb
--- /dev/null
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -0,0 +1,166 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "quantum.h"
+#include "action_tapping.h"
+
+uint8_t get_oneshot_mods(void);
+
+static uint16_t last_td;
+static int8_t highest_td = -1;
+
+void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data) {
+ qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data;
+
+ if (state->count == 1) {
+ register_code16 (pair->kc1);
+ } else if (state->count == 2) {
+ register_code16 (pair->kc2);
+ }
+}
+
+void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data) {
+ qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data;
+
+ if (state->count == 1) {
+ unregister_code16 (pair->kc1);
+ } else if (state->count == 2) {
+ unregister_code16 (pair->kc2);
+ }
+}
+
+static inline void _process_tap_dance_action_fn (qk_tap_dance_state_t *state,
+ void *user_data,
+ qk_tap_dance_user_fn_t fn)
+{
+ if (fn) {
+ fn(state, user_data);
+ }
+}
+
+static inline void process_tap_dance_action_on_each_tap (qk_tap_dance_action_t *action)
+{
+ _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_each_tap);
+}
+
+static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_action_t *action)
+{
+ if (action->state.finished)
+ return;
+ action->state.finished = true;
+ add_mods(action->state.oneshot_mods);
+ send_keyboard_report();
+ _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_dance_finished);
+}
+
+static inline void process_tap_dance_action_on_reset (qk_tap_dance_action_t *action)
+{
+ _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_reset);
+ del_mods(action->state.oneshot_mods);
+ send_keyboard_report();
+}
+
+bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
+ uint16_t idx = keycode - QK_TAP_DANCE;
+ qk_tap_dance_action_t *action;
+
+ if (last_td && last_td != keycode) {
+ (&tap_dance_actions[last_td - QK_TAP_DANCE])->state.interrupted = true;
+ }
+
+ switch(keycode) {
+ case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
+ if ((int16_t)idx > highest_td)
+ highest_td = idx;
+ action = &tap_dance_actions[idx];
+
+ action->state.pressed = record->event.pressed;
+ if (record->event.pressed) {
+ action->state.keycode = keycode;
+ action->state.count++;
+ action->state.timer = timer_read();
+ action->state.oneshot_mods = get_oneshot_mods();
+ process_tap_dance_action_on_each_tap (action);
+
+ if (last_td && last_td != keycode) {
+ qk_tap_dance_action_t *paction = &tap_dance_actions[last_td - QK_TAP_DANCE];
+ paction->state.interrupted = true;
+ process_tap_dance_action_on_dance_finished (paction);
+ reset_tap_dance (&paction->state);
+ }
+
+ last_td = keycode;
+ }
+
+ break;
+
+ default:
+ if (!record->event.pressed)
+ return true;
+
+ if (highest_td == -1)
+ return true;
+
+ for (int i = 0; i <= highest_td; i++) {
+ action = &tap_dance_actions[i];
+ if (action->state.count == 0)
+ continue;
+ action->state.interrupted = true;
+ process_tap_dance_action_on_dance_finished (action);
+ reset_tap_dance (&action->state);
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+
+void matrix_scan_tap_dance () {
+ if (highest_td == -1)
+ return;
+ uint16_t tap_user_defined;
+
+for (uint8_t i = 0; i <= highest_td; i++) {
+ qk_tap_dance_action_t *action = &tap_dance_actions[i];
+ if(action->custom_tapping_term > 0 ) {
+ tap_user_defined = action->custom_tapping_term;
+ }
+ else{
+ tap_user_defined = TAPPING_TERM;
+ }
+ if (action->state.count && timer_elapsed (action->state.timer) > tap_user_defined) {
+ process_tap_dance_action_on_dance_finished (action);
+ reset_tap_dance (&action->state);
+ }
+ }
+}
+
+void reset_tap_dance (qk_tap_dance_state_t *state) {
+ qk_tap_dance_action_t *action;
+
+ if (state->pressed)
+ return;
+
+ action = &tap_dance_actions[state->keycode - QK_TAP_DANCE];
+
+ process_tap_dance_action_on_reset (action);
+
+ state->count = 0;
+ state->interrupted = false;
+ state->finished = false;
+ last_td = 0;
+}
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
new file mode 100644
index 0000000000..f42c154a05
--- /dev/null
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -0,0 +1,95 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef PROCESS_TAP_DANCE_H
+#define PROCESS_TAP_DANCE_H
+
+#ifdef TAP_DANCE_ENABLE
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+typedef struct
+{
+ uint8_t count;
+ uint8_t oneshot_mods;
+ uint16_t keycode;
+ uint16_t timer;
+ bool interrupted;
+ bool pressed;
+ bool finished;
+} qk_tap_dance_state_t;
+
+#define TD(n) (QK_TAP_DANCE + n)
+
+typedef void (*qk_tap_dance_user_fn_t) (qk_tap_dance_state_t *state, void *user_data);
+
+typedef struct
+{
+ struct {
+ qk_tap_dance_user_fn_t on_each_tap;
+ qk_tap_dance_user_fn_t on_dance_finished;
+ qk_tap_dance_user_fn_t on_reset;
+ } fn;
+ qk_tap_dance_state_t state;
+ uint16_t custom_tapping_term;
+ void *user_data;
+} qk_tap_dance_action_t;
+
+typedef struct
+{
+ uint16_t kc1;
+ uint16_t kc2;
+} qk_tap_dance_pair_t;
+
+#define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) { \
+ .fn = { NULL, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset }, \
+ .user_data = (void *)&((qk_tap_dance_pair_t) { kc1, kc2 }), \
+ }
+
+#define ACTION_TAP_DANCE_FN(user_fn) { \
+ .fn = { NULL, user_fn, NULL }, \
+ .user_data = NULL, \
+ }
+
+#define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) { \
+ .fn = { user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset }, \
+ .user_data = NULL, \
+ }
+
+#define ACTION_TAP_DANCE_FN_ADVANCED_TIME(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, tap_specific_tapping_term) { \
+ .fn = { user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset }, \
+ .user_data = NULL, \
+ .custom_tapping_term = tap_specific_tapping_term, \
+ }
+
+extern qk_tap_dance_action_t tap_dance_actions[];
+
+/* To be used internally */
+
+bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
+void matrix_scan_tap_dance (void);
+void reset_tap_dance (qk_tap_dance_state_t *state);
+
+void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data);
+void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data);
+
+#else
+
+#define TD(n) KC_NO
+
+#endif
+
+#endif
diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c
new file mode 100644
index 0000000000..86c0937f5e
--- /dev/null
+++ b/quantum/process_keycode/process_ucis.c
@@ -0,0 +1,149 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_ucis.h"
+
+qk_ucis_state_t qk_ucis_state;
+
+void qk_ucis_start(void) {
+ qk_ucis_state.count = 0;
+ qk_ucis_state.in_progress = true;
+
+ qk_ucis_start_user();
+}
+
+__attribute__((weak))
+void qk_ucis_start_user(void) {
+ unicode_input_start();
+ register_hex(0x2328);
+ unicode_input_finish();
+}
+
+static bool is_uni_seq(char *seq) {
+ uint8_t i;
+
+ for (i = 0; seq[i]; i++) {
+ uint16_t code;
+ if (('1' <= seq[i]) && (seq[i] <= '0'))
+ code = seq[i] - '1' + KC_1;
+ else
+ code = seq[i] - 'a' + KC_A;
+
+ if (i > qk_ucis_state.count || qk_ucis_state.codes[i] != code)
+ return false;
+ }
+
+ return (qk_ucis_state.codes[i] == KC_ENT ||
+ qk_ucis_state.codes[i] == KC_SPC);
+}
+
+__attribute__((weak))
+void qk_ucis_symbol_fallback (void) {
+ for (uint8_t i = 0; i < qk_ucis_state.count - 1; i++) {
+ uint8_t code = qk_ucis_state.codes[i];
+ register_code(code);
+ unregister_code(code);
+ wait_ms(UNICODE_TYPE_DELAY);
+ }
+}
+
+void register_ucis(const char *hex) {
+ for(int i = 0; hex[i]; i++) {
+ uint8_t kc = 0;
+ char c = hex[i];
+
+ switch (c) {
+ case '0':
+ kc = KC_0;
+ break;
+ case '1' ... '9':
+ kc = c - '1' + KC_1;
+ break;
+ case 'a' ... 'f':
+ kc = c - 'a' + KC_A;
+ break;
+ case 'A' ... 'F':
+ kc = c - 'A' + KC_A;
+ break;
+ }
+
+ if (kc) {
+ register_code (kc);
+ unregister_code (kc);
+ wait_ms (UNICODE_TYPE_DELAY);
+ }
+ }
+}
+
+bool process_ucis (uint16_t keycode, keyrecord_t *record) {
+ uint8_t i;
+
+ if (!qk_ucis_state.in_progress)
+ return true;
+
+ if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH &&
+ !(keycode == KC_BSPC || keycode == KC_ESC || keycode == KC_SPC || keycode == KC_ENT)) {
+ return false;
+ }
+
+ if (!record->event.pressed)
+ return true;
+
+ qk_ucis_state.codes[qk_ucis_state.count] = keycode;
+ qk_ucis_state.count++;
+
+ if (keycode == KC_BSPC) {
+ if (qk_ucis_state.count >= 2) {
+ qk_ucis_state.count -= 2;
+ return true;
+ } else {
+ qk_ucis_state.count--;
+ return false;
+ }
+ }
+
+ if (keycode == KC_ENT || keycode == KC_SPC || keycode == KC_ESC) {
+ bool symbol_found = false;
+
+ for (i = qk_ucis_state.count; i > 0; i--) {
+ register_code (KC_BSPC);
+ unregister_code (KC_BSPC);
+ wait_ms(UNICODE_TYPE_DELAY);
+ }
+
+ if (keycode == KC_ESC) {
+ qk_ucis_state.in_progress = false;
+ return false;
+ }
+
+ unicode_input_start();
+ for (i = 0; ucis_symbol_table[i].symbol; i++) {
+ if (is_uni_seq (ucis_symbol_table[i].symbol)) {
+ symbol_found = true;
+ register_ucis(ucis_symbol_table[i].code + 2);
+ break;
+ }
+ }
+ if (!symbol_found) {
+ qk_ucis_symbol_fallback();
+ }
+ unicode_input_finish();
+
+ qk_ucis_state.in_progress = false;
+ return false;
+ }
+ return true;
+}
diff --git a/quantum/process_keycode/process_ucis.h b/quantum/process_keycode/process_ucis.h
new file mode 100644
index 0000000000..3f736a709f
--- /dev/null
+++ b/quantum/process_keycode/process_ucis.h
@@ -0,0 +1,51 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_UCIS_H
+#define PROCESS_UCIS_H
+
+#include "quantum.h"
+#include "process_unicode_common.h"
+
+#ifndef UCIS_MAX_SYMBOL_LENGTH
+#define UCIS_MAX_SYMBOL_LENGTH 32
+#endif
+
+typedef struct {
+ char *symbol;
+ char *code;
+} qk_ucis_symbol_t;
+
+typedef struct {
+ uint8_t count;
+ uint16_t codes[UCIS_MAX_SYMBOL_LENGTH];
+ bool in_progress:1;
+} qk_ucis_state_t;
+
+extern qk_ucis_state_t qk_ucis_state;
+
+#define UCIS_TABLE(...) {__VA_ARGS__, {NULL, NULL}}
+#define UCIS_SYM(name, code) {name, #code}
+
+extern const qk_ucis_symbol_t ucis_symbol_table[];
+
+void qk_ucis_start(void);
+void qk_ucis_start_user(void);
+void qk_ucis_symbol_fallback (void);
+void register_ucis(const char *hex);
+bool process_ucis (uint16_t keycode, keyrecord_t *record);
+
+#endif
diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c
new file mode 100644
index 0000000000..fd008eca12
--- /dev/null
+++ b/quantum/process_keycode/process_unicode.c
@@ -0,0 +1,35 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "process_unicode.h"
+#include "action_util.h"
+#include "eeprom.h"
+
+static uint8_t first_flag = 0;
+
+bool process_unicode(uint16_t keycode, keyrecord_t *record) {
+ if (keycode > QK_UNICODE && record->event.pressed) {
+ if (first_flag == 0) {
+ set_unicode_input_mode(eeprom_read_byte(EECONFIG_UNICODEMODE));
+ first_flag = 1;
+ }
+ uint16_t unicode = keycode & 0x7FFF;
+ unicode_input_start();
+ register_hex(unicode);
+ unicode_input_finish();
+ }
+ return true;
+}
+
diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
new file mode 100644
index 0000000000..c525b74f03
--- /dev/null
+++ b/quantum/process_keycode/process_unicode.h
@@ -0,0 +1,24 @@
+/* Copyright 2016 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef PROCESS_UNICODE_H
+#define PROCESS_UNICODE_H
+
+#include "quantum.h"
+#include "process_unicode_common.h"
+
+bool process_unicode(uint16_t keycode, keyrecord_t *record);
+
+#endif
diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
new file mode 100644
index 0000000000..84b5d673dd
--- /dev/null
+++ b/quantum/process_keycode/process_unicode_common.c
@@ -0,0 +1,116 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_unicode_common.h"
+#include "eeprom.h"
+
+static uint8_t input_mode;
+uint8_t mods;
+
+void set_unicode_input_mode(uint8_t os_target)
+{
+ input_mode = os_target;
+ eeprom_update_byte(EECONFIG_UNICODEMODE, os_target);
+}
+
+uint8_t get_unicode_input_mode(void) {
+ return input_mode;
+}
+
+__attribute__((weak))
+void unicode_input_start (void) {
+ // save current mods
+ mods = keyboard_report->mods;
+
+ // unregister all mods to start from clean state
+ if (mods & MOD_BIT(KC_LSFT)) unregister_code(KC_LSFT);
+ if (mods & MOD_BIT(KC_RSFT)) unregister_code(KC_RSFT);
+ if (mods & MOD_BIT(KC_LCTL)) unregister_code(KC_LCTL);
+ if (mods & MOD_BIT(KC_RCTL)) unregister_code(KC_RCTL);
+ if (mods & MOD_BIT(KC_LALT)) unregister_code(KC_LALT);
+ if (mods & MOD_BIT(KC_RALT)) unregister_code(KC_RALT);
+ if (mods & MOD_BIT(KC_LGUI)) unregister_code(KC_LGUI);
+ if (mods & MOD_BIT(KC_RGUI)) unregister_code(KC_RGUI);
+
+ switch(input_mode) {
+ case UC_OSX:
+ register_code(KC_LALT);
+ break;
+ case UC_LNX:
+ register_code(KC_LCTL);
+ register_code(KC_LSFT);
+ register_code(KC_U);
+ unregister_code(KC_U);
+ unregister_code(KC_LSFT);
+ unregister_code(KC_LCTL);
+ break;
+ case UC_WIN:
+ register_code(KC_LALT);
+ register_code(KC_PPLS);
+ unregister_code(KC_PPLS);
+ break;
+ case UC_WINC:
+ register_code(KC_RALT);
+ unregister_code(KC_RALT);
+ register_code(KC_U);
+ unregister_code(KC_U);
+ }
+ wait_ms(UNICODE_TYPE_DELAY);
+}
+
+__attribute__((weak))
+void unicode_input_finish (void) {
+ switch(input_mode) {
+ case UC_OSX:
+ case UC_WIN:
+ unregister_code(KC_LALT);
+ break;
+ case UC_LNX:
+ register_code(KC_SPC);
+ unregister_code(KC_SPC);
+ break;
+ }
+
+ // reregister previously set mods
+ if (mods & MOD_BIT(KC_LSFT)) register_code(KC_LSFT);
+ if (mods & MOD_BIT(KC_RSFT)) register_code(KC_RSFT);
+ if (mods & MOD_BIT(KC_LCTL)) register_code(KC_LCTL);
+ if (mods & MOD_BIT(KC_RCTL)) register_code(KC_RCTL);
+ if (mods & MOD_BIT(KC_LALT)) register_code(KC_LALT);
+ if (mods & MOD_BIT(KC_RALT)) register_code(KC_RALT);
+ if (mods & MOD_BIT(KC_LGUI)) register_code(KC_LGUI);
+ if (mods & MOD_BIT(KC_RGUI)) register_code(KC_RGUI);
+}
+
+__attribute__((weak))
+uint16_t hex_to_keycode(uint8_t hex)
+{
+ if (hex == 0x0) {
+ return KC_0;
+ } else if (hex < 0xA) {
+ return KC_1 + (hex - 0x1);
+ } else {
+ return KC_A + (hex - 0xA);
+ }
+}
+
+void register_hex(uint16_t hex) {
+ for(int i = 3; i >= 0; i--) {
+ uint8_t digit = ((hex >> (i*4)) & 0xF);
+ register_code(hex_to_keycode(digit));
+ unregister_code(hex_to_keycode(digit));
+ }
+}
diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h
new file mode 100644
index 0000000000..f5be1da5cb
--- /dev/null
+++ b/quantum/process_keycode/process_unicode_common.h
@@ -0,0 +1,148 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_UNICODE_COMMON_H
+#define PROCESS_UNICODE_COMMON_H
+
+#include "quantum.h"
+
+#ifndef UNICODE_TYPE_DELAY
+#define UNICODE_TYPE_DELAY 10
+#endif
+
+__attribute__ ((unused))
+static uint8_t input_mode;
+
+void set_unicode_input_mode(uint8_t os_target);
+uint8_t get_unicode_input_mode(void);
+void unicode_input_start(void);
+void unicode_input_finish(void);
+void register_hex(uint16_t hex);
+
+#define UC_OSX 0 // Mac OS X
+#define UC_LNX 1 // Linux
+#define UC_WIN 2 // Windows 'HexNumpad'
+#define UC_BSD 3 // BSD (not implemented)
+#define UC_WINC 4 // WinCompose https://github.com/samhocevar/wincompose
+
+#define UC_BSPC UC(0x0008)
+
+#define UC_SPC UC(0x0020)
+
+#define UC_EXLM UC(0x0021)
+#define UC_DQUT UC(0x0022)
+#define UC_HASH UC(0x0023)
+#define UC_DLR UC(0x0024)
+#define UC_PERC UC(0x0025)
+#define UC_AMPR UC(0x0026)
+#define UC_QUOT UC(0x0027)
+#define UC_LPRN UC(0x0028)
+#define UC_RPRN UC(0x0029)
+#define UC_ASTR UC(0x002A)
+#define UC_PLUS UC(0x002B)
+#define UC_COMM UC(0x002C)
+#define UC_DASH UC(0x002D)
+#define UC_DOT UC(0x002E)
+#define UC_SLSH UC(0x002F)
+
+#define UC_0 UC(0x0030)
+#define UC_1 UC(0x0031)
+#define UC_2 UC(0x0032)
+#define UC_3 UC(0x0033)
+#define UC_4 UC(0x0034)
+#define UC_5 UC(0x0035)
+#define UC_6 UC(0x0036)
+#define UC_7 UC(0x0037)
+#define UC_8 UC(0x0038)
+#define UC_9 UC(0x0039)
+
+#define UC_COLN UC(0x003A)
+#define UC_SCLN UC(0x003B)
+#define UC_LT UC(0x003C)
+#define UC_EQL UC(0x003D)
+#define UC_GT UC(0x003E)
+#define UC_QUES UC(0x003F)
+#define UC_AT UC(0x0040)
+
+#define UC_A UC(0x0041)
+#define UC_B UC(0x0042)
+#define UC_C UC(0x0043)
+#define UC_D UC(0x0044)
+#define UC_E UC(0x0045)
+#define UC_F UC(0x0046)
+#define UC_G UC(0x0047)
+#define UC_H UC(0x0048)
+#define UC_I UC(0x0049)
+#define UC_J UC(0x004A)
+#define UC_K UC(0x004B)
+#define UC_L UC(0x004C)
+#define UC_M UC(0x004D)
+#define UC_N UC(0x004E)
+#define UC_O UC(0x004F)
+#define UC_P UC(0x0050)
+#define UC_Q UC(0x0051)
+#define UC_R UC(0x0052)
+#define UC_S UC(0x0053)
+#define UC_T UC(0x0054)
+#define UC_U UC(0x0055)
+#define UC_V UC(0x0056)
+#define UC_W UC(0x0057)
+#define UC_X UC(0x0058)
+#define UC_Y UC(0x0059)
+#define UC_Z UC(0x005A)
+
+#define UC_LBRC UC(0x005B)
+#define UC_BSLS UC(0x005C)
+#define UC_RBRC UC(0x005D)
+#define UC_CIRM UC(0x005E)
+#define UC_UNDR UC(0x005F)
+
+#define UC_GRV UC(0x0060)
+
+#define UC_a UC(0x0061)
+#define UC_b UC(0x0062)
+#define UC_c UC(0x0063)
+#define UC_d UC(0x0064)
+#define UC_e UC(0x0065)
+#define UC_f UC(0x0066)
+#define UC_g UC(0x0067)
+#define UC_h UC(0x0068)
+#define UC_i UC(0x0069)
+#define UC_j UC(0x006A)
+#define UC_k UC(0x006B)
+#define UC_l UC(0x006C)
+#define UC_m UC(0x006D)
+#define UC_n UC(0x006E)
+#define UC_o UC(0x006F)
+#define UC_p UC(0x0070)
+#define UC_q UC(0x0071)
+#define UC_r UC(0x0072)
+#define UC_s UC(0x0073)
+#define UC_t UC(0x0074)
+#define UC_u UC(0x0075)
+#define UC_v UC(0x0076)
+#define UC_w UC(0x0077)
+#define UC_x UC(0x0078)
+#define UC_y UC(0x0079)
+#define UC_z UC(0x007A)
+
+#define UC_LCBR UC(0x007B)
+#define UC_PIPE UC(0x007C)
+#define UC_RCBR UC(0x007D)
+#define UC_TILD UC(0x007E)
+#define UC_DEL UC(0x007F)
+
+#endif
diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c
new file mode 100644
index 0000000000..75f35112b1
--- /dev/null
+++ b/quantum/process_keycode/process_unicodemap.c
@@ -0,0 +1,72 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "process_unicodemap.h"
+#include "process_unicode_common.h"
+
+__attribute__((weak))
+const uint32_t PROGMEM unicode_map[] = {
+};
+
+void register_hex32(uint32_t hex) {
+ bool onzerostart = true;
+ for(int i = 7; i >= 0; i--) {
+ if (i <= 3) {
+ onzerostart = false;
+ }
+ uint8_t digit = ((hex >> (i*4)) & 0xF);
+ if (digit == 0) {
+ if (!onzerostart) {
+ register_code(hex_to_keycode(digit));
+ unregister_code(hex_to_keycode(digit));
+ }
+ } else {
+ register_code(hex_to_keycode(digit));
+ unregister_code(hex_to_keycode(digit));
+ onzerostart = false;
+ }
+ }
+}
+
+__attribute__((weak))
+void unicode_map_input_error() {}
+
+bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
+ uint8_t input_mode = get_unicode_input_mode();
+ if ((keycode & QK_UNICODE_MAP) == QK_UNICODE_MAP && record->event.pressed) {
+ const uint32_t* map = unicode_map;
+ uint16_t index = keycode - QK_UNICODE_MAP;
+ uint32_t code = pgm_read_dword(&map[index]);
+ if (code > 0xFFFF && code <= 0x10ffff && input_mode == UC_OSX) {
+ // Convert to UTF-16 surrogate pair
+ code -= 0x10000;
+ uint32_t lo = code & 0x3ff;
+ uint32_t hi = (code & 0xffc00) >> 10;
+ unicode_input_start();
+ register_hex32(hi + 0xd800);
+ register_hex32(lo + 0xdc00);
+ unicode_input_finish();
+ } else if ((code > 0x10ffff && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) {
+ // when character is out of range supported by the OS
+ unicode_map_input_error();
+ } else {
+ unicode_input_start();
+ register_hex32(code);
+ unicode_input_finish();
+ }
+ }
+ return true;
+}
diff --git a/quantum/process_keycode/process_unicodemap.h b/quantum/process_keycode/process_unicodemap.h
new file mode 100644
index 0000000000..929c88c0b6
--- /dev/null
+++ b/quantum/process_keycode/process_unicodemap.h
@@ -0,0 +1,25 @@
+/* Copyright 2017 Jack Humbert
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef PROCESS_UNICODEMAP_H
+#define PROCESS_UNICODEMAP_H
+
+#include "quantum.h"
+#include "process_unicode_common.h"
+
+void unicode_map_input_error(void);
+bool process_unicode_map(uint16_t keycode, keyrecord_t *record);
+#endif