summary refs log tree commit diff
path: root/quantum
diff options
context:
space:
mode:
authorluc wastiaux <luc.wastiaux@airpost.net>2017-02-13 07:44:28 +0800
committerluc wastiaux <luc.wastiaux@airpost.net>2017-02-13 07:44:28 +0800
commit352d79e1fbbd7eea65793abb261dd544fec7bb1c (patch)
treee006ee8098826054d93939b2a59521f6c032e10d /quantum
parent5f8535b356e66975e20f1a573ab104db75f8b33c (diff)
parent4505db5d0e0c2423645cc9117f29b942d4841c5b (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'quantum')
-rw-r--r--quantum/config_common.h6
-rw-r--r--quantum/keymap_common.c7
-rw-r--r--quantum/keymap_extras/keymap_br_abnt2.h16
-rwxr-xr-xquantum/light_ws2812.c6
-rw-r--r--quantum/matrix.c14
-rw-r--r--quantum/process_keycode/process_combo.c134
-rw-r--r--quantum/process_keycode/process_combo.h43
-rw-r--r--quantum/process_keycode/process_tap_dance.c7
-rw-r--r--quantum/process_keycode/process_tap_dance.h1
-rw-r--r--quantum/process_keycode/process_unicode.c13
-rw-r--r--quantum/quantum.c167
-rw-r--r--quantum/quantum.h5
-rw-r--r--quantum/quantum_keycodes.h43
-rw-r--r--quantum/rgblight.c35
-rw-r--r--quantum/rgblight.h11
-rw-r--r--quantum/template/config.h2
-rw-r--r--quantum/visualizer/visualizer.c58
-rw-r--r--quantum/visualizer/visualizer.h9
18 files changed, 509 insertions, 68 deletions
diff --git a/quantum/config_common.h b/quantum/config_common.h
index 4bdb2065d9..28f68b9c70 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -2,8 +2,10 @@
 #define CONFIG_DEFINITIONS_H
 
 /* diode directions */
-#define COL2ROW 0
-#define ROW2COL 1
+#define COL2ROW       0
+#define ROW2COL       1
+#define CUSTOM_MATRIX 2 /* Disables built-in matrix scanning code */
+
 /* I/O pins */
 #ifndef F0
     #define B0 0x30
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index eced3d2bba..54b872d49e 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -80,7 +80,10 @@ action_t action_for_key(uint8_t layer, keypos_t key)
             action.code = keymap_function_id_to_action( (int)keycode & 0xFFF );
             break;
         case QK_MACRO ... QK_MACRO_MAX:
-            action.code = ACTION_MACRO(keycode & 0xFF);
+            if (keycode & 0x800) // tap macros have upper bit set
+                action.code = ACTION_MACRO_TAP(keycode & 0xFF);
+            else
+                action.code = ACTION_MACRO(keycode & 0xFF);
             break;
         case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
             action.code = ACTION_LAYER_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF);
@@ -117,7 +120,7 @@ action_t action_for_key(uint8_t layer, keypos_t key)
             action.code = ACTION_MODS_ONESHOT(mod);
             break;
         case QK_MOD_TAP ... QK_MOD_TAP_MAX:
-            action.code = ACTION_MODS_TAP_KEY((keycode >> 0x8) & 0xF, keycode & 0xFF);
+            action.code = ACTION_MODS_TAP_KEY((keycode >> 0x8) & 0x1F, keycode & 0xFF);
             break;
     #ifdef BACKLIGHT_ENABLE
         case BL_0 ... BL_15:
diff --git a/quantum/keymap_extras/keymap_br_abnt2.h b/quantum/keymap_extras/keymap_br_abnt2.h
index 0df177721d..b001139dd4 100644
--- a/quantum/keymap_extras/keymap_br_abnt2.h
+++ b/quantum/keymap_extras/keymap_br_abnt2.h
@@ -1,3 +1,19 @@
+/* Copyright 2017 Potiguar Faga
+ *
+ * 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 KEYMAP_BR_ABNT2_H
 #define KEYMAP_BR_ABNT2_H
 
diff --git a/quantum/light_ws2812.c b/quantum/light_ws2812.c
index a883b13884..55bdd9cd81 100755
--- a/quantum/light_ws2812.c
+++ b/quantum/light_ws2812.c
@@ -70,7 +70,7 @@ void I2C_WriteBit(unsigned char c)
 
 // Inits bitbanging port, must be called before using the functions below
 //
-void I2C_Init()
+void I2C_Init(void)
 {
     I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
 
@@ -82,7 +82,7 @@ void I2C_Init()
 
 // Send a START Condition
 //
-void I2C_Start()
+void I2C_Start(void)
 {
     // set both to high at the same time
     I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
@@ -97,7 +97,7 @@ void I2C_Start()
 
 // Send a STOP Condition
 //
-void I2C_Stop()
+void I2C_Stop(void)
 {
     I2C_CLOCK_HI();
     _delay_us(I2C_DELAY);
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 07eb87bc36..ac523482ad 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -60,13 +60,14 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
     extern const matrix_row_t matrix_mask[];
 #endif
 
+#if (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
 static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
 static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+#endif
 
 /* matrix state(1:on, 0:off) */
 static matrix_row_t matrix[MATRIX_ROWS];
 
-static matrix_row_t matrix_raw[MATRIX_ROWS];
 static matrix_row_t matrix_debouncing[MATRIX_ROWS];
 
 
@@ -76,7 +77,7 @@ static matrix_row_t matrix_debouncing[MATRIX_ROWS];
     static void unselect_rows(void);
     static void select_row(uint8_t row);
     static void unselect_row(uint8_t row);
-#else // ROW2COL
+#elif (DIODE_DIRECTION == ROW2COL)
     static void init_rows(void);
     static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
     static void unselect_cols(void);
@@ -133,7 +134,7 @@ uint8_t matrix_cols(void) {
 //         /* PORTxn */
 //         _SFR_IO8((col_pins[c] >> 4) + 2) |= _BV(col_pins[c] & 0xF);
 //     }
-// #else
+// #elif (DIODE_DIRECTION == ROW2COL)
 //     for (int8_t c = MATRIX_COLS - 1; c >= 0; --c) {
 //         /* DDRxn */
 //         _SFR_IO8((col_pins[c] >> 4) + 1) |= _BV(col_pins[c] & 0xF);
@@ -158,7 +159,7 @@ void matrix_init(void) {
 #if (DIODE_DIRECTION == COL2ROW)
     unselect_rows();
     init_cols();
-#else // ROW2COL
+#elif (DIODE_DIRECTION == ROW2COL)
     unselect_cols();
     init_rows();
 #endif
@@ -166,7 +167,6 @@ void matrix_init(void) {
     // initialize matrix state: all keys off
     for (uint8_t i=0; i < MATRIX_ROWS; i++) {
         matrix[i] = 0;
-        matrix_raw[i] = 0;
         matrix_debouncing[i] = 0;
     }
 
@@ -194,7 +194,7 @@ uint8_t matrix_scan(void)
 
     }
 
-#else // ROW2COL
+#elif (DIODE_DIRECTION == ROW2COL)
 
     // Set col, read rows
     for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
@@ -336,7 +336,7 @@ static void unselect_rows(void)
     }
 }
 
-#else // ROW2COL
+#elif (DIODE_DIRECTION == ROW2COL)
 
 static void init_rows(void)
 {
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
new file mode 100644
index 0000000000..e2189ad98b
--- /dev/null
+++ b/quantum/process_keycode/process_combo.c
@@ -0,0 +1,134 @@
+#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..847f2b7376
--- /dev/null
+++ b/quantum/process_keycode/process_combo.h
@@ -0,0 +1,43 @@
+#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_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 6ae362c4c2..403dca5380 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -43,12 +43,16 @@ static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_acti
   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) {
@@ -70,6 +74,7 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
       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) {
@@ -109,7 +114,7 @@ void matrix_scan_tap_dance () {
   if (highest_td == -1)
     return;
 
-  for (int i = 0; i <= highest_td; i++) {
+for (int i = 0; i <= highest_td; i++) {
     qk_tap_dance_action_t *action = &tap_dance_actions[i];
 
     if (action->state.count && timer_elapsed (action->state.timer) > TAPPING_TERM) {
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index f753cbba66..726752ecc7 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -9,6 +9,7 @@
 typedef struct
 {
   uint8_t count;
+  uint8_t oneshot_mods;
   uint16_t keycode;
   uint16_t timer;
   bool interrupted;
diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c
index a30e93ae32..9d01a592d2 100644
--- a/quantum/process_keycode/process_unicode.c
+++ b/quantum/process_keycode/process_unicode.c
@@ -139,9 +139,18 @@ void unicode_map_input_error() {}
 bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
   if ((keycode & QK_UNICODE_MAP) == QK_UNICODE_MAP && record->event.pressed) {
     const uint32_t* map = unicode_map;
-    uint16_t index = keycode & 0x7FF;
+    uint16_t index = keycode - QK_UNICODE_MAP;
     uint32_t code = pgm_read_dword_far(&map[index]);
-    if ((code > 0xFFFF && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) {
+    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 {
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 63ffe2074e..45ea8cb73c 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -1,4 +1,7 @@
 #include "quantum.h"
+#ifdef PROTOCOL_LUFA
+#include "outputselect.h"
+#endif
 
 #ifndef TAPPING_TERM
 #define TAPPING_TERM 200
@@ -33,14 +36,42 @@ static void do_code16 (uint16_t code, void (*f) (uint8_t)) {
     f(KC_RGUI);
 }
 
+static inline void qk_register_weak_mods(uint8_t kc) {
+    add_weak_mods(MOD_BIT(kc));
+    send_keyboard_report();
+}
+
+static inline void qk_unregister_weak_mods(uint8_t kc) {
+    del_weak_mods(MOD_BIT(kc));
+    send_keyboard_report();
+}
+
+static inline void qk_register_mods(uint8_t kc) {
+    add_weak_mods(MOD_BIT(kc));
+    send_keyboard_report();
+}
+
+static inline void qk_unregister_mods(uint8_t kc) {
+    del_weak_mods(MOD_BIT(kc));
+    send_keyboard_report();
+}
+
 void register_code16 (uint16_t code) {
-  do_code16 (code, register_code);
+  if (IS_MOD(code) || code == KC_NO) {
+      do_code16 (code, qk_register_mods);
+  } else {
+      do_code16 (code, qk_register_weak_mods);
+  }
   register_code (code);
 }
 
 void unregister_code16 (uint16_t code) {
   unregister_code (code);
-  do_code16 (code, unregister_code);
+  if (IS_MOD(code) || code == KC_NO) {
+      do_code16 (code, qk_unregister_mods);
+  } else {
+      do_code16 (code, qk_unregister_weak_mods);
+  }
 }
 
 __attribute__ ((weak))
@@ -130,6 +161,9 @@ bool process_record_quantum(keyrecord_t *record) {
   #ifndef DISABLE_CHORDING
     process_chording(keycode, record) &&
   #endif
+  #ifdef COMBO_ENABLE
+    process_combo(keycode, record) &&
+  #endif
   #ifdef UNICODE_ENABLE
     process_unicode(keycode, record) &&
   #endif
@@ -212,6 +246,36 @@ bool process_record_quantum(keyrecord_t *record) {
 	  return false;
       break;
 	#endif
+    #ifdef PROTOCOL_LUFA
+    case OUT_AUTO:
+      if (record->event.pressed) {
+        set_output(OUTPUT_AUTO);
+      }
+      return false;
+      break;
+    case OUT_USB:
+      if (record->event.pressed) {
+        set_output(OUTPUT_USB);
+      }
+      return false;
+      break;
+    #ifdef BLUETOOTH_ENABLE
+    case OUT_BT:
+      if (record->event.pressed) {
+        set_output(OUTPUT_BLUETOOTH);
+      }
+      return false;
+      break;
+    #endif
+    #ifdef ADAFRUIT_BLE_ENABLE
+    case OUT_BLE:
+      if (record->event.pressed) {
+        set_output(OUTPUT_ADAFRUIT_BLE);
+      }
+      return false;
+      break;
+    #endif
+    #endif
     case MAGIC_SWAP_CONTROL_CAPSLOCK ... MAGIC_TOGGLE_NKRO:
       if (record->event.pressed) {
         // MAGIC actions (BOOTMAGIC without the boot)
@@ -508,6 +572,11 @@ void matrix_scan_quantum() {
   #ifdef TAP_DANCE_ENABLE
     matrix_scan_tap_dance();
   #endif
+
+  #ifdef COMBO_ENABLE
+    matrix_scan_combo();
+  #endif
+
   matrix_scan_kb();
 }
 
@@ -525,34 +594,45 @@ static const uint8_t backlight_pin = BACKLIGHT_PIN;
 #  define COM1x1 COM1A1
 #  define OCR1x  OCR1A
 #else
-#  error "Backlight pin not supported - use B5, B6, or B7"
+#  define NO_BACKLIGHT_CLOCK
+#endif
+
+#ifndef BACKLIGHT_ON_STATE
+#define BACKLIGHT_ON_STATE 0
 #endif
 
 __attribute__ ((weak))
 void backlight_init_ports(void)
 {
 
-  // Setup backlight pin as output and output low.
+  // Setup backlight pin as output and output to on state.
   // DDRx |= n
   _SFR_IO8((backlight_pin >> 4) + 1) |= _BV(backlight_pin & 0xF);
-  // PORTx &= ~n
-  _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
+  #if BACKLIGHT_ON_STATE == 0
+    // PORTx &= ~n
+    _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
+  #else
+    // PORTx |= n
+    _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
+  #endif
 
-  // Use full 16-bit resolution.
-  ICR1 = 0xFFFF;
+  #ifndef NO_BACKLIGHT_CLOCK
+    // Use full 16-bit resolution.
+    ICR1 = 0xFFFF;
 
-  // I could write a wall of text here to explain... but TL;DW
-  // Go read the ATmega32u4 datasheet.
-  // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
+    // I could write a wall of text here to explain... but TL;DW
+    // Go read the ATmega32u4 datasheet.
+    // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on
 
-  // Pin PB7 = OCR1C (Timer 1, Channel C)
-  // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
-  // (i.e. start high, go low when counter matches.)
-  // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
-  // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
+    // Pin PB7 = OCR1C (Timer 1, Channel C)
+    // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0
+    // (i.e. start high, go low when counter matches.)
+    // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0
+    // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1
 
-  TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
-  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
+    TCCR1A = _BV(COM1x1) | _BV(WGM11); // = 0b00001010;
+    TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;
+  #endif
 
   backlight_init();
   #ifdef BACKLIGHT_BREATHING
@@ -564,24 +644,43 @@ __attribute__ ((weak))
 void backlight_set(uint8_t level)
 {
   // Prevent backlight blink on lowest level
-  // PORTx &= ~n
-  _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
+  #if BACKLIGHT_ON_STATE == 0
+    // PORTx &= ~n
+    _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
+  #else
+    // PORTx |= n
+    _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
+  #endif
 
   if ( level == 0 ) {
-    // Turn off PWM control on backlight pin, revert to output low.
-    TCCR1A &= ~(_BV(COM1x1));
-    OCR1x = 0x0;
-  } else if ( level == BACKLIGHT_LEVELS ) {
-    // Turn on PWM control of backlight pin
-    TCCR1A |= _BV(COM1x1);
-    // Set the brightness
-    OCR1x = 0xFFFF;
-  } else {
-    // Turn on PWM control of backlight pin
-    TCCR1A |= _BV(COM1x1);
-    // Set the brightness
-    OCR1x = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2));
-  }
+    #ifndef NO_BACKLIGHT_CLOCK
+      // Turn off PWM control on backlight pin, revert to output low.
+      TCCR1A &= ~(_BV(COM1x1));
+      OCR1x = 0x0;
+    #else
+      #if BACKLIGHT_ON_STATE == 0
+        // PORTx |= n
+        _SFR_IO8((backlight_pin >> 4) + 2) |= _BV(backlight_pin & 0xF);
+      #else
+        // PORTx &= ~n
+        _SFR_IO8((backlight_pin >> 4) + 2) &= ~_BV(backlight_pin & 0xF);
+      #endif
+    #endif
+  } 
+  #ifndef NO_BACKLIGHT_CLOCK
+    else if ( level == BACKLIGHT_LEVELS ) {
+      // Turn on PWM control of backlight pin
+      TCCR1A |= _BV(COM1x1);
+      // Set the brightness
+      OCR1x = 0xFFFF;
+    } 
+    else {
+      // Turn on PWM control of backlight pin
+      TCCR1A |= _BV(COM1x1);
+      // Set the brightness
+      OCR1x = 0xFFFF >> ((BACKLIGHT_LEVELS - level) * ((BACKLIGHT_LEVELS + 1) / 2));
+    }
+  #endif
 
   #ifdef BACKLIGHT_BREATHING
     breathing_intensity_default();
diff --git a/quantum/quantum.h b/quantum/quantum.h
index e6adf974ab..18f072189d 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -15,7 +15,6 @@
 #ifdef RGBLIGHT_ENABLE
   #include "rgblight.h"
 #endif
-
 #include "action_layer.h"
 #include "eeconfig.h"
 #include <stddef.h>
@@ -63,6 +62,10 @@ extern uint32_t default_layer_state;
 	#include "process_printer.h"
 #endif
 
+#ifdef COMBO_ENABLE
+	#include "process_combo.h"
+#endif
+
 #define SEND_STRING(str) send_string(PSTR(str))
 void send_string(const char *str);
 
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index 4853655f95..ab2e790269 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -39,14 +39,14 @@ enum quantum_keycodes {
     QK_CHORDING           = 0x5600,
     QK_CHORDING_MAX       = 0x56FF,
 #endif
-    QK_MOD_TAP            = 0x6000,
-    QK_MOD_TAP_MAX        = 0x6FFF,
-    QK_TAP_DANCE          = 0x7100,
-    QK_TAP_DANCE_MAX      = 0x71FF,
+    QK_TAP_DANCE          = 0x5700,
+    QK_TAP_DANCE_MAX      = 0x57FF,
 #ifdef UNICODEMAP_ENABLE
-    QK_UNICODE_MAP        = 0x7800,
-    QK_UNICODE_MAP_MAX    = 0x7FFF,
+    QK_UNICODE_MAP        = 0x5800,
+    QK_UNICODE_MAP_MAX    = 0x5BFF,
 #endif
+    QK_MOD_TAP            = 0x6000,
+    QK_MOD_TAP_MAX        = 0x7FFF,
 #ifdef UNICODE_ENABLE
     QK_UNICODE            = 0x8000,
     QK_UNICODE_MAX        = 0xFFFF,
@@ -54,7 +54,7 @@ enum quantum_keycodes {
 
     // Loose keycodes - to be used directly
 
-    RESET = 0x7000,
+    RESET = 0x5C00,
     DEBUG,
     MAGIC_SWAP_CONTROL_CAPSLOCK,
     MAGIC_CAPSLOCK_TO_CONTROL,
@@ -141,6 +141,16 @@ enum quantum_keycodes {
     PRINT_ON,
     PRINT_OFF,
 
+    // output selection
+    OUT_AUTO,
+    OUT_USB,
+#ifdef BLUETOOTH_ENABLE
+    OUT_BT,
+#endif
+#ifdef ADAFRUIT_BLE_ENABLE
+    OUT_BLE,
+#endif
+
     // always leave at the end
     SAFE_RANGE
 };
@@ -246,8 +256,10 @@ enum quantum_keycodes {
 
 #define M(kc) (kc | QK_MACRO)
 
+#define MACROTAP(kc) (kc | QK_MACRO | FUNC_TAP<<8)
 #define MACRODOWN(...) (record->event.pressed ? MACRO(__VA_ARGS__) : MACRO_NONE)
 
+
 // L-ayer, T-ap - 256 keycode max, 16 layer max
 #define LT(layer, kc) (kc | QK_LAYER_TAP | ((layer & 0xF) << 8))
 
@@ -286,14 +298,29 @@ enum quantum_keycodes {
 #define OSM(mod) (mod | QK_ONE_SHOT_MOD)
 
 // M-od, T-ap - 256 keycode max
-#define MT(mod, kc) (kc | QK_MOD_TAP | ((mod & 0xF) << 8))
+#define MT(mod, kc) (kc | QK_MOD_TAP | ((mod & 0x1F) << 8))
+
 #define CTL_T(kc) MT(MOD_LCTL, kc)
+#define LCTL_T(kc) MT(MOD_LCTL, kc)
+#define RCTL_T(kc) MT(MOD_RCTL, kc)
+
 #define SFT_T(kc) MT(MOD_LSFT, kc)
+#define LSFT_T(kc) MT(MOD_LSFT, kc)
+#define RSFT_T(kc) MT(MOD_RSFT, kc)
+
 #define ALT_T(kc) MT(MOD_LALT, kc)
+#define LALT_T(kc) MT(MOD_LALT, kc)
+#define RALT_T(kc) MT(MOD_RALT, kc)
+#define ALGR_T(kc) MT(MOD_RALT, kc) // dual-function AltGR
+
 #define GUI_T(kc) MT(MOD_LGUI, kc)
+#define LGUI_T(kc) MT(MOD_LGUI, kc)
+#define RGUI_T(kc) MT(MOD_RGUI, kc)
+
 #define C_S_T(kc) MT((MOD_LCTL | MOD_LSFT), kc) // Control + Shift e.g. for gnome-terminal
 #define MEH_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT), kc) // Meh is a less hyper version of the Hyper key -- doesn't include Win or Cmd, so just alt+shift+ctrl
 #define LCAG_T(kc) MT((MOD_LCTL | MOD_LALT | MOD_LGUI), kc) // Left control alt and gui
+#define RCAG_T(kc) MT((MOD_RCTL | MOD_RALT | MOD_RGUI), kc) // Right control alt and gui
 #define ALL_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI), kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/
 #define SCMD_T(kc) MT((MOD_LGUI | MOD_LSFT), kc)
 #define SWIN_T(kc) SCMD_T(kc)
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index 52a09817a1..dd1b91c63c 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -66,6 +66,8 @@ __attribute__ ((weak))
 const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
 __attribute__ ((weak))
 const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {100, 50, 20};
+__attribute__ ((weak))
+const uint16_t RGBLED_GRADIENT_RANGES[] PROGMEM = {360, 240, 180, 120, 90};
 
 rgblight_config_t rgblight_config;
 rgblight_config_t inmem_config;
@@ -219,6 +221,14 @@ void rgblight_step(void) {
   }
   rgblight_mode(mode);
 }
+void rgblight_step_reverse(void) {
+  uint8_t mode = 0;
+  mode = rgblight_config.mode - 1;
+  if (mode < 1) {
+    mode = RGBLIGHT_MODES;
+  }
+  rgblight_mode(mode);
+}
 
 void rgblight_mode(uint8_t mode) {
   if (!rgblight_config.enable) {
@@ -237,7 +247,7 @@ void rgblight_mode(uint8_t mode) {
     #ifdef RGBLIGHT_ANIMATIONS
       rgblight_timer_disable();
     #endif
-  } else if (rgblight_config.mode >= 2 && rgblight_config.mode <= 23) {
+  } else if (rgblight_config.mode >= 2 && rgblight_config.mode <= 24) {
     // MODE 2-5, breathing
     // MODE 6-8, rainbow mood
     // MODE 9-14, rainbow swirl
@@ -247,6 +257,12 @@ void rgblight_mode(uint8_t mode) {
     #ifdef RGBLIGHT_ANIMATIONS
       rgblight_timer_enable();
     #endif
+  } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
+    // MODE 25-34, static gradient
+
+    #ifdef RGBLIGHT_ANIMATIONS
+      rgblight_timer_disable();
+    #endif
   }
   rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
 }
@@ -350,6 +366,17 @@ void rgblight_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {
       } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 14) {
         // rainbow mood and rainbow swirl, ignore the change of hue
         hue = rgblight_config.hue;
+      } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
+        // static gradient
+        uint16_t _hue;
+        int8_t direction = ((rgblight_config.mode - 25) % 2) ? -1 : 1;
+        uint16_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[(rgblight_config.mode - 25) / 2]);
+        for (uint8_t i = 0; i < RGBLED_NUM; i++) {
+          _hue = (range / RGBLED_NUM * i * direction + hue + 360) % 360;
+          dprintf("rgblight rainbow set hsv: %u,%u,%d,%u\n", i, _hue, direction, range);
+          sethsv(_hue, sat, val, (LED_TYPE *)&led[i]);
+        }
+        rgblight_set();
       }
     }
     rgblight_config.hue = hue;
@@ -450,7 +477,7 @@ void rgblight_task(void) {
     } else if (rgblight_config.mode >= 21 && rgblight_config.mode <= 23) {
       // mode = 21 to 23, knight mode
       rgblight_effect_knight(rgblight_config.mode - 21);
-    } else {
+    } else if (rgblight_config.mode == 24) {
       // mode = 24, christmas mode
       rgblight_effect_christmas();
     }
@@ -604,13 +631,13 @@ void rgblight_effect_christmas(void) {
   static uint16_t last_timer = 0;
   uint16_t hue;
   uint8_t i;
-  if (timer_elapsed(last_timer) < 1000) {
+  if (timer_elapsed(last_timer) < RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL) {
     return;
   }
   last_timer = timer_read();
   current_offset = (current_offset + 1) % 2;
   for (i = 0; i < RGBLED_NUM; i++) {
-    hue = 0 + ((RGBLED_NUM * (i + current_offset)) % 2) * 80;
+    hue = 0 + ((i/RGBLIGHT_EFFECT_CHRISTMAS_STEP + current_offset) % 2) * 120;
     sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
   }
   rgblight_set();
diff --git a/quantum/rgblight.h b/quantum/rgblight.h
index 726b8de72e..2b3e791bf8 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight.h
@@ -2,7 +2,7 @@
 #define RGBLIGHT_H
 
 #ifdef RGBLIGHT_ANIMATIONS
-	#define RGBLIGHT_MODES 24
+	#define RGBLIGHT_MODES 34
 #else
 	#define RGBLIGHT_MODES 1
 #endif
@@ -22,6 +22,14 @@
 #define RGBLIGHT_EFFECT_DUALKNIGHT_LENGTH 4
 #endif
 
+#ifndef RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL
+#define RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL 1000
+#endif
+
+#ifndef RGBLIGHT_EFFECT_CHRISTMAS_STEP
+#define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2
+#endif
+
 #ifndef RGBLIGHT_HUE_STEP
 #define RGBLIGHT_HUE_STEP 10
 #endif
@@ -65,6 +73,7 @@ void rgblight_decrease(void);
 void rgblight_toggle(void);
 void rgblight_enable(void);
 void rgblight_step(void);
+void rgblight_step_reverse(void);
 void rgblight_mode(uint8_t mode);
 void rgblight_set(void);
 void rgblight_update_dword(uint32_t dword);
diff --git a/quantum/template/config.h b/quantum/template/config.h
index b02f0c7ebc..c61c4a6181 100644
--- a/quantum/template/config.h
+++ b/quantum/template/config.h
@@ -46,7 +46,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define MATRIX_COL_PINS { F1, F0, B0 }
 #define UNUSED_PINS
 
-/* COL2ROW or ROW2COL */
+/* COL2ROW, ROW2COL, or CUSTOM_MATRIX */
 #define DIODE_DIRECTION COL2ROW
  
 // #define BACKLIGHT_PIN B7
diff --git a/quantum/visualizer/visualizer.c b/quantum/visualizer/visualizer.c
index 54f6faaa42..5826d909e4 100644
--- a/quantum/visualizer/visualizer.c
+++ b/quantum/visualizer/visualizer.c
@@ -53,10 +53,13 @@ SOFTWARE.
 #define "Visualizer thread priority not defined"
 #endif
 
+// mods status
+#include "action_util.h"
 
 static visualizer_keyboard_status_t current_status = {
     .layer = 0xFFFFFFFF,
     .default_layer = 0xFFFFFFFF,
+    .mods = 0xFF,
     .leds = 0xFFFFFFFF,
     .suspended = false,
 };
@@ -64,6 +67,7 @@ static visualizer_keyboard_status_t current_status = {
 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
     return status1->layer == status2->layer &&
         status1->default_layer == status2->default_layer &&
+        status1->mods == status2->mods &&
         status1->leds == status2->leds &&
         status1->suspended == status2->suspended;
 }
@@ -307,6 +311,45 @@ bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_s
     gdispFlush();
     return false;
 }
+
+static void format_mods_bitmap_string(uint8_t mods, char* buffer) {
+    *buffer = ' ';
+    ++buffer;
+
+    for (int i = 0; i<8; i++)
+    {
+        uint32_t mask = (1u << i);
+        if (mods & mask) {
+            *buffer = '1';
+        } else {
+            *buffer = '0';
+        }
+        ++buffer;
+
+        if (i==3) {
+            *buffer = ' ';
+            ++buffer;
+        }
+    }
+    *buffer = 0;
+}
+
+bool keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
+    (void)animation;
+
+    const char* title = "Modifier states";
+    const char* mods_header = " CSAG CSAG ";
+    char status_buffer[12]; 
+    
+    gdispClear(White);
+    gdispDrawString(0, 0, title, state->font_fixed5x8, Black);
+    gdispDrawString(0, 10, mods_header, state->font_fixed5x8, Black);
+    format_mods_bitmap_string(state->status.mods, status_buffer);
+    gdispDrawString(0, 20, status_buffer, state->font_fixed5x8, Black);
+
+    gdispFlush();
+    return false;
+}
 #endif // LCD_ENABLE
 
 bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
@@ -350,6 +393,7 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
     visualizer_keyboard_status_t initial_status = {
         .default_layer = 0xFFFFFFFF,
         .layer = 0xFFFFFFFF,
+        .mods = 0xFF,
         .leds = 0xFFFFFFFF,
         .suspended = false,
     };
@@ -499,7 +543,18 @@ void update_status(bool changed) {
 #endif
 }
 
-void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
+uint8_t visualizer_get_mods() {
+  uint8_t mods = get_mods();
+
+#ifndef NO_ACTION_ONESHOT
+  if (!has_oneshot_mods_timed_out()) {
+    mods |= get_oneshot_mods();
+  }
+#endif  
+  return mods;
+}
+
+void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
     // Note that there's a small race condition here, the thread could read
     // a state where one of these are set but not the other. But this should
     // not really matter as it will be fixed during the next loop step.
@@ -523,6 +578,7 @@ void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
         visualizer_keyboard_status_t new_status = {
             .layer = state,
             .default_layer = default_state,
+            .mods = mods,
             .leds = leds,
             .suspended = current_status.suspended,
         };
diff --git a/quantum/visualizer/visualizer.h b/quantum/visualizer/visualizer.h
index 53e250725c..315af50228 100644
--- a/quantum/visualizer/visualizer.h
+++ b/quantum/visualizer/visualizer.h
@@ -34,10 +34,14 @@ SOFTWARE.
 #include "lcd_backlight.h"
 #endif
 
+// use this function to merget both real_mods and oneshot_mods in a uint16_t
+uint8_t visualizer_get_mods(void);
+
 // This need to be called once at the start
 void visualizer_init(void);
 // This should be called at every matrix scan
-void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds);
+void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds);
+
 // This should be called when the keyboard goes to suspend state
 void visualizer_suspend(void);
 // This should be called when the keyboard wakes up from suspend state
@@ -61,6 +65,7 @@ struct keyframe_animation_t;
 typedef struct {
     uint32_t layer;
     uint32_t default_layer;
+    uint8_t mods;
     uint32_t leds; // See led.h for available statuses
     bool suspended;
 } visualizer_keyboard_status_t;
@@ -129,6 +134,8 @@ bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_st
 bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state);
 // Displays a bitmap (0/1) of all the currently active layers
 bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state);
+// Displays a bitmap (0/1) of all the currently active mods
+bool keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state);
 
 bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state);
 bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state);