~ruther/qmk_firmware

b7cf9a888a607dc024d74ee72c0ebbd8e0ddfdbe — Drashna Jaelre 4 years ago bbe4518
Drashna's split updates (#13350)

Co-authored-by: Ryan <fauxpark@gmail.com>
53 files changed, 1883 insertions(+), 1813 deletions(-)

D keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/rules.mk
D keyboards/handwired/dactyl_manuform/5x6_right_trackball/pointer_transport.c
M keyboards/handwired/dactyl_manuform/dactyl_manuform.h
A keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.c
A keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.h
A keyboards/handwired/tractyl_manuform/4x6_right/config.h
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/4x6_right}/info.json
A keyboards/handwired/tractyl_manuform/4x6_right/keymaps/default/keymap.c
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/4x6_right}/keymaps/drashna/config.h
A keyboards/handwired/tractyl_manuform/4x6_right/keymaps/drashna/keymap.c
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/4x6_right}/keymaps/drashna/rules.mk
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/4x6_right}/readme.md
A keyboards/handwired/tractyl_manuform/4x6_right/rules.mk
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball/post_config.h => tractyl_manuform/5x6_right/5x6_right.c}
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball/5x6_right_trackball => tractyl_manuform/5x6_right/5x6_right}.h
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/5x6_right}/config.h
A keyboards/handwired/tractyl_manuform/5x6_right/info.json
A keyboards/handwired/tractyl_manuform/5x6_right/keymaps/default/keymap.c
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball/keymaps/default/keymap.c => tractyl_manuform/5x6_right/keymaps/drashna/config.h}
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/5x6_right}/keymaps/drashna/keymap.c
A keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk
A keyboards/handwired/tractyl_manuform/5x6_right/readme.md
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball => tractyl_manuform/5x6_right}/rules.mk
A keyboards/handwired/tractyl_manuform/config.h
A keyboards/handwired/tractyl_manuform/post_config.h
A keyboards/handwired/tractyl_manuform/readme.md
A keyboards/handwired/tractyl_manuform/tm_sync.c
R keyboards/handwired/{dactyl_manuform/5x6_right_trackball/5x6_right_trackball => tractyl_manuform/tractyl_manuform}.c
A keyboards/handwired/tractyl_manuform/tractyl_manuform.h
M keyboards/kyria/keymaps/drashna/keymap.c
M keyboards/kyria/keymaps/drashna/rules.mk
M layouts/community/ergodox/drashna/keymap.c
R layouts/community/ergodox/drashna/{visualizer.c => visualizer.c_old}
M layouts/community/split_3x6_3/drashna/config.h
M layouts/community/split_3x6_3/drashna/keymap.c
M users/drashna/config.h
M users/drashna/drashna.c
M users/drashna/drashna.h
M users/drashna/drashna_font.h
D users/drashna/drashna_transport.c
M users/drashna/oled_stuff.c
M users/drashna/oled_stuff.h
M users/drashna/process_records.c
M users/drashna/process_records.h
M users/drashna/rgb_matrix_stuff.c
M users/drashna/rgb_stuff.c
M users/drashna/rules.mk
M users/drashna/tap_dances.c
M users/drashna/tap_dances.h
M users/drashna/template.c
M users/drashna/template.h
A users/drashna/transport_sync.c
R {keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/config => users/drashna/transport_sync}.h
D keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/rules.mk => keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/rules.mk +0 -4
@@ 1,4 0,0 @@
USER_NAME := drashna
SRC += ../drashna/keymap.c

include $(KEYBOARD_PATH_1)/keymaps/drashna/rules.mk

D keyboards/handwired/dactyl_manuform/5x6_right_trackball/pointer_transport.c => keyboards/handwired/dactyl_manuform/5x6_right_trackball/pointer_transport.c +0 -616
@@ 1,616 0,0 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 <string.h>
#include <stddef.h>

#include "matrix.h"
#include QMK_KEYBOARD_H

#define ROWS_PER_HAND (MATRIX_ROWS / 2)
#define SYNC_TIMER_OFFSET 2

#ifdef RGBLIGHT_ENABLE
#    include "rgblight.h"
#endif

#ifdef BACKLIGHT_ENABLE
#    include "backlight.h"
#endif

#ifdef ENCODER_ENABLE
#    include "encoder.h"
static pin_t encoders_pad[] = ENCODERS_PAD_A;
#    define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
#endif

#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
#    include "led_matrix.h"
#endif
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
#    include "rgb_matrix.h"
#endif

#ifdef POINTING_DEVICE_ENABLE
static uint16_t device_cpi    = 0;
static int8_t   split_mouse_x = 0, split_mouse_y = 0;
#endif

#ifdef OLED_DRIVER_ENABLE
#    include "oled_driver.h"
#endif

#if defined(USE_I2C)

#    include "i2c_master.h"
#    include "i2c_slave.h"

typedef struct _I2C_slave_buffer_t {
#    ifndef DISABLE_SYNC_TIMER
    uint32_t sync_timer;
#    endif
#    ifdef SPLIT_TRANSPORT_MIRROR
    matrix_row_t mmatrix[ROWS_PER_HAND];
#    endif
    matrix_row_t smatrix[ROWS_PER_HAND];
#    ifdef SPLIT_MODS_ENABLE
    uint8_t real_mods;
    uint8_t weak_mods;
#        ifndef NO_ACTION_ONESHOT
    uint8_t oneshot_mods;
#        endif
#    endif
#    ifdef BACKLIGHT_ENABLE
    uint8_t backlight_level;
#    endif
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    rgblight_syncinfo_t rgblight_sync;
#    endif
#    ifdef ENCODER_ENABLE
    uint8_t encoder_state[NUMBER_OF_ENCODERS];
#    endif
#    ifdef WPM_ENABLE
    uint8_t current_wpm;
#    endif
    int8_t        mouse_x;
    int8_t        mouse_y;
    uint16_t      device_cpi;
    bool          oled_on;
    layer_state_t t_layer_state;
    layer_state_t t_default_layer_state;
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_eeconfig_t led_matrix;
    bool           led_suspend_state;
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_config_t rgb_matrix;
    bool         rgb_suspend_state;
#    endif
} __attribute__((packed)) I2C_slave_buffer_t;

static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;

#    define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
#    define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
#    define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
#    define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
#    define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer)
#    define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
#    define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
#    define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
#    define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
#    define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
#    define I2C_MOUSE_X_START offsetof(I2C_slave_buffer_t, mouse_x)
#    define I2C_MOUSE_Y_START offsetof(I2C_slave_buffer_t, mouse_y)
#    define I2C_MOUSE_DPI_START offsetof(I2C_slave_buffer_t, device_cpi)
#    define I2C_OLED_ON_START offsetof(I2C_slave_buffer_t, oled_on)
#    define I2C_LAYER_STATE_START offsetof(I2C_slave_buffer_t, t_layer_state)
#    define I2C_DEFAULT_LAYER_STATE_START offsetof(I2C_slave_buffer_t, t_default_layer_state)
#    define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
#    define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
#    define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
#    define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)

#    define TIMEOUT 100

#    ifndef SLAVE_I2C_ADDRESS
#        define SLAVE_I2C_ADDRESS 0x32
#    endif

// Get rows from other half over i2c
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
#    ifdef SPLIT_TRANSPORT_MIRROR
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
#    endif
    // write backlight info
#    ifdef BACKLIGHT_ENABLE
    uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
    if (level != i2c_buffer->backlight_level) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
            i2c_buffer->backlight_level = level;
        }
    }
#    endif

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    if (rgblight_get_change_flags()) {
        rgblight_syncinfo_t rgblight_sync;
        rgblight_get_syncinfo(&rgblight_sync);
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
            rgblight_clear_change_flags();
        }
    }
#    endif

#    ifdef ENCODER_ENABLE
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT);
    encoder_update_raw(i2c_buffer->encoder_state);
#    endif

#    ifdef WPM_ENABLE
    uint8_t current_wpm = get_current_wpm();
    if (current_wpm != i2c_buffer->current_wpm) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) {
            i2c_buffer->current_wpm = current_wpm;
        }
    }
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_left()) {
        report_mouse_t temp_report = pointing_device_get_report();
        i2c_readReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_X_START, (void *)&i2c_buffer->mouse_x, sizeof(i2c_buffer->mouse_x), TIMEOUT);
        temp_report.x = i2c_buffer->mouse_x;
        i2c_readReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_Y_START, (void *)&i2c_buffer->mouse_y, sizeof(i2c_buffer->mouse_y), TIMEOUT);
        temp_report.y = i2c_buffer->mouse_y;
        pointing_device_set_report(temp_report);

        if (device_cpi != i2c_buffer->device_cpi) {
            if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_DPI_START, (void *)&device_cpi, sizeof(device_cpi), TIMEOUT) >= 0) {
                i2c_buffer->device_cpi = device_cpi
            }
        }
    }
#    endif

#    ifdef SPLIT_MODS_ENABLE
    uint8_t real_mods = get_mods();
    if (real_mods != i2c_buffer->real_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) {
            i2c_buffer->real_mods = real_mods;
        }
    }

    uint8_t weak_mods = get_weak_mods();
    if (weak_mods != i2c_buffer->weak_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) {
            i2c_buffer->weak_mods = weak_mods;
        }
    }

#        ifndef NO_ACTION_ONESHOT
    uint8_t oneshot_mods = get_oneshot_mods();
    if (oneshot_mods != i2c_buffer->oneshot_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) {
            i2c_buffer->oneshot_mods = oneshot_mods;
        }
    }
#        endif
#    endif

    if (layer_state != i2c_buffer->t_layer_state) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LAYER_STATE_START, (void *)&layer_state, sizeof(layer_state), TIMEOUT) >= 0) {
            i2c_buffer->t_layer_state = layer_state;
        }
    }

    if (default_layer_state != i2c_buffer->t_default_layer_state) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_DEFAULT_LAYER_STATE_START, (void *)&default_layer_state, sizeof(default_layer_state), TIMEOUT) >= 0) {
            i2c_buffer->t_default_layer_state = default_layer_state;
        }
    }

#    ifdef OLED_DRIVER_ENABLE
    bool is_oled_on = is_oled_on();
    if (is_oled_on != i2c_buffer->oled_on) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LAYER_STATE_START, (void *)&is_oled_on, sizeof(is_oled_on), TIMEOUT) >= 0) {
            i2c_buffer->oled_on = is_oled_on;
        }
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
    bool suspend_state = led_matrix_get_suspend_state();
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
    bool suspend_state = rgb_matrix_get_suspend_state();
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
#    endif

#    ifndef DISABLE_SYNC_TIMER
    i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
#    endif

    return true;
}

void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
#    ifndef DISABLE_SYNC_TIMER
    sync_timer_update(i2c_buffer->sync_timer);
#    endif
    // Copy matrix to I2C buffer
    memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
#    ifdef SPLIT_TRANSPORT_MIRROR
    memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
#    endif

// Read Backlight Info
#    ifdef BACKLIGHT_ENABLE
    backlight_set(i2c_buffer->backlight_level);
#    endif

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    // Update the RGB with the new data
    if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
        rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
        i2c_buffer->rgblight_sync.status.change_flags = 0;
    }
#    endif

#    ifdef ENCODER_ENABLE
    encoder_state_raw(i2c_buffer->encoder_state);
#    endif

#    ifdef WPM_ENABLE
    set_current_wpm(i2c_buffer->current_wpm);
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (!is_keyboard_left()) {
        static uint16_t cpi;
        if (cpi != i2c_buffer->device_cpi) {
            cpi = i2c_buffer->device_cpi;
            pmw_set_cpi(cpi);
        }
        i2c_buffer->mouse_x = split_mouse_x;
        i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_X_START, (void *)&i2c_buffer->mouse_x, sizeof(i2c_buffer->mouse_x), TIMEOUT);
        i2c_buffer->mouse_y = split_mouse_y;
        i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_Y_START, (void *)&i2c_buffer->mouse_y, sizeof(i2c_buffer->mouse_y), TIMEOUT);
    }

#    endif

#    ifdef SPLIT_MODS_ENABLE
    set_mods(i2c_buffer->real_mods);
    set_weak_mods(i2c_buffer->weak_mods);
#        ifndef NO_ACTION_ONESHOT
    set_oneshot_mods(i2c_buffer->oneshot_mods);
#        endif
#    endif

    if (layer_state != i2c_buffer->t_layer_state) {
        layer_state = i2c_buffer->t_layer_state;
    }
    if (default_layer_state != i2c_buffer->t_default_layer_state) {
        default_layer_state = i2c_buffer->t_default_layer_state;
    }

#    ifdef OLED_DRIVER_ENABLE
    if (i2c_buffer->oled_on) {
        oled_on();
    } else {
        oled_off();
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
    led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
    rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
#    endif
}

void transport_master_init(void) { i2c_init(); }

void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }

#else  // USE_SERIAL

#    include "serial.h"

typedef struct _Serial_s2m_buffer_t {
    // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
    matrix_row_t smatrix[ROWS_PER_HAND];
#    ifdef ENCODER_ENABLE
    uint8_t      encoder_state[NUMBER_OF_ENCODERS];
#    endif
    int8_t       mouse_x;
    int8_t       mouse_y;
} __attribute__((packed)) Serial_s2m_buffer_t;

typedef struct _Serial_m2s_buffer_t {
#    ifdef SPLIT_MODS_ENABLE
    uint8_t       real_mods;
    uint8_t       weak_mods;
#        ifndef NO_ACTION_ONESHOT
    uint8_t       oneshot_mods;
#        endif
#    endif
#    ifndef DISABLE_SYNC_TIMER
    uint32_t     sync_timer;
#    endif
#    ifdef SPLIT_TRANSPORT_MIRROR
    matrix_row_t mmatrix[ROWS_PER_HAND];
#    endif
#    ifdef BACKLIGHT_ENABLE
    uint8_t       backlight_level;
#    endif
#    ifdef WPM_ENABLE
    uint8_t       current_wpm;
#    endif
    uint16_t      device_cpi;
    bool          oled_on;
    layer_state_t t_layer_state;
    layer_state_t t_default_layer_state;
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_eeconfig_t led_matrix;
    bool           led_suspend_state;
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_config_t   rgb_matrix;
    bool           rgb_suspend_state;
#    endif
} __attribute__((packed)) Serial_m2s_buffer_t;

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
// When MCUs on both sides drive their respective RGB LED chains,
// it is necessary to synchronize, so it is necessary to communicate RGB
// information. In that case, define RGBLIGHT_SPLIT with info on the number
// of LEDs on each half.
//
// Otherwise, if the master side MCU drives both sides RGB LED chains,
// there is no need to communicate.

typedef struct _Serial_rgblight_t {
    rgblight_syncinfo_t rgblight_sync;
} Serial_rgblight_t;

volatile Serial_rgblight_t serial_rgblight = {};
uint8_t volatile status_rgblight           = 0;
#    endif

volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
uint8_t volatile status0                       = 0;

enum serial_transaction_id {
    GET_SLAVE_MATRIX = 0,
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    PUT_RGBLIGHT,
#    endif
};

SSTD_t transactions[] = {
    [GET_SLAVE_MATRIX] =
        {
            (uint8_t *)&status0,
            sizeof(serial_m2s_buffer),
            (uint8_t *)&serial_m2s_buffer,
            sizeof(serial_s2m_buffer),
            (uint8_t *)&serial_s2m_buffer,
        },
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    [PUT_RGBLIGHT] =
        {
            (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL  // no slave to master transfer
        },
#    endif
};

void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }

void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)

// rgblight synchronization information communication.

void transport_rgblight_master(void) {
    if (rgblight_get_change_flags()) {
        rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
        if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
            rgblight_clear_change_flags();
        }
    }
}

void transport_rgblight_slave(void) {
    if (status_rgblight == TRANSACTION_ACCEPTED) {
        rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
        status_rgblight = TRANSACTION_END;
    }
}

#    else
#        define transport_rgblight_master()
#        define transport_rgblight_slave()
#    endif

bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
#    ifndef SERIAL_USE_MULTI_TRANSACTION
    if (soft_serial_transaction() != TRANSACTION_END) {
        return false;
    }
#    else
    transport_rgblight_master();
    if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
        return false;
    }
#    endif

    // TODO:  if MATRIX_COLS > 8 change to unpack()
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        slave_matrix[i] = serial_s2m_buffer.smatrix[i];
#    ifdef SPLIT_TRANSPORT_MIRROR
        serial_m2s_buffer.mmatrix[i] = master_matrix[i];
#    endif
    }

#    ifdef BACKLIGHT_ENABLE
    // Write backlight level for slave to read
    serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
#    endif

#    ifdef ENCODER_ENABLE
    encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
#    endif

#    ifdef WPM_ENABLE
    // Write wpm to slave
    serial_m2s_buffer.current_wpm  = get_current_wpm();
#    endif

#    ifdef SPLIT_MODS_ENABLE
    serial_m2s_buffer.real_mods    = get_mods();
    serial_m2s_buffer.weak_mods    = get_weak_mods();
#        ifndef NO_ACTION_ONESHOT
    serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
#        endif
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_left()) {
        report_mouse_t temp_report = pointing_device_get_report();
        temp_report.x              = serial_s2m_buffer.mouse_x;
        temp_report.y              = serial_s2m_buffer.mouse_y;
        pointing_device_set_report(temp_report);
        serial_m2s_buffer.device_cpi = device_cpi;
    }
#    endif

    serial_m2s_buffer.t_layer_state           = layer_state;
    serial_m2s_buffer.t_default_layer_state   = default_layer_state;
#    ifdef OLED_DRIVER_ENABLE
    serial_m2s_buffer.oled_on                 = is_oled_on();
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    serial_m2s_buffer.led_matrix        = led_matrix_eeconfig;
    serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    serial_m2s_buffer.rgb_matrix        = rgb_matrix_config;
    serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
#    endif
#    ifndef DISABLE_SYNC_TIMER
    serial_m2s_buffer.sync_timer   = sync_timer_read32() + SYNC_TIMER_OFFSET;
#    endif
    return true;
}

void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
    transport_rgblight_slave();
#    ifndef DISABLE_SYNC_TIMER
    sync_timer_update(serial_m2s_buffer.sync_timer);
#    endif

    // TODO: if MATRIX_COLS > 8 change to pack()
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        serial_s2m_buffer.smatrix[i] = slave_matrix[i];
#    ifdef SPLIT_TRANSPORT_MIRROR
        master_matrix[i]             = serial_m2s_buffer.mmatrix[i];
#    endif
    }

#    ifdef BACKLIGHT_ENABLE
    backlight_set(serial_m2s_buffer.backlight_level);
#    endif

#    ifdef ENCODER_ENABLE
    encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
#    endif

#    ifdef WPM_ENABLE
    set_current_wpm(serial_m2s_buffer.current_wpm);
#    endif

#    ifdef SPLIT_MODS_ENABLE
    set_mods(serial_m2s_buffer.real_mods);
    set_weak_mods(serial_m2s_buffer.weak_mods);
#        ifndef NO_ACTION_ONESHOT
    set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
#        endif
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (!is_keyboard_left()) {
        static uint16_t cpi;
        if (cpi != serial_m2s_buffer.device_cpi) {
            cpi = serial_m2s_buffer.device_cpi;
            pmw_set_cpi(cpi);
        }
        serial_s2m_buffer.mouse_x = split_mouse_x;
        serial_s2m_buffer.mouse_y = split_mouse_y;
    }
#    endif

    if (layer_state != serial_m2s_buffer.t_layer_state) {
        layer_state = serial_m2s_buffer.t_layer_state;
    }
    if (default_layer_state != serial_m2s_buffer.t_default_layer_state) {
        default_layer_state = serial_m2s_buffer.t_default_layer_state;
    }
#    ifdef OLED_DRIVER_ENABLE
    if (serial_m2s_buffer.oled_on) {
        oled_on();
    } else {
        oled_off();
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
    led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
    rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
#    endif
}

#endif

#ifdef POINTING_DEVICE_ENABLE
void master_mouse_send(int8_t x, int8_t y) {
    split_mouse_x = x;
    split_mouse_y = y;
}
void trackball_set_cpi(uint16_t cpi) {
    if (!is_keyboard_left()) {
        pmw_set_cpi(cpi);
    } else {
        device_cpi = cpi;
    }
}
#endif

M keyboards/handwired/dactyl_manuform/dactyl_manuform.h => keyboards/handwired/dactyl_manuform/dactyl_manuform.h +0 -2
@@ 16,8 16,6 @@
    #include "6x6.h"
#elif KEYBOARD_handwired_dactyl_manuform_dmote_62key
    #include "62key.h"
#elif KEYBOARD_handwired_dactyl_manuform_5x6_right_trackball
#    include "5x6_right_trackball.h"
#endif

#include "quantum.h"

A keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.c => keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.c +67 -0
@@ 0,0 1,67 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 "4x6_right.h"

#ifdef RGB_MATRIX_ENABLE
led_config_t g_led_config = { {
    {  24,  23,  18,  17,  10,   9 },
    {  25,  22,  19,  16,  11,   8 },
    {  26,  21,  20,  15,  12,   7 },
    { NO_LED, NO_LED, NO_LED,  14,  13,   6 },
    { NO_LED, NO_LED, NO_LED,  14,  13,   6 },
    {  51,  50,  45,  44,  37,  36 },
    {  52,  49,  46,  43,  38,  35 },
    {  53,  48,  47,  42,  39,  34 },
    { NO_LED, NO_LED, NO_LED,  41,  40,  33 },
    { NO_LED, NO_LED, NO_LED,  41,  40,  33 }
}, {
    {  85,  16 }, {  50,  13 }, {  16,  20 }, {  16,  38 }, {  50,  48 }, {  85,  52 }, {  95,  63 },
    {  85,  39 }, {  85,  21 }, {  85,   4 }, {  68,   2 }, {  68,  19 }, {  68,  37 }, {  80,  58 },
    {  60,  55 }, {  50,  35 }, {  50,  13 }, {  50,   0 }, {  33,   3 }, {  33,  20 }, {  33,  37 },
    {  16,  42 }, {  16,  24 }, {  16,   7 }, {   0,   7 }, {   0,  24 }, {   0,  41 }, { 139,  16 },
    { 174,  13 }, { 208,  20 }, { 208,  38 }, { 174,  48 }, { 139,  52 }, { 129,  63 }, { 139,  39 },
    { 139,  21 }, { 139,   4 }, { 156,   2 }, { 156,  19 }, { 156,  37 }, { 144,  58 }, { 164,  55 },
    { 174,  35 }, { 174,  13 }, { 174,   0 }, { 191,   3 }, { 191,  20 }, { 191,  37 }, { 208,  42 },
    { 208,  24 }, { 208,   7 }, { 224,   7 }, { 224,  24 }, { 224,  41 }
}, {
    2, 2, 2, 2, 2, 2, 1,
    4, 4, 4, 4, 4, 4, 1,
    1, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 1, 1, 1, 2,
    2, 2, 2, 2, 2, 1, 4,
    4, 4, 4, 4, 4, 1, 1,
    4, 4, 4, 4, 4, 4, 4,
    4, 4, 1, 1, 1
} };
#endif

#ifdef SWAP_HANDS_ENABLE
const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {
    /* Left hand, matrix positions */
    {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}},
    {{5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}},
    {{5, 7}, {4, 7}, {3, 7}, {2, 7}, {1, 7}, {0, 7}},
    {{5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}},
    {{5, 9}, {4, 9}, {3, 9}, {2, 9}, {1, 9}, {0, 9}},
    /* Right hand, matrix positions */
    {{5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},
    {{5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},
    {{5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},
    {{5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},
    {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}}
};
#endif

A keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.h => keyboards/handwired/tractyl_manuform/4x6_right/4x6_right.h +47 -0
@@ 0,0 1,47 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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/>.
 */

#pragma once

#include "tractyl_manuform.h"
#include "quantum.h"

#define ___ KC_NO

// clang-format off
#define LAYOUT_4x6_right(\
  L00, L01, L02, L03, L04, L05,                          R00, R01, R02, R03, R04, R05, \
  L10, L11, L12, L13, L14, L15,                          R10, R11, R12, R13, R14, R15, \
  L20, L21, L22, L23, L24, L25,                          R20, R21, R22, R23, R24, R25, \
            L32, L33,                                              R32, R33,           \
                      L34, L35,                               R31,                     \
                                L44, L45,           R41,                               \
                                L42, L43,      R42, R43                                \
  ) \
  { \
    { L00, L01, L02, L03, L04, L05 }, \
    { L10, L11, L12, L13, L14, L15 }, \
    { L20, L21, L22, L23, L24, L25 }, \
    { ___, ___, L32, L33, L34, L35 }, \
    { ___, ___, L42, L43, L44, L45 }, \
                                      \
    { R00, R01, R02, R03, R04, R05 }, \
    { R10, R11, R12, R13, R14, R15 }, \
    { R20, R21, R22, R23, R24, R25 }, \
    { ___, R31, R32, R33, ___, ___ }, \
    { ___, R41, R42, R43, ___, ___ }  \
}
// clang-format on

A keyboards/handwired/tractyl_manuform/4x6_right/config.h => keyboards/handwired/tractyl_manuform/4x6_right/config.h +91 -0
@@ 0,0 1,91 @@
/*
Copyright 2012 Jun Wako <wakojun@gmail.com>
Copyright 2015 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/>.
*/

#pragma once

#include "config_common.h"

#define PRODUCT_ID  0x3537
#define DEVICE_VER  0x0001
#define PRODUCT     Tractyl Manuform(4x6)

/* key matrix size */
// Rows are doubled-up
#define MATRIX_ROWS 10
#define MATRIX_COLS 6

// wiring of each half
#define MATRIX_COL_PINS { C0, C1, C2, C3, C4, C5 }
#define MATRIX_ROW_PINS { A0, A1, A2, A3, A4 }

#define DIODE_DIRECTION COL2ROW

// WS2812 RGB LED strip input and number of LEDs
#define RGB_DI_PIN       E7
#define DRIVER_LED_TOTAL 62
#define RGB_MATRIX_SPLIT { 32, 30 }
#define RGB_DISABLE_WHEN_USB_SUSPENDED true
#define RGB_MATRIX_KEYPRESSES
// #define RGB_MATRIX_KEYRELEASES
#define RGB_MATRIX_FRAMEBUFFER_EFFECTS
#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 80

#define SPLIT_TRANSPORT_MIRROR
#define SPLIT_HAND_PIN A6


/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST

/* number of backlight levels */
// #define BACKLIGHT_LEVELS 3

// #define DEBUG_LED_PIN                  D6

#define ROTATIONAL_TRANSFORM_ANGLE     -25

/* Bootmagic Lite key configuration */
#define BOOTMAGIC_LITE_ROW             0
#define BOOTMAGIC_LITE_COLUMN          0
#define BOOTMAGIC_LITE_ROW_RIGHT       4
#define BOOTMAGIC_LITE_COLUMN_RIGHT    5

#define AUDIO_PIN                      C6
#define AUDIO_PIN_ALT                  B7

#define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095
#define DYNAMIC_KEYMAP_LAYER_COUNT     16
#define LAYER_STATE_16BIT

/* serial.c configuration for split keyboard */
#define SOFT_SERIAL_PIN D3
#define EE_HANDS


/* Set 0 if debouncing isn't needed */
#define DEBOUNCE 5

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
#define NO_ACTION_MACRO
#define NO_ACTION_FUNCTION

#define SERIAL_USE_MULTI_TRANSACTION
#define SPLIT_TRANSACTION_IDS_KB RPC_ID_STATE_SYNC, RPC_ID_SLAVE_STATE

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/info.json => keyboards/handwired/tractyl_manuform/4x6_right/info.json +3 -3
@@ 1,11 1,11 @@
{
    "keyboard_name": "Dactyl Manuform 5x6",
    "keyboard_name": "Tractyl Manuform 4x6",
    "url": "",
    "maintainer": "qmk",
    "maintainer": "drashna",
    "width": 17,
    "height": 8,
    "layouts": {
        "LAYOUT_5x6_right_trackball": {
        "LAYOUT_5x6_right": {
            "layout": [
                {"label":"L00", "x":0, "y":0},
                {"label":"L01", "x":1, "y":0},

A keyboards/handwired/tractyl_manuform/4x6_right/keymaps/default/keymap.c => keyboards/handwired/tractyl_manuform/4x6_right/keymaps/default/keymap.c +62 -0
@@ 0,0 1,62 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 QMK_KEYBOARD_H

enum custom_layers {
    _QWERTY,
    _LOWER,
    _RAISE,
};

#define RAISE MO(_RAISE)
#define LOWER MO(_LOWER)

#define RAISE MO(_RAISE)
#define LOWER MO(_LOWER)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {

[_QWERTY] = LAYOUT_4x6_right(
    KC_ESC,  KC_Q,  KC_W,   KC_E,   KC_R,   KC_T,               KC_Y,   KC_U,   KC_I,   KC_O,   KC_P,   KC_MINS,
    KC_TAB,  KC_A,  KC_S,   KC_D,   KC_F,   KC_G,               KC_H,   KC_J,   KC_K,   KC_L,   KC_SCLN,KC_QUOT,
    KC_LSFT, KC_Z,  KC_X,   KC_C,   KC_V,   KC_B,               KC_N,   KC_M,   KC_COMM,KC_DOT, KC_SLSH,KC_BSLASH,
                    KC_LBRC,KC_RBRC,                                            KC_PLUS, KC_EQL,
                                    RAISE,  KC_SPC,                     LOWER,
                                    KC_TAB, KC_HOME,                    KC_ENT,
                                    KC_BSPC,KC_GRV,             KC_LGUI,KC_DEL
),

[_LOWER] = LAYOUT_4x6_right(
    _______,_______,_______,_______,_______,KC_LBRC,            KC_RBRC, KC_P7, KC_P8,  KC_P9,  RESET,  KC_PLUS,
    _______,KC_HOME,KC_PGUP,KC_PGDN,KC_END ,KC_LPRN,            KC_RPRN, KC_P4, KC_P5,  KC_P6,  KC_MINS,KC_PIPE,
    _______,_______,_______,_______,_______,_______,            _______, KC_P1, KC_P2,  KC_P3,  KC_EQL, KC_UNDS,
                     _______,KC_PSCR,                                          _______, KC_P0,
                                            _______,_______,            _______,
                                            _______,_______,            _______,
                                            _______,_______,    _______,_______
),

[_RAISE] = LAYOUT_4x6_right(
    _______,RESET,  _______,_______,_______,KC_LBRC,            KC_RBRC,_______,KC_NLCK,KC_INS, KC_SLCK,KC_MUTE,
    _______,KC_LEFT,KC_UP  ,KC_DOWN,KC_RGHT,KC_LPRN,            KC_RPRN,KC_MPRV,KC_MPLY,KC_MNXT,_______,KC_VOLU,
    _______,_______,_______,_______,_______,_______,            _______,_______,_______,_______,_______,KC_VOLD,
                    _______,_______,                                            KC_EQL ,_______,
                                            _______,_______,            _______,
                                            _______,_______,            _______,
                                            _______,_______,    _______,_______
),
};

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/drashna/config.h => keyboards/handwired/tractyl_manuform/4x6_right/keymaps/drashna/config.h +2 -5
@@ 17,13 17,12 @@
#pragma once

// #define USE_I2C
#define SELECT_SOFT_SERIAL_SPEED 0
// #define SELECT_SOFT_SERIAL_SPEED 1
// #define SERIAL_USE_MULTI_TRANSACTION
#define SPLIT_MODS_ENABLE
#define EE_HANDS

#define TRACKBALL_DPI_OPTIONS \
    { 1200, 1800, 2600, 3400 }
#define TRACKBALL_DPI_OPTIONS { 1200, 1800, 2600, 3400 }

#define RGBLIGHT_MAX_BRIGHTNESS 60



@@ 35,5 34,3 @@

#define OLED_DISPLAY_128X64
#define OLED_BRIGHTNESS 50

#define MK_KINETIC_SPEED

A keyboards/handwired/tractyl_manuform/4x6_right/keymaps/drashna/keymap.c => keyboards/handwired/tractyl_manuform/4x6_right/keymaps/drashna/keymap.c +243 -0
@@ 0,0 1,243 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 "drashna.h"

// clang-format off
#define LAYOUT_4x6_right_wrapper(...) LAYOUT_4x6_right(__VA_ARGS__)
#define LAYOUT_4x6_right_base( \
    K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, \
    K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, \
    K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A  \
  ) \
  LAYOUT_4x6_right_wrapper( \
     SH_TT,   K01,    K02,      K03,     K04,     K05,                K06,     K07,     K08,     K09,     K0A,     SH_TT, \
     LALT_T(KC_TAB), K11, K12,  K13,     K14,     K15,                K16,     K17,     K18,     K19,     K1A,     RALT_T(K1B), \
     OS_LSFT, CTL_T(K21), K22,  K23,     K24,     K25,                K26,     K27,     K28,     K29, RCTL_T(K2A), OS_RSFT, \
                       OS_LGUI, OS_LALT,                                                OS_RGUI, OS_RALT, \
                                KC_PSCR, KC_GRV,                                        MEH_T(KC_BTN3),  \
                                         KC_SPC,  OS_LGUI,                     KC_ENT,  \
                                         BK_LWER, TT(_MOUSE),      TT(_MOUSE), DL_RAIS  \
  )
#define LAYOUT_4x6_right_base_wrapper(...)       LAYOUT_4x6_right_base(__VA_ARGS__)


const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [_QWERTY] = LAYOUT_4x6_right_base_wrapper(
        _________________QWERTY_L1_________________, _________________QWERTY_R1_________________,
        _________________QWERTY_L2_________________, _________________QWERTY_R2_________________,
        _________________QWERTY_L3_________________, _________________QWERTY_R3_________________
    ),

    [_COLEMAK] = LAYOUT_4x6_right_base_wrapper(
        _________________COLEMAK_L1________________, _________________COLEMAK_R1________________,
        _________________COLEMAK_L2________________, _________________COLEMAK_R2________________,
        _________________COLEMAK_L3________________, _________________COLEMAK_R3________________
    ),

    [_DVORAK] = LAYOUT_4x6_right_base_wrapper(
        _________________DVORAK_L1_________________, _________________DVORAK_R1_________________,
        _________________DVORAK_L2_________________, _________________DVORAK_R2_________________,
        _________________DVORAK_L3_________________, _________________DVORAK_R3_________________
    ),

    [_WORKMAN] = LAYOUT_4x6_right_base_wrapper(
        _________________WORKMAN_L1________________, _________________WORKMAN_R1________________,
        _________________WORKMAN_L2________________, _________________WORKMAN_R2________________,
        _________________WORKMAN_L3________________, _________________WORKMAN_R3________________
    ),

    [_NORMAN] = LAYOUT_4x6_right_base_wrapper(
        _________________NORMAN_L1_________________, _________________NORMAN_L1_________________,
        _________________NORMAN_L2_________________, _________________NORMAN_R2_________________,
        _________________NORMAN_L3_________________, _________________NORMAN_R3_________________
    ),

    [_MALTRON] = LAYOUT_4x6_right_base_wrapper(
        _________________MALTRON_L1________________, _________________MALTRON_R1________________,
        _________________MALTRON_L2________________, _________________MALTRON_R2________________,
        _________________MALTRON_L3________________, _________________MALTRON_R3________________
    ),

    [_EUCALYN] = LAYOUT_4x6_right_base_wrapper(
        _________________EUCALYN_L1________________, _________________EUCALYN_R1________________,
        _________________EUCALYN_L2________________, _________________EUCALYN_R2________________,
        _________________EUCALYN_L3________________, _________________EUCALYN_R3________________
    ),

    [_CARPLAX] = LAYOUT_4x6_right_base_wrapper(
        _____________CARPLAX_QFMLWY_L1_____________, _____________CARPLAX_QFMLWY_R1_____________,
        _____________CARPLAX_QFMLWY_L2_____________, _____________CARPLAX_QFMLWY_R2_____________,
        _____________CARPLAX_QFMLWY_L3_____________, _____________CARPLAX_QFMLWY_R3_____________
    ),

    [_MOUSE] = LAYOUT_4x6_right(
        _______, _______, _______, _______, _______, _______,                        KC_WH_U, _______, _______, _______, DPI_CONFIG, _______,
        _______, _______, _______, _______, _______, _______,                        KC_WH_D, KC_BTN1, KC_BTN3, KC_BTN2, KC_BTN6, _______,
        _______, _______, _______, _______, _______, _______,                        KC_BTN7, KC_BTN4, KC_BTN5, KC_BTN8, _______, _______,
                          _______, _______,                                                            _______, _______,
                                            _______, _______,                                 _______,
                                                     _______, _______,               _______,
                                                     _______, _______,      _______, _______
    ),
    [_GAMEPAD] = LAYOUT_4x6_right(
        KC_F1,   KC_K,    KC_Q,    KC_W,    KC_E,    KC_R,                           _______, _______, _______, _______, _______, _______,
        KC_TAB,  KC_G,    KC_A,    KC_S,    KC_D,    KC_F,                           _______, _______, _______, _______, _______, _______,
        KC_LCTL, KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_H,                           _______, _______, _______, _______, _______, _______,
                          KC_I,    KC_T,                                                               TG_GAME, KC_NO,
                                            KC_V,    KC_O,                                    _______,
                                                     KC_SPC,  KC_P,                  _______,
                                                     KC_H,    KC_LGUI,      _______, _______
    ),
    [_DIABLO] = LAYOUT_4x6_right(
        KC_TAB,  KC_S,    KC_I,    KC_F,    KC_M,    KC_T,                           _______, _______, _______, _______, _______, _______,
        KC_Q,    KC_1,    KC_2,    KC_3,    KC_4,    KC_G,                           _______, _______, _______, _______, _______, _______,
        KC_LCTL, KC_D3_1, KC_D3_2, KC_D3_3, KC_D3_4, KC_Z,                           _______, _______, _______, _______, _______, _______,
                          KC_F,    KC_L,                                                               KC_NO,   TG_DBLO,
                                    KC_DIABLO_CLEAR, KC_F,                                    _______,
                                               SFT_T(KC_SPC), KC_J,                  _______,
                                               ALT_T(KC_Q),   KC_LGUI,      _______, _______
    ),
    [_LOWER] = LAYOUT_4x6_right_wrapper(
        KC_F12,  _________________LOWER_L1__________________,                        _________________LOWER_R1__________________, KC_F11,
        _______, _________________LOWER_L2__________________,                        _________________LOWER_R2__________________, KC_PIPE,
        _______, _________________LOWER_L3__________________,                        _________________LOWER_R3__________________, _______,
                          _______, _______,                                                          _______, _______,
                                            _______, _______,                               _______,
                                                     _______, _______,             _______,
                                                     _______, _______,    _______, _______
    ),
    [_RAISE] = LAYOUT_4x6_right_wrapper(
        KC_F12,  _________________RAISE_L1__________________,                      _________________RAISE_R1__________________, KC_F11,
        _______, _________________RAISE_L2__________________,                      _________________RAISE_R2__________________, KC_BSLS,
        _______, _________________RAISE_L3__________________,                      _________________RAISE_R3__________________, _______,
                          _______, _______,                                                          _______, _______,
                                            _______, _______,                               _______,
                                                     _______, _______,             _______,
                                                     _______, _______,    _______, _______
    ),
    [_ADJUST] = LAYOUT_4x6_right_wrapper(
        KC_MAKE, _________________ADJUST_L1_________________,                      _________________ADJUST_R1_________________, KC_RST,
        VRSN,    _________________ADJUST_L2_________________,                      _________________ADJUST_R2_________________, EEP_RST,
        UC_MOD,  _________________ADJUST_L3_________________,                      _________________ADJUST_R3_________________, TG_MODS,
                          HPT_DWLI, HPT_DWLD,                                                        TG_GAME, TG_DBLO,
                                            HPT_TOG, HPT_BUZ,                               KC_NUKE,
                                                     _______, _______,             _______,
                                                     _______, _______,    KC_NUKE, _______
    ),
};
// clang-format on

#ifdef POINTING_DEVICE_ENABLE
static uint16_t mouse_timer           = 0;
static uint16_t mouse_debounce_timer  = 0;
static uint8_t  mouse_keycode_tracker = 0;
bool            tap_toggling          = false;

void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) {
    if ((x || y) && timer_elapsed(mouse_timer) > 125) {
        mouse_timer = timer_read();
        if (!layer_state_is(_MOUSE) && !(layer_state_is(_GAMEPAD) || layer_state_is(_DIABLO)) && timer_elapsed(mouse_debounce_timer) > 125) {
            layer_on(_MOUSE);
        }
    }

#    ifdef TAPPING_TERM_PER_KEY
    if (timer_elapsed(mouse_debounce_timer) > get_tapping_term(KC_BTN1, NULL)
#    else
    if (timer_elapsed(mouse_debounce_timer) > TAPPING_TERM
#    endif
        || (layer_state_is(_GAMEPAD) || layer_state_is(_DIABLO))) {
        mouse_report->x = x;
        mouse_report->y = y;
    }
#    ifdef OLED_DRIVER_ENABLE
    if (x || y) oled_timer = timer_read32();
#    endif
}

void matrix_scan_keymap(void) {
    if (timer_elapsed(mouse_timer) > 650 && layer_state_is(_MOUSE) && !mouse_keycode_tracker && !tap_toggling) {
        layer_off(_MOUSE);
    }
    if (tap_toggling) {
        if (!layer_state_is(_MOUSE)) {
            layer_on(_MOUSE);
        }
    }
}

bool process_record_keymap(uint16_t keycode, keyrecord_t* record) {
    switch (keycode) {
        case TT(_MOUSE): {
            if (record->event.pressed) {
                mouse_keycode_tracker++;
            } else {
#    if TAPPING_TOGGLE != 0
                if (record->tap.count == TAPPING_TOGGLE) {
                    tap_toggling ^= 1;
#        if TAPPING_TOGGLE == 1
                    if (!tap_toggling) mouse_keycode_tracker -= record->tap.count + 1;
#        else
                    if (!tap_toggling) mouse_keycode_tracker -= record->tap.count;
#        endif
                } else {
                    mouse_keycode_tracker--;
                }
#    endif
            }
            mouse_timer = timer_read();
            break;
        }
        case MO(_MOUSE):
        case DPI_CONFIG:
        case KC_MS_UP ... KC_MS_WH_RIGHT:
            record->event.pressed ? mouse_keycode_tracker++ : mouse_keycode_tracker--;
            mouse_timer = timer_read();
            break;
        default:
            if (layer_state_is(_MOUSE) && !mouse_keycode_tracker) {
                layer_off(_MOUSE);
            }
            mouse_keycode_tracker = 0;
            mouse_debounce_timer = timer_read();
            break;
    }
    return true;
}

layer_state_t layer_state_set_keymap(layer_state_t state) {
    if (layer_state_cmp(state, _GAMEPAD) || layer_state_cmp(state, _DIABLO)) {
        state |= (1UL << _MOUSE);
    }
    return state;
}
#endif

void matrix_init_keymap(void) {
#ifdef AUDIO_ENABLE
    extern audio_config_t audio_config;

    if (!is_keyboard_master()) {
        audio_stop_all();
        audio_config.enable = false;
    }
#endif
}

void keyboard_post_init_keymap(void) {
    matrix_init_keymap();
}

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/drashna/rules.mk => keyboards/handwired/tractyl_manuform/4x6_right/keymaps/drashna/rules.mk +2 -1
@@ 1,6 1,7 @@
RGBLIGHT_STARTUP_ANIMATION   = yes
COMMAND_ENABLE               = no
AUDIO_ENABLE                 = yes
HAPTIC_ENABLE                = no
COMMAND_ENABLE               = no
TAP_DANCE_ENABLE             = yes
UNICODE_ENABLE               = yes
OLED_DRIVER_ENABLE           = yes

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/readme.md => keyboards/handwired/tractyl_manuform/4x6_right/readme.md +0 -0
A keyboards/handwired/tractyl_manuform/4x6_right/rules.mk => keyboards/handwired/tractyl_manuform/4x6_right/rules.mk +33 -0
@@ 0,0 1,33 @@
# MCU name
MCU = at90usb1286

# Bootloader selection
BOOTLOADER = atmel-dfu

# Build Options
#   change yes to no to disable
#
BOOTMAGIC_ENABLE = lite     # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes       # Mouse keys
EXTRAKEY_ENABLE = yes       # Audio control and System control
CONSOLE_ENABLE = no         # Console for debug
COMMAND_ENABLE = no         # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = yes           # USB Nkey Rollover
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no        # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no       # Enable Bluetooth
AUDIO_ENABLE = no           # Audio output
RGB_MATRIX_ENABLE = no
RGB_MATRIX_DRIVER = WS2812


POINTING_DEVICE_ENABLE = yes
MOUSE_SHARED_EP = no

SPLIT_KEYBOARD = yes

SRC += drivers/sensors/pmw3360.c
QUANTUM_LIB_SRC += spi_master.c tm_sync.c

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/post_config.h => keyboards/handwired/tractyl_manuform/5x6_right/5x6_right.c +17 -15
@@ 14,20 14,22 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once
#include "5x6_right.h"

#ifndef MOUSEKEY_WHEEL_DELTA
#    define MOUSEKEY_WHEEL_DELTA 1
#endif
#ifndef MOUSEKEY_WHEEL_DELAY
#    define MOUSEKEY_WHEEL_DELAY 200
#endif
#ifndef MOUSEKEY_WHEEL_INTERVAL
#    define MOUSEKEY_WHEEL_INTERVAL 50
#endif
#ifndef MOUSEKEY_WHEEL_MAX_SPEED
#    define MOUSEKEY_WHEEL_MAX_SPEED 8
#endif
#ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
#    define MOUSEKEY_WHEEL_TIME_TO_MAX 80
#ifdef SWAP_HANDS_ENABLE
const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {
    /* Left hand, matrix positions */
    {{5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}},
    {{5, 7}, {4, 7}, {3, 7}, {2, 7}, {1, 7}, {0, 7}},
    {{5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}},
    {{5, 9}, {4, 9}, {3, 9}, {2, 9}, {1, 9}, {0, 9}},
    {{5, 10}, {4, 10}, {3, 10}, {2, 10}, {1, 10}, {0, 10}},
    {{5, 11}, {4, 11}, {3, 11}, {2, 11}, {1, 11}, {0, 11}},
    /* Right hand, matrix positions */
    {{5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},
    {{5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},
    {{5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},
    {{5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},
    {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}},
    {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}};
#endif

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.h => keyboards/handwired/tractyl_manuform/5x6_right/5x6_right.h +4 -27
@@ 16,16 16,13 @@

#pragma once

#include "dactyl_manuform.h"
#include "tractyl_manuform.h"
#include "quantum.h"
#include "spi_master.h"
#include "drivers/sensors/pmw3360.h"
#include "pointing_device.h"


#define ___ KC_NO

#define LAYOUT_5x6_right_trackball(\
// clang-format off
#define LAYOUT_5x6_right(\
  L00, L01, L02, L03, L04, L05,                          R00, R01, R02, R03, R04, R05, \
  L10, L11, L12, L13, L14, L15,                          R10, R11, R12, R13, R14, R15, \
  L20, L21, L22, L23, L24, L25,                          R20, R21, R22, R23, R24, R25, \


@@ 50,24 47,4 @@
    { ___, R41, R42, R43, ___, ___ }, \
    { ___, R51, R52, R53, ___, ___ }  \
}


void process_wheel(report_mouse_t* mouse_report);
void process_wheel_user(report_mouse_t* mouse_report, int16_t h, int16_t v);

typedef union {
  uint32_t raw;
  struct {
    uint8_t    dpi_config;
  };
} keyboard_config_t;

extern keyboard_config_t keyboard_config;

enum ploopy_keycodes {
    DPI_CONFIG = SAFE_RANGE,
    KEYMAP_SAFE_RANGE,
};

void master_mouse_send(int8_t x, int8_t y);
void trackball_set_cpi(uint16_t cpi);
// clang-format on

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/config.h => keyboards/handwired/tractyl_manuform/5x6_right/config.h +35 -19
@@ 20,9 20,9 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "config_common.h"

#define PRODUCT_ID      0x3536
#define DEVICE_VER      0x0001
#define PRODUCT         Tractyl-Manuform (5x6 with right side Trackball)
#define PRODUCT_ID  0x3536
#define DEVICE_VER  0x0001
#define PRODUCT     Tractyl Manuform(5x6)

/* key matrix size */
// Rows are doubled-up


@@ 36,37 36,53 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
#define DIODE_DIRECTION COL2ROW

// WS2812 RGB LED strip input and number of LEDs
#define RGB_DI_PIN E7
#define RGBLED_NUM 58
#define RGB_DI_PIN      E7
#define RGBLED_NUM      58
#define RGBLIGHT_SPLIT
#define RGBLED_SPLIT { 30 , 28 }
#define RGBLED_SPLIT { 30, 28 }
#define RGBLIGHT_SLEEP
// #define RGBW
#define RGBLIGHT_LIMIT_VAL 100
#define RGBLIGHT_LIMIT_VAL             50
/* define if matrix has ghost */
//#define MATRIX_HAS_GHOST

/* number of backlight levels */
// #define BACKLIGHT_LEVELS 3

#define DEBUG_LED_PIN D6
#define DEBUG_LED_PIN                  D6

#define USB_POLLING_INTERVAL_MS 1

#define ROTATIONAL_TRANSFORM_ANGLE -25
#define ROTATIONAL_TRANSFORM_ANGLE     -25

/* Bootmagic Lite key configuration */
#define BOOTMAGIC_LITE_ROW 0
#define BOOTMAGIC_LITE_COLUMN 0
#define BOOTMAGIC_LITE_ROW_RIGHT 6
#define BOOTMAGIC_LITE_COLUMN_RIGHT 5
#define BOOTMAGIC_LITE_ROW             0
#define BOOTMAGIC_LITE_COLUMN          0
#define BOOTMAGIC_LITE_ROW_RIGHT       6
#define BOOTMAGIC_LITE_COLUMN_RIGHT    5

#define AUDIO_PIN C6
#define AUDIO_PIN_ALT B7
#define AUDIO_PIN                      C6
#define AUDIO_PIN_ALT                  B7

#define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR 4095
#define DYNAMIC_KEYMAP_LAYER_COUNT 16
#define DYNAMIC_KEYMAP_LAYER_COUNT     16
#define LAYER_STATE_16BIT

/* serial.c configuration for split keyboard */
#undef SOFT_SERIAL_PIN
#define SOFT_SERIAL_PIN D2
#define EE_HANDS

#define ENCODERS_PAD_A { D5 }
#define ENCODERS_PAD_B { D6 }
#define ENCODER_RESOLUTION 1

/* Set 0 if debouncing isn't needed */
#define DEBOUNCE 5

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
#define NO_ACTION_MACRO
#define NO_ACTION_FUNCTION

#define SERIAL_USE_MULTI_TRANSACTION
#define SPLIT_TRANSACTION_IDS_KB RPC_ID_STATE_SYNC, RPC_ID_SLAVE_STATE

A keyboards/handwired/tractyl_manuform/5x6_right/info.json => keyboards/handwired/tractyl_manuform/5x6_right/info.json +75 -0
@@ 0,0 1,75 @@
{
    "keyboard_name": "Tractyl Manuform 5x6",
    "url": "",
    "maintainer": "drashna",
    "width": 17,
    "height": 8,
    "layouts": {
        "LAYOUT_5x6_right": {
            "layout": [
                {"label":"L00", "x":0, "y":0},
                {"label":"L01", "x":1, "y":0},
                {"label":"L02", "x":2, "y":0},
                {"label":"L03", "x":3, "y":0},
                {"label":"L04", "x":4, "y":0},
                {"label":"L05", "x":5, "y":0},
                {"label":"R00", "x":11, "y":0},
                {"label":"R01", "x":12, "y":0},
                {"label":"R02", "x":13, "y":0},
                {"label":"R03", "x":14, "y":0},
                {"label":"R04", "x":15, "y":0},
                {"label":"R05", "x":16, "y":0},
                {"label":"L10", "x":0, "y":1},
                {"label":"L11", "x":1, "y":1},
                {"label":"L12", "x":2, "y":1},
                {"label":"L13", "x":3, "y":1},
                {"label":"L14", "x":4, "y":1},
                {"label":"L15", "x":5, "y":1},
                {"label":"R10", "x":11, "y":1},
                {"label":"R11", "x":12, "y":1},
                {"label":"R12", "x":13, "y":1},
                {"label":"R13", "x":14, "y":1},
                {"label":"R14", "x":15, "y":1},
                {"label":"R15", "x":16, "y":1},
                {"label":"L20", "x":0, "y":2},
                {"label":"L21", "x":1, "y":2},
                {"label":"L22", "x":2, "y":2},
                {"label":"L23", "x":3, "y":2},
                {"label":"L24", "x":4, "y":2},
                {"label":"L25", "x":5, "y":2},
                {"label":"R20", "x":11, "y":2},
                {"label":"R21", "x":12, "y":2},
                {"label":"R22", "x":13, "y":2},
                {"label":"R23", "x":14, "y":2},
                {"label":"R24", "x":15, "y":2},
                {"label":"R25", "x":16, "y":2},
                {"label":"L30", "x":0, "y":3},
                {"label":"L31", "x":1, "y":3},
                {"label":"L32", "x":2, "y":3},
                {"label":"L33", "x":3, "y":3},
                {"label":"L34", "x":4, "y":3},
                {"label":"L35", "x":5, "y":3},
                {"label":"R30", "x":11, "y":3},
                {"label":"R31", "x":12, "y":3},
                {"label":"R32", "x":13, "y":3},
                {"label":"R33", "x":14, "y":3},
                {"label":"R34", "x":15, "y":3},
                {"label":"R35", "x":16, "y":3},
                {"label":"L42", "x":2, "y":4},
                {"label":"L43", "x":3, "y":4},
                {"label":"R42", "x":13, "y":4},
                {"label":"R43", "x":14, "y":4},
                {"label":"L44", "x":4, "y":5},
                {"label":"L45", "x":5, "y":5},
                {"label":"R41", "x":12, "y":5},
                {"label":"L54", "x":6, "y":6},
                {"label":"L55", "x":7, "y":6},
                {"label":"R51", "x":10, "y":6},
                {"label":"L52", "x":6, "y":7},
                {"label":"L53", "x":7, "y":7},
                {"label":"R52", "x":9, "y":7},
                {"label":"R53", "x":10, "y":7}
            ]
        }
    }
}

A keyboards/handwired/tractyl_manuform/5x6_right/keymaps/default/keymap.c => keyboards/handwired/tractyl_manuform/5x6_right/keymaps/default/keymap.c +63 -0
@@ 0,0 1,63 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 QMK_KEYBOARD_H

enum custom_layers {
    _QWERTY,
    _LOWER,
    _RAISE,
};


#define RAISE MO(_RAISE)
#define LOWER MO(_LOWER)

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [_QWERTY] = LAYOUT_5x6_right(
     KC_ESC , KC_1  , KC_2  , KC_3  , KC_4  , KC_5  ,                         KC_6  , KC_7  , KC_8  , KC_9  , KC_0  ,KC_BSPC,
     KC_TAB , KC_Q  , KC_W  , KC_E  , KC_R  , KC_T  ,                         KC_Y  , KC_U  , KC_I  , KC_O  , KC_P  ,KC_MINS,
     KC_LSFT, KC_A  , KC_S  , KC_D  , KC_F  , KC_G  ,                         KC_H  , KC_J  , KC_K  , KC_L  ,KC_SCLN,KC_QUOT,
     KC_LCTL, KC_Z  , KC_X  , KC_C  , KC_V  , KC_B  ,                         KC_N  , KC_M  ,KC_COMM,KC_DOT ,KC_SLSH,KC_BSLASH,
                      KC_LBRC,KC_RBRC,                                                       KC_PLUS, KC_EQL,
                                      RAISE,KC_SPC,                                    LOWER,
                                      KC_TAB,KC_HOME,                                  KC_DEL,
                                      KC_BSPC, KC_GRV,                        KC_LGUI, KC_ENT
  ),

  [_LOWER] = LAYOUT_5x6_right(
     KC_TILD,KC_EXLM, KC_AT ,KC_HASH,KC_DLR ,KC_PERC,                        KC_CIRC,KC_AMPR,KC_ASTR,KC_LPRN,KC_RPRN,KC_DEL,
     _______,_______,_______,_______,_______,KC_LBRC,                        KC_RBRC, KC_P7 , KC_P8 , KC_P9 ,_______,KC_PLUS,
     _______,KC_HOME,KC_PGUP,KC_PGDN,KC_END ,KC_LPRN,                        KC_RPRN, KC_P4 , KC_P5 , KC_P6 ,KC_MINS,KC_PIPE,
     _______,_______,_______,_______,_______,_______,                        _______, KC_P1 , KC_P2 , KC_P3 ,KC_EQL ,KC_UNDS,
                     _______,KC_PSCR,                                                        _______, KC_P0,
                                             _______,_______,                _______,
                                             _______,_______,                _______,
                                             _______,_______,        _______,_______

  ),

  [_RAISE] = LAYOUT_5x6_right(
       KC_F12 , KC_F1 , KC_F2 , KC_F3 , KC_F4 , KC_F5 ,                        KC_F6  , KC_F7 , KC_F8 , KC_F9 ,KC_F10 ,KC_F11 ,
       _______,_______,_______,_______,_______,KC_LBRC,                        KC_RBRC,_______,KC_NLCK,KC_INS ,KC_SLCK,KC_MUTE,
       _______,KC_LEFT,KC_UP  ,KC_DOWN,KC_RGHT,KC_LPRN,                        KC_RPRN,KC_MPRV,KC_MPLY,KC_MNXT,_______,KC_VOLU,
       _______,_______,_______,_______,_______,_______,                        _______,_______,_______,_______,_______,KC_VOLD,
                       _______,_______,                                                        KC_EQL ,_______,
                                               _______,_______,                _______,
                                               _______,_______,                _______,
                                               _______,_______,         _______,_______
  ),
};

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/keymap.c => keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/config.h +20 -1
@@ 14,4 14,23 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// placeholder
#pragma once

// #define USE_I2C
// #define SELECT_SOFT_SERIAL_SPEED 1
// #define SERIAL_USE_MULTI_TRANSACTION
#define SPLIT_MODS_ENABLE
#define EE_HANDS

#define TRACKBALL_DPI_OPTIONS { 1200, 1800, 2600, 3400 }

#define RGBLIGHT_MAX_BRIGHTNESS 60

#undef DEBOUNCE
#define DEBOUNCE 10

#define SOLENOID_PIN F1
#define SOLENOID_DEFAULT_DWELL 8

#define OLED_DISPLAY_128X64
#define OLED_BRIGHTNESS 50

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/drashna/keymap.c => keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/keymap.c +23 -23
@@ 17,75 17,75 @@
#include "drashna.h"

// clang-format off
#define LAYOUT_5x6_right_trackball_wrapper(...) LAYOUT_5x6_right_trackball(__VA_ARGS__)
#define LAYOUT_5x6_right_trackball_base( \
#define LAYOUT_5x6_right_wrapper(...) LAYOUT_5x6_right(__VA_ARGS__)
#define LAYOUT_5x6_right_base( \
    K01, K02, K03, K04, K05, K06, K07, K08, K09, K0A, \
    K11, K12, K13, K14, K15, K16, K17, K18, K19, K1A, K1B, \
    K21, K22, K23, K24, K25, K26, K27, K28, K29, K2A  \
  ) \
  LAYOUT_5x6_right_trackball_wrapper( \
     KC_ESC,  ________________NUMBER_LEFT________________,            ________________NUMBER_RIGHT_______________, KC_MINS, \
  LAYOUT_5x6_right_wrapper( \
     KC_ESC,  ________________NUMBER_LEFT________________,            ________________NUMBER_RIGHT_______________, UC_IRNY, \
     SH_TT,   K01,    K02,      K03,     K04,     K05,                K06,     K07,     K08,     K09,     K0A,     SH_TT, \
     LALT_T(KC_TAB), K11, K12,  K13,     K14,     K15,                K16,     K17,     K18,     K19,     K1A,     RALT_T(K1B), \
     OS_LSFT, CTL_T(K21), K22,  K23,     K24,     K25,                K26,     K27,     K28,     K29, RCTL_T(K2A), OS_RSFT, \
                       OS_LALT, OS_LGUI,                                                TG_GAME, TG_DBLO, \
                                OS_LGUI, KC_GRV,                                        OS_RGUI,  \
                       OS_LGUI, OS_LALT,                                                OS_RGUI, OS_RALT, \
                                KC_PSCR, KC_GRV,                                        MEH_T(KC_BTN3),  \
                                         KC_SPC,  OS_LGUI,                     KC_ENT,  \
                                         BK_LWER, TT(_MOUSE),      TT(_MOUSE), DL_RAIS  \
  )
#define LAYOUT_5x6_right_trackball_base_wrapper(...)       LAYOUT_5x6_right_trackball_base(__VA_ARGS__)
#define LAYOUT_5x6_right_base_wrapper(...)       LAYOUT_5x6_right_base(__VA_ARGS__)


const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    [_QWERTY] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_QWERTY] = LAYOUT_5x6_right_base_wrapper(
        _________________QWERTY_L1_________________, _________________QWERTY_R1_________________,
        _________________QWERTY_L2_________________, _________________QWERTY_R2_________________,
        _________________QWERTY_L3_________________, _________________QWERTY_R3_________________
    ),

    [_COLEMAK] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_COLEMAK] = LAYOUT_5x6_right_base_wrapper(
        _________________COLEMAK_L1________________, _________________COLEMAK_R1________________,
        _________________COLEMAK_L2________________, _________________COLEMAK_R2________________,
        _________________COLEMAK_L3________________, _________________COLEMAK_R3________________
    ),

    [_DVORAK] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_DVORAK] = LAYOUT_5x6_right_base_wrapper(
        _________________DVORAK_L1_________________, _________________DVORAK_R1_________________,
        _________________DVORAK_L2_________________, _________________DVORAK_R2_________________,
        _________________DVORAK_L3_________________, _________________DVORAK_R3_________________
    ),

    [_WORKMAN] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_WORKMAN] = LAYOUT_5x6_right_base_wrapper(
        _________________WORKMAN_L1________________, _________________WORKMAN_R1________________,
        _________________WORKMAN_L2________________, _________________WORKMAN_R2________________,
        _________________WORKMAN_L3________________, _________________WORKMAN_R3________________
    ),

    [_NORMAN] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_NORMAN] = LAYOUT_5x6_right_base_wrapper(
        _________________NORMAN_L1_________________, _________________NORMAN_L1_________________,
        _________________NORMAN_L2_________________, _________________NORMAN_R2_________________,
        _________________NORMAN_L3_________________, _________________NORMAN_R3_________________
    ),

    [_MALTRON] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_MALTRON] = LAYOUT_5x6_right_base_wrapper(
        _________________MALTRON_L1________________, _________________MALTRON_R1________________,
        _________________MALTRON_L2________________, _________________MALTRON_R2________________,
        _________________MALTRON_L3________________, _________________MALTRON_R3________________
    ),

    [_EUCALYN] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_EUCALYN] = LAYOUT_5x6_right_base_wrapper(
        _________________EUCALYN_L1________________, _________________EUCALYN_R1________________,
        _________________EUCALYN_L2________________, _________________EUCALYN_R2________________,
        _________________EUCALYN_L3________________, _________________EUCALYN_R3________________
    ),

    [_CARPLAX] = LAYOUT_5x6_right_trackball_base_wrapper(
    [_CARPLAX] = LAYOUT_5x6_right_base_wrapper(
        _____________CARPLAX_QFMLWY_L1_____________, _____________CARPLAX_QFMLWY_R1_____________,
        _____________CARPLAX_QFMLWY_L2_____________, _____________CARPLAX_QFMLWY_R2_____________,
        _____________CARPLAX_QFMLWY_L3_____________, _____________CARPLAX_QFMLWY_R3_____________
    ),

    [_MOUSE] = LAYOUT_5x6_right_trackball(
    [_MOUSE] = LAYOUT_5x6_right(
        _______, _______, _______, _______, _______, _______,                     DPI_CONFIG, _______, _______, _______, _______, _______,
        _______, _______, _______, _______, _______, _______,                        KC_WH_U, _______, _______, _______, _______, _______,
        _______, _______, _______, _______, _______, _______,                        KC_WH_D, KC_BTN1, KC_BTN3, KC_BTN2, KC_BTN6, _______,


@@ 95,7 95,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
                                                     _______, _______,               _______,
                                                     _______, _______,      _______, _______
    ),
    [_GAMEPAD] = LAYOUT_5x6_right_trackball(
    [_GAMEPAD] = LAYOUT_5x6_right(
        KC_ESC,  KC_NO,   KC_1,    KC_2,    KC_3,    KC_4,                        DPI_CONFIG, _______, _______, _______, _______, _______,
        KC_F1,   KC_K,    KC_Q,    KC_W,    KC_E,    KC_R,                           _______, _______, _______, _______, _______, _______,
        KC_TAB,  KC_G,    KC_A,    KC_S,    KC_D,    KC_F,                           _______, _______, _______, _______, _______, _______,


@@ 105,7 105,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
                                                     KC_SPC,  KC_P,                  _______,
                                                     KC_H,    KC_LGUI,      _______, _______
    ),
    [_DIABLO] = LAYOUT_5x6_right_trackball(
    [_DIABLO] = LAYOUT_5x6_right(
        KC_ESC,  KC_V,    KC_D,    KC_LALT, KC_NO,   KC_NO,                          KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_NO,   KC_NO,
        KC_TAB,  KC_S,    KC_I,    KC_F,    KC_M,    KC_T,                           _______, _______, _______, _______, _______, _______,
        KC_Q,    KC_1,    KC_2,    KC_3,    KC_4,    KC_G,                           _______, _______, _______, _______, _______, _______,


@@ 115,7 115,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
                                               SFT_T(KC_SPC), KC_J,                  _______,
                                               ALT_T(KC_Q),   KC_LGUI,      _______, _______
    ),
    [_LOWER] = LAYOUT_5x6_right_trackball_wrapper(
    [_LOWER] = LAYOUT_5x6_right_wrapper(
        KC_F12,  _________________FUNC_LEFT_________________,                        _________________FUNC_RIGHT________________, KC_F11,
        _______, _________________LOWER_L1__________________,                        _________________LOWER_R1__________________, _______,
        _______, _________________LOWER_L2__________________,                        _________________LOWER_R2__________________, KC_PIPE,


@@ 125,7 125,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
                                                     _______, _______,             _______,
                                                     _______, _______,    _______, _______
    ),
    [_RAISE] = LAYOUT_5x6_right_trackball_wrapper(
    [_RAISE] = LAYOUT_5x6_right_wrapper(
        KC_F12,  _________________FUNC_LEFT_________________,                      _________________FUNC_RIGHT________________, KC_F11,
        KC_GRV,  _________________RAISE_L1__________________,                      _________________RAISE_R1__________________, _______,
        _______, _________________RAISE_L2__________________,                      _________________RAISE_R2__________________, KC_BSLS,


@@ 135,12 135,12 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
                                                     _______, _______,             _______,
                                                     _______, _______,    _______, _______
    ),
    [_ADJUST] = LAYOUT_5x6_right_trackball_wrapper(
    [_ADJUST] = LAYOUT_5x6_right_wrapper(
        KC_MAKE, ___________________BLANK___________________,                      _________________ADJUST_R1_________________, KC_RST,
        VRSN,    _________________ADJUST_L1_________________,                      _________________ADJUST_R1_________________, EEP_RST,
        _______, _________________ADJUST_L2_________________,                      _________________ADJUST_R2_________________, TG_MODS,
        UC_MOD,  _________________ADJUST_L2_________________,                      _________________ADJUST_R2_________________, TG_MODS,
        _______, _________________ADJUST_L3_________________,                      _________________ADJUST_R3_________________, KC_MPLY,
                          HPT_DWLI, HPT_DWLD,                                                        _______, _______,
                          HPT_DWLI, HPT_DWLD,                                                        TG_GAME, TG_DBLO,
                                            HPT_TOG, HPT_BUZ,                               KC_NUKE,
                                                     _______, _______,             _______,
                                                     _______, _______,    KC_NUKE, _______

A keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk => keyboards/handwired/tractyl_manuform/5x6_right/keymaps/drashna/rules.mk +11 -0
@@ 0,0 1,11 @@
RGBLIGHT_ENABLE              = yes
RGBLIGHT_STARTUP_ANIMATION   = yes
AUDIO_ENABLE                 = yes
HAPTIC_ENABLE                = no
COMMAND_ENABLE               = no
TAP_DANCE_ENABLE             = yes
UNICODE_ENABLE               = yes
OLED_DRIVER_ENABLE           = yes
WPM_ENABLE                   = yes
ENCODER_ENABLE               = yes
# DEBOUNCE_TYPE = sym_eager_pk

A keyboards/handwired/tractyl_manuform/5x6_right/readme.md => keyboards/handwired/tractyl_manuform/5x6_right/readme.md +21 -0
@@ 0,0 1,21 @@
# Drashna's Dactyl Manuform (5x6) with a right side trackball

![](https://preview.redd.it/zwt91036m3y51.jpg?width=960&crop=smart&auto=webp&s=e030deb7d8285c95a1a30c69a7e7a71f750e87bb)

It's a Dactyl Manuform with an integrated thumb based trackball, using the pmw3360 optical sensor.

It's powered by 2x Teensy++ 2.0's, using Drashna's [Teensy VBUS Hack](https://docs.qmk.fm/#/feature_split_keyboard?id=hardware-considerations-and-mods) for better detection.


* Keyboard Maintainer: [Drashna Jael're](https://github.com/drashna)
* Hardware Supported: [Design files](https://gitlab.com/keyboards1/dm_r_track/-/tree/master/boolean), [Teensy++ 2.0 (2x)](https://www.pjrc.com/store/teensypp.html), [PMW3360 Optical Sensor](https://www.tindie.com/products/jkicklighter/pmw3360-motion-sensor/)

Make example for this keyboard (after setting up your build environment):

    make handwired/dactyl_manuform/5x6_right_trackball:default

Flashing example for this keyboard:

    make handwired/dactyl_manuform/5x6_right_trackball:default:flash

See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/rules.mk => keyboards/handwired/tractyl_manuform/5x6_right/rules.mk +5 -5
@@ 11,21 11,21 @@ BOOTMAGIC_ENABLE = lite     # Virtual DIP switch configuration
MOUSEKEY_ENABLE = yes       # Mouse keys
EXTRAKEY_ENABLE = yes       # Audio control and System control
CONSOLE_ENABLE = no         # Console for debug
COMMAND_ENABLE = no        # Commands for debug and configuration
COMMAND_ENABLE = no         # Commands for debug and configuration
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
NKRO_ENABLE = yes            # USB Nkey Rollover
NKRO_ENABLE = yes           # USB Nkey Rollover
BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes       # Enable keyboard RGB underglow
RGBLIGHT_ENABLE = no        # Enable keyboard RGB underglow
BLUETOOTH_ENABLE = no       # Enable Bluetooth
AUDIO_ENABLE = no           # Audio output
SWAP_HANDS_ENABLE = yes

POINTING_DEVICE_ENABLE = yes
MOUSE_SHARED_EP = no

SPLIT_KEYBOARD = yes
SPLIT_TRANSPORT = custom

SRC += drivers/sensors/pmw3360.c
QUANTUM_LIB_SRC += pointer_transport.c serial.c i2c_master.c i2c_slave.c spi_master.c
QUANTUM_LIB_SRC += spi_master.c tm_sync.c

A keyboards/handwired/tractyl_manuform/config.h => keyboards/handwired/tractyl_manuform/config.h +43 -0
@@ 0,0 1,43 @@
/*
Copyright 2012 Jun Wako <wakojun@gmail.com>
Copyright 2015 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/>.
*/

#pragma once

#include "config_common.h"

/* USB Device descriptor parameter */
#define VENDOR_ID               0x44DD
#define MANUFACTURER            Drashna

/* Set 0 if debouncing isn't needed */
#define DEBOUNCE                5

#define USB_POLLING_INTERVAL_MS 1

/* disable debug print */
// #define NO_DEBUG

/* disable print */
// #define NO_PRINT

/* disable action features */
//#define NO_ACTION_LAYER
//#define NO_ACTION_TAPPING
//#define NO_ACTION_ONESHOT
#define NO_ACTION_MACRO
#define NO_ACTION_FUNCTION

A keyboards/handwired/tractyl_manuform/post_config.h => keyboards/handwired/tractyl_manuform/post_config.h +96 -0
@@ 0,0 1,96 @@
/*
Copyright 2012 Jun Wako <wakojun@gmail.com>
Copyright 2015 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/>.
*/

#pragma once

// mouse config
#    ifndef MOUSEKEY_MOVE_DELTA
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_MOVE_DELTA 5
#        else
#            define MOUSEKEY_MOVE_DELTA 25
#        endif
#    endif
#    ifndef MOUSEKEY_DELAY
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_DELAY 300
#        else
#            define MOUSEKEY_DELAY 8
#        endif
#    endif
#    ifndef MOUSEKEY_INTERVAL
#        ifndef MK_KINETIC_SPEED
#            define MOUSEKEY_INTERVAL 50
#        else
#            define MOUSEKEY_INTERVAL 20
#        endif
#    endif
#    ifndef MOUSEKEY_MAX_SPEED
#        define MOUSEKEY_MAX_SPEED 7
#    endif
#    ifndef MOUSEKEY_TIME_TO_MAX
#        define MOUSEKEY_TIME_TO_MAX 60
#    endif
#    ifndef MOUSEKEY_INITIAL_SPEED
#        define MOUSEKEY_INITIAL_SPEED 100
#    endif
#    ifndef MOUSEKEY_BASE_SPEED
#        define MOUSEKEY_BASE_SPEED 1000
#    endif
#    ifndef MOUSEKEY_DECELERATED_SPEED
#        define MOUSEKEY_DECELERATED_SPEED 400
#    endif
#    ifndef MOUSEKEY_ACCELERATED_SPEED
#        define MOUSEKEY_ACCELERATED_SPEED 3000
#    endif


// mouse scroll config
#    ifndef MOUSEKEY_WHEEL_DELAY
#        define MOUSEKEY_WHEEL_DELAY 15
#    endif
#    ifndef MOUSEKEY_WHEEL_DELTA
#        define MOUSEKEY_WHEEL_DELTA 1
#    endif
#    ifndef MOUSEKEY_WHEEL_INTERVAL
#        define MOUSEKEY_WHEEL_INTERVAL 50
#    endif
#    ifndef MOUSEKEY_WHEEL_MAX_SPEED
#        define MOUSEKEY_WHEEL_MAX_SPEED 8
#    endif
#    ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
#        define MOUSEKEY_WHEEL_TIME_TO_MAX 80
#    endif

#    ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
#        define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 8
#    endif
#    ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
#        define MOUSEKEY_WHEEL_BASE_MOVEMENTS 48
#    endif
#    ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
#    endif
#    ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
#    endif


#ifndef DEBOUNCE
#    define DEBOUNCE 5
#endif

A keyboards/handwired/tractyl_manuform/readme.md => keyboards/handwired/tractyl_manuform/readme.md +159 -0
@@ 0,0 1,159 @@
Dactyl Manuform (4x5, 5x6, 5x7, 6x6)
======
the [Dactyl-Manuform](https://github.com/tshort/dactyl-keyboard) is a split curved keyboard based on the design of [adereth dactyl](https://github.com/adereth/dactyl-keyboard) and thumb cluster design of the [manuform](https://geekhack.org/index.php?topic=46015.0) keyboard, the hardware is similar to the let's split keyboard. all information needed for making one is in the first link.
![Imgur](https://i.imgur.com/7y0Vbyd.jpg)


## First Time Setup

Download or clone the `qmk_firmware` repo and navigate to its top level directory. Once your build environment is setup, you'll be able to generate the default .hex using:

Depending on your Layout chose one of the follwing commands:

```
$ make handwired/dactyl_manuform/YOUR_LAYOUT:YOUR_KEYMAP_NAME
```

example:
```
$ make handwired/dactyl_manuform/4x5:default
```

If everything worked correctly you will see a file:

```
dactyl_manuform_YOUR_LAYOUT_YOUR_KEYMAP_NAME.hex
```

For more information on customizing keymaps, take a look at the primary documentation for [Customizing Your Keymap](/docs/faq_keymap.md) in the main readme.md.


## Keymaps

### [Keymaps 4x5](/keyboards/handwired/dactyl_manuform/4x5/keymaps/)

#### Default
Simple QWERTY layout with 3 Layers.
#### Dvorak

### [Keymaps 5x6](/keyboards/handwired/dactyl_manuform/5x6/keymaps/)

#### Default
Just a copy of the Impstyle keymap. Feel free to adjust it.

#### Impstyle
A simple QWERTY keymap with 3 Layers. Both sides are connected via serial and the Left ist the master.

### [Keymaps 5x7 aka almost Ergodox](/keyboards/handwired/dactyl_manuform/5x7/keymaps/)
#### Default
Keymap of Loligagger from geekhack.

### [Keymaps 6x6](/keyboards/handwired/dactyl_manuform/6x6/keymaps/)

#### Default
Simple QWERTY layout with 3 Layers.

## Required Hardware

Apart from diodes and key switches for the keyboard matrix in each half, you
will need:

* 2 Arduino Pro Micros. You can find these on AliExpress for ≈3.50USD each.
* 2 TRRS sockets and 1 TRRS cable, or 2 TRS sockets and 1 TRS cable

Alternatively, you can use any sort of cable and socket that has at least 3
wires. If you want to use I2C to communicate between halves, you will need a
cable with at least 4 wires and 2x 4.7kΩ pull-up resistors

## Optional Hardware
A speaker can be hooked-up to either side to the `5` (`C6`) pin and `GND`, and turned on via `AUDIO_ENABLE`.

## Wiring

The 3 wires of the TRS/TRRS cable need to connect GND, VCC, and digital pin 3 (i.e.
PD0 on the ATmega32u4) between the two Pro Micros.

Next, wire your key matrix to any of the remaining 17 IO pins of the pro micro
and modify the `matrix.c` accordingly.

The wiring for serial:

![serial wiring](https://i.imgur.com/C3D1GAQ.png)

The wiring for i2c:

![i2c wiring](https://i.imgur.com/Hbzhc6E.png)

The pull-up resistors may be placed on either half. It is also possible
to use 4 resistors and have the pull-ups in both halves, but this is
unnecessary in simple use cases.

You can change your configuration between serial and i2c by modifying your `config.h` file.

## Notes on Software Configuration

the keymaps in here are for the 4x5 layout of the keyboard only.

## Flashing

To flash your firmware take a look at: [Flashing Instructions and Bootloader Information](https://docs.qmk.fm/#/flashing)


## Choosing which board to plug the USB cable into (choosing Master)

Because the two boards are identical, the firmware has logic to differentiate the left and right board.

It uses two strategies to figure things out: looking at the EEPROM (memory on the chip) or looking if the current board has the usb cable.

The EEPROM approach requires additional setup (flashing the eeprom) but allows you to swap the usb cable to either side.

The USB cable approach is easier to setup and if you just want the usb cable on the left board, you do not need to do anything extra.

### Setting the left hand as master

If you always plug the usb cable into the left board, nothing extra is needed as this is the default. Comment out `EE_HANDS` and comment out `I2C_MASTER_RIGHT` or `MASTER_RIGHT` if for some reason it was set.

### Setting the right hand as master

If you always plug the usb cable into the right board, add an extra flag to your `config.h`
```
 #define MASTER_RIGHT
```

### Setting EE_hands to use either hands as master

If you define `EE_HANDS` in your `config.h`, you will need to set the
EEPROM for the left and right halves.

The EEPROM is used to store whether the
half is left handed or right handed. This makes it so that the same firmware
file will run on both hands instead of having to flash left and right handed
versions of the firmware to each half. To flash the EEPROM file for the left
half run:
```
make handwired/dactyl_promicro:default:dfu-split-left
make handwired/dactyl_promicro:default:dfu-split-right
```

After you have flashed the EEPROM, you then need to set `EE_HANDS` in your config.h, rebuild the hex files and reflash.

Note that you need to program both halves, but you have the option of using
different keymaps for each half. You could program the left half with a QWERTY
layout and the right half with a Colemak layout using bootmagic's default layout option.
Then if you connect the left half to a computer by USB the keyboard will use QWERTY and Colemak when the
right half is connected.


Notes on Using Pro Micro 3.3V
-----------------------------

Do update the `F_CPU` parameter in `rules.mk` to `8000000` which reflects
the frequency on the 3.3V board.

Also, if the slave board is producing weird characters in certain columns,
update the following line in `matrix.c` to the following:

```
// wait_us(30);  // without this wait read unstable value.
wait_us(300);  // without this wait read unstable value.
```

A keyboards/handwired/tractyl_manuform/tm_sync.c => keyboards/handwired/tractyl_manuform/tm_sync.c +132 -0
@@ 0,0 1,132 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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/>.
 */

#ifdef SPLIT_TRANSACTION_IDS_KB

#    include "tractyl_manuform.h"
#    include "transactions.h"
#    include <string.h>
#    include "drivers/sensors/pmw3360.h"

kb_runtime_config_t kb_state;
kb_slave_data_t     kb_slave;

void slave_state_sync(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) {
    if (target2initiator_buffer_size == sizeof(kb_slave)) {
        memcpy(target2initiator_buffer, &kb_slave, sizeof(kb_slave));
        if (kb_slave.mouse_x > 127) {
            kb_slave.mouse_x -= 127;
        } else if (kb_slave.mouse_x < -127) {
            kb_slave.mouse_x += 127;
        } else {
            kb_slave.mouse_x = 0;
        }

        if (kb_slave.mouse_y > 127) {
            kb_slave.mouse_y -= 127;
        } else if (kb_slave.mouse_y < -127) {
            kb_slave.mouse_y += 127;
        } else {
            kb_slave.mouse_y = 0;
        }
    }
}

void pointer_state_sync(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) {
    if (initiator2target_buffer_size == sizeof(kb_state)) {
        memcpy(&kb_state, initiator2target_buffer, sizeof(kb_state));
    }
}

void keyboard_post_init_kb(void) {
    // Register keyboard state sync split transaction
    transaction_register_rpc(RPC_ID_STATE_SYNC, pointer_state_sync);
    transaction_register_rpc(RPC_ID_SLAVE_STATE, slave_state_sync);

    keyboard_post_init_user();
}

void kb_state_update(void) {
#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_master() && !is_keyboard_left()) {
        static uint16_t cpi = 0;
        if (cpi != kb_state.device_cpi) {
            cpi = kb_state.device_cpi;
            pmw_set_cpi(cpi);
        }
    }
#    endif
}

void kb_post_state_update(void) {
#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_master() && is_keyboard_left()) {
        report_mouse_t temp_report = pointing_device_get_report();
        temp_report.x              = kb_slave.mouse_x;
        temp_report.y              = kb_slave.mouse_y;
        pointing_device_set_report(temp_report);
    }
#    endif
}

void kb_state_sync(void) {
    if (is_keyboard_master()) {
        // Keep track of the last state, so that we can tell if we need to propagate to slave
        static kb_runtime_config_t last_kb_state;
        static uint32_t            last_sync        = 0;
        static uint32_t            mouse_sync       = 0;
        bool                       needs_sync       = false;

        // Check if the state values are different
        if (memcmp(&kb_state, &last_kb_state, sizeof(kb_runtime_config_t))) {
            needs_sync = true;
            memcpy(&last_kb_state, &kb_state, sizeof(kb_runtime_config_t));
        }
        // Send to slave every 500ms regardless of state change
        if (timer_elapsed32(last_sync) > 500) {
            needs_sync = true;
        }

        // Perform the sync if requested
        if (needs_sync) {
            if (transaction_rpc_send(RPC_ID_STATE_SYNC, sizeof(kb_runtime_config_t), &kb_state)) {
                last_sync = timer_read32();
            }
        }

        if (is_keyboard_left()) {
            if (timer_elapsed32(mouse_sync) >= 5) {
                // always sync slave data, since it may contain device reports.
                if (transaction_rpc_recv(RPC_ID_SLAVE_STATE, sizeof(kb_slave_data_t), &kb_slave)) {
                    if (kb_slave.mouse_x >= -127 && kb_slave.mouse_x <= 127 && kb_slave.mouse_y >= -127 && kb_slave.mouse_y <= 127) {
                        mouse_sync = timer_read32();
                    }
                }
            }
        }
    }
}

void housekeeping_task_kb(void) {
    // Update kb_state so we can send to slave
    kb_state_update();

    // Data sync from master to slave
    kb_state_sync();

    kb_post_state_update();
}
#endif

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/5x6_right_trackball.c => keyboards/handwired/tractyl_manuform/tractyl_manuform.c +39 -49
@@ 1,4 1,4 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
/* Copyright 2020 Christopher Courtney <drashna@live.com> (@drashna)
 *
 * 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


@@ 14,10 14,14 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "5x6_right_trackball.h"
#include "tractyl_manuform.h"
#include "transactions.h"
#include <string.h>
#include "drivers/sensors/pmw3360.h"

#ifndef TRACKBALL_DPI_OPTIONS
#    define TRACKBALL_DPI_OPTIONS { 1200, 1600, 2400 }
#    define TRACKBALL_DPI_OPTIONS \
        { 1200, 1600, 2400 }
#    ifndef TRACKBALL_DPI_DEFAULT
#        define TRACKBALL_DPI_DEFAULT 1
#    endif


@@ 26,12 30,15 @@
#    define TRACKBALL_DPI_DEFAULT 0
#endif

extern kb_runtime_config_t kb_state;
extern kb_slave_data_t     kb_slave;

keyboard_config_t keyboard_config;
uint16_t dpi_array[] = TRACKBALL_DPI_OPTIONS;
uint16_t          dpi_array[] = TRACKBALL_DPI_OPTIONS;
#define DPI_OPTION_SIZE (sizeof(dpi_array) / sizeof(uint16_t))

bool     BurstState        = false;  // init burst state for Trackball module
uint16_t MotionStart       = 0;      // Timer for accel, 0 is resting state
bool     BurstState  = false; // init burst state for Trackball module
uint16_t MotionStart = 0;     // Timer for accel, 0 is resting state

__attribute__((weak)) void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y) {
    mouse_report->x = x;


@@ 53,12 60,8 @@ __attribute__((weak)) void process_mouse(report_mouse_t* mouse_report) {
            MotionStart = timer_read();
        }

        if (debug_mouse) {
            dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart);
        }
        if (debug_mouse) {
            dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy);
        }
        if (debug_mouse) { dprintf("Delt] d: %d t: %u\n", abs(data.dx) + abs(data.dy), MotionStart); }
        if (debug_mouse) { dprintf("Pre ] X: %d, Y: %d\n", data.dx, data.dy); }
#if defined(PROFILE_LINEAR)
        float scale = float(timer_elaspsed(MotionStart)) / 1000.0;
        data.dx *= scale;


@@ 85,7 88,7 @@ bool process_record_kb(uint16_t keycode, keyrecord_t* record) {

#ifdef POINTING_DEVICE_ENABLE
    if (keycode == DPI_CONFIG && record->event.pressed) {
        if ((get_mods()|get_oneshot_mods()) & MOD_MASK_SHIFT) {
        if ((get_mods() | get_oneshot_mods()) & MOD_MASK_SHIFT) {
            keyboard_config.dpi_config = (keyboard_config.dpi_config - 1) % DPI_OPTION_SIZE;
        } else {
            keyboard_config.dpi_config = (keyboard_config.dpi_config + 1) % DPI_OPTION_SIZE;


@@ 146,19 149,11 @@ void pointing_device_init(void) {
    trackball_set_cpi(dpi_array[keyboard_config.dpi_config]);
}

static bool has_report_changed(report_mouse_t new, report_mouse_t old) {
    return (new.buttons != old.buttons) ||
           (new.x && new.x != old.x) ||
           (new.y && new.y != old.y) ||
           (new.h && new.h != old.h) ||
           (new.v && new.v != old.v);
}
static bool has_report_changed(report_mouse_t new, report_mouse_t old) { return (new.buttons != old.buttons) || (new.x&& new.x != old.x) || (new.y&& new.y != old.y) || (new.h&& new.h != old.h) || (new.v&& new.v != old.v); }

void pointing_device_task(void) {
    report_mouse_t mouse_report = pointing_device_get_report();
    if (!is_keyboard_left()) {
        process_mouse(&mouse_report);
    }
    if (!is_keyboard_left()) { process_mouse(&mouse_report); }

    pointing_device_set_report(mouse_report);
    pointing_device_send();


@@ 178,24 173,20 @@ void matrix_init_kb(void) {
    // is safe to just read DPI setting since matrix init
    // comes before pointing device init.
    keyboard_config.raw = eeconfig_read_kb();
    if (keyboard_config.dpi_config > DPI_OPTION_SIZE) {
        eeconfig_init_kb();
    }
    if (keyboard_config.dpi_config > DPI_OPTION_SIZE) { eeconfig_init_kb(); }
    matrix_init_user();
}

#ifdef POINTING_DEVICE_ENABLE
void pointing_device_send(void) {
    static report_mouse_t old_report = {};
    report_mouse_t mouseReport = pointing_device_get_report();
    static report_mouse_t old_report  = {};
    report_mouse_t        mouseReport = pointing_device_get_report();
    if (is_keyboard_master()) {
        int8_t x = mouseReport.x, y = mouseReport.y;
        mouseReport.x = 0;
        mouseReport.y = 0;
        process_mouse_user(&mouseReport, x, y);
        if (has_report_changed(mouseReport, old_report)) {
            host_mouse_send(&mouseReport);
        }
        if (has_report_changed(mouseReport, old_report)) { host_mouse_send(&mouseReport); }
    } else {
        master_mouse_send(mouseReport.x, mouseReport.y);
    }


@@ 203,26 194,25 @@ void pointing_device_send(void) {
    mouseReport.y = 0;
    mouseReport.v = 0;
    mouseReport.h = 0;
    old_report = mouseReport;
    old_report    = mouseReport;
    pointing_device_set_report(mouseReport);
}
#endif

#ifdef SWAP_HANDS_ENABLE
const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {
    /* Left hand, matrix positions */
    {{5, 6}, {4, 6}, {3, 6}, {2, 6}, {1, 6}, {0, 6}},
    {{5, 7}, {4, 7}, {3, 7}, {2, 7}, {1, 7}, {0, 7}},
    {{5, 8}, {4, 8}, {3, 8}, {2, 8}, {1, 8}, {0, 8}},
    {{5, 9}, {4, 9}, {3, 9}, {2, 9}, {1, 9}, {0, 9}},
    {{5, 10}, {4, 10}, {3, 10}, {2, 10}, {1, 10}, {0, 10}},
    {{5, 11}, {4, 11}, {3, 11}, {2, 11}, {1, 11}, {0, 11}},
    /* Right hand, matrix positions */
    {{5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},
    {{5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},
    {{5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},
    {{5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},
    {{5, 4}, {4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}},
    {{5, 5}, {4, 5}, {3, 5}, {2, 5}, {1, 5}, {0, 5}}
};
#ifdef POINTING_DEVICE_ENABLE
void master_mouse_send(int8_t x, int8_t y) {
#ifdef SPLIT_TRANSACTION_IDS_KB
    kb_slave.mouse_x += x;
    kb_slave.mouse_y += y;
#endif
}
void trackball_set_cpi(uint16_t cpi) {
    if (!is_keyboard_left()) {
        pmw_set_cpi(cpi);
    } else {
#ifdef SPLIT_TRANSACTION_IDS_KB
        kb_state.device_cpi = cpi;
#endif
    }
}
#endif

A keyboards/handwired/tractyl_manuform/tractyl_manuform.h => keyboards/handwired/tractyl_manuform/tractyl_manuform.h +55 -0
@@ 0,0 1,55 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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/>.
 */

#pragma once

#include "quantum.h"
#include "pointing_device.h"

#if defined(KEYBOARD_handwired_tractyl_manuform_5x6_right)
#    include "5x6_right.h"
#elif defined(KEYBOARD_handwired_tractyl_manuform_4x6_right)
#    include "4x6_right.h"
#endif

void process_mouse(report_mouse_t* mouse_report);
void process_mouse_user(report_mouse_t* mouse_report, int16_t x, int16_t y);

typedef union {
    uint32_t raw;
    struct {
        uint8_t dpi_config;
    };
} keyboard_config_t;

extern keyboard_config_t keyboard_config;

enum ploopy_keycodes {
    DPI_CONFIG = SAFE_RANGE,
    KEYMAP_SAFE_RANGE,
};

void master_mouse_send(int8_t x, int8_t y);
void trackball_set_cpi(uint16_t cpi);

typedef struct {
    uint16_t        device_cpi;
} kb_runtime_config_t;

typedef struct  {
    int16_t mouse_x;
    int16_t mouse_y;
} kb_slave_data_t;

M keyboards/kyria/keymaps/drashna/keymap.c => keyboards/kyria/keymaps/drashna/keymap.c +1 -1
@@ 131,7 131,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
// clang-format on

#ifdef OLED_DRIVER_ENABLE
oled_rotation_t oled_init_user(oled_rotation_t rotation) { return OLED_ROTATION_180; }
oled_rotation_t oled_init_keymap(oled_rotation_t rotation) { return OLED_ROTATION_180; }
#endif

#ifdef ENCODER_ENABLE

M keyboards/kyria/keymaps/drashna/rules.mk => keyboards/kyria/keymaps/drashna/rules.mk +3 -9
@@ 3,19 3,13 @@ ENCODER_ENABLE = yes       # ENables the use of one or more encoders
RGBLIGHT_ENABLE = yes      # Enable keyboard RGB underglow
RGBLIGHT_STARTUP_ANIMATION = no

BOOTMAGIC_ENABLE = no      # Virtual DIP switch configuration
BOOTMAGIC_ENABLE = lite    # Virtual DIP switch configuration
MOUSEKEY_ENABLE = no       # Mouse keys
EXTRAKEY_ENABLE = yes      # Audio control and System control
CONSOLE_ENABLE = no        # Console for debug
COMMAND_ENABLE = no        # Commands for debug and configuration
NKRO_ENABLE = no           # USB Nkey Rollover
BACKLIGHT_ENABLE = no      # Enable keyboard backlight functionality on B7 by default
MIDI_ENABLE = no           # MIDI support
UNICODE_ENABLE = no        # Unicode
BLUETOOTH_ENABLE = no      # Enable Bluetooth with the Adafruit EZ-Key HID
AUDIO_ENABLE = no          # Audio output on port C6
NKRO_ENABLE = yes          # USB Nkey Rollover
UNICODE_ENABLE = yes       # Unicode
BOOTLOADER = atmel-dfu

# SPLIT_TRANSPORT = mirror

# TAP_DANCE_ENABLE = yes

M layouts/community/ergodox/drashna/keymap.c => layouts/community/ergodox/drashna/keymap.c +7 -6
@@ 19,7 19,7 @@
#ifdef UNICODEMAP_ENABLE
#    include "drashna_unicode.h"
#endif  // UNICODEMAP_ENABLE

#include "drivers/sensors/pimoroni_trackball.h"
enum more_custom_keycodes {
    KC_SWAP_NUM = NEW_SAFE_RANGE,
    PM_SCROLL,


@@ 317,13 317,14 @@ void shutdown_keymap(void) {

static bool mouse_button_one, trackball_button_one;

void trackball_check_click(bool pressed, report_mouse_t* mouse) {
    if (mouse_button_one | pressed) {
        mouse->buttons |= MOUSE_BTN1;
void trackball_register_button(bool pressed, enum mouse_buttons button) {
    report_mouse_t currentReport = pointing_device_get_report();
    if (pressed) {
        currentReport.buttons |= button;
    } else {
        mouse->buttons &= ~MOUSE_BTN1;
        currentReport.buttons &= ~button;
    }
    trackball_button_one = pressed;
    pointing_device_set_report(currentReport);
}
#endif


R layouts/community/ergodox/drashna/visualizer.c => layouts/community/ergodox/drashna/visualizer.c_old +0 -0
M layouts/community/split_3x6_3/drashna/config.h => layouts/community/split_3x6_3/drashna/config.h +19 -20
@@ 31,11 31,11 @@

#ifdef RGBLIGHT_ENABLE
#    undef RGBLED_NUM
#    define RGBLED_NUM 27
#    define RGBLED_NUM         27

#    define RGBLIGHT_HUE_STEP 8
#    define RGBLIGHT_SAT_STEP 8
#    define RGBLIGHT_VAL_STEP 5
#    define RGBLIGHT_HUE_STEP  8
#    define RGBLIGHT_SAT_STEP  8
#    define RGBLIGHT_VAL_STEP  5
#    define RGBLIGHT_LIMIT_VAL 120
#endif



@@ 44,37 44,36 @@
#        undef RGB_DI_PIN
#        define RGB_DI_PIN A3
#    endif
#    define RGB_MATRIX_KEYPRESSES  // reacts to keypresses
#    define RGB_MATRIX_KEYPRESSES // reacts to keypresses
// #   define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
// #   define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
#    define RGB_DISABLE_WHEN_USB_SUSPENDED  // turn off effects when suspended
// #   define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
// #   define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness)
#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS 120  // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
#    define RGB_MATRIX_HUE_STEP 8
#    define RGB_MATRIX_SAT_STEP 8
#    define RGB_MATRIX_VAL_STEP 5
#    define RGB_MATRIX_SPD_STEP 10
#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS 120 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
#    define RGB_MATRIX_HUE_STEP           8
#    define RGB_MATRIX_SAT_STEP           8
#    define RGB_MATRIX_VAL_STEP           5
#    define RGB_MATRIX_SPD_STEP           10
#endif

#ifdef CONVERT_TO_PROTON_C
#    define WS2812_PWM_DRIVER PWMD2                 // default: PWMD2
#    define WS2812_PWM_CHANNEL 4                    // default: 2
#    define WS2812_PWM_PAL_MODE 1                   // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 2
#    define WS2812_DMA_STREAM STM32_DMA1_STREAM2    // DMA Stream for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#    define WS2812_DMA_CHANNEL 2                    // DMA Channel for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#    define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM2_UP  // DMAMUX configuration for TIMx_UP -- only required if your MCU has a DMAMUX peripheral, see the respective reference manual for the appropriate values for your MCU.
#    define WS2812_PWM_DRIVER   PWMD2                 // default: PWMD2
#    define WS2812_PWM_CHANNEL  4                     // default: 2
#    define WS2812_PWM_PAL_MODE 1                     // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 2
#    define WS2812_DMA_STREAM   STM32_DMA1_STREAM2    // DMA Stream for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#    define WS2812_DMA_CHANNEL  2                     // DMA Channel for TIMx_UP, see the respective reference manual for the appropriate values for your MCU.
#    define WS2812_DMAMUX_ID    STM32_DMAMUX1_TIM2_UP // DMAMUX configuration for TIMx_UP -- only required if your MCU has a DMAMUX peripheral, see the respective reference manual for the appropriate values for your MCU.

#    undef SOFT_SERIAL_PIN
#    define SOFT_SERIAL_PIN D3
#    define SERIAL_USART_DRIVER SD1     // USART driver of TX pin. default: SD1
#    define SERIAL_USART_TX_PAL_MODE 7  // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7
#    define SOFT_SERIAL_PIN          D3
#    define SERIAL_USART_DRIVER      SD1 // USART driver of TX pin. default: SD1
#    define SERIAL_USART_TX_PAL_MODE 7   // Pin "alternate function", see the respective datasheet for the appropriate values for your MCU. default: 7

// #define INIT_EE_HANDS_LEFT
// #define INIT_EE_HANDS_RIGHT
#endif


#if defined(AUDIO_ENABLE) && !defined(CONVERT_TO_PROTON_C)
#    define AUDIO_PIN B6
#    define NO_MUSIC_MODE

M layouts/community/split_3x6_3/drashna/keymap.c => layouts/community/split_3x6_3/drashna/keymap.c +1 -1
@@ 114,7 114,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
// clang-format on

#ifdef OLED_DRIVER_ENABLE
oled_rotation_t oled_init_user(oled_rotation_t rotation) {
oled_rotation_t oled_init_keymap(oled_rotation_t rotation) {
    return OLED_ROTATION_270;
}
#endif

M users/drashna/config.h => users/drashna/config.h +129 -38
@@ 17,34 17,49 @@
#pragma once

// Use custom magic number so that when switching branches, EEPROM always gets reset
#define EECONFIG_MAGIC_NUMBER (uint16_t)0x1339
#define EECONFIG_MAGIC_NUMBER   (uint16_t)0x1339

/* Set Polling rate to 1000Hz */
#define USB_POLLING_INTERVAL_MS 1

#if defined(SPLIT_KEYBOARD)
#    define SPLIT_MODS_ENABLE
#    define SPLIT_TRANSPORT_MIRROR
#    define SPLIT_LAYER_STATE_ENABLE
#    define SPLIT_LED_STATE_ENABLE

// #    define SPLIT_TRANSPORT_MIRROR
#    define SERIAL_USE_MULTI_TRANSACTION
// #    define SPLIT_NUM_TRANSACTIONS_KB 2
#    define SPLIT_TRANSACTION_IDS_USER RPC_ID_USER_STATE_SYNC
#endif

#ifdef AUDIO_ENABLE

#    define AUDIO_CLICKY
#    define STARTUP_SONG SONG(RICK_ROLL)
#    define GOODBYE_SONG SONG(SONIC_RING)
#    define DEFAULT_LAYER_SONGS \
        { SONG(QWERTY_SOUND), SONG(COLEMAK_SOUND), SONG(DVORAK_SOUND), SONG(OVERWATCH_THEME) }

#    define AUDIO_CLICKY_FREQ_RANDOMNESS 1.5f

#    define UNICODE_SONG_MAC SONG(RICK_ROLL)
#    define UNICODE_SONG_LNX SONG(RICK_ROLL)
#    define UNICODE_SONG_WIN SONG(RICK_ROLL)
#    define UNICODE_SONG_BSD SONG(RICK_ROLL)
#    define UNICODE_SONG_WINC SONG(RICK_ROLL)
#endif  // !AUDIO_ENABLE
#    ifdef USER_SONG_LIST
#        define STARTUP_SONG SONG(RICK_ROLL)
#        define GOODBYE_SONG SONG(SONIC_RING)
#        define DEFAULT_LAYER_SONGS \
            { SONG(QWERTY_SOUND), SONG(COLEMAK_SOUND), SONG(DVORAK_SOUND), SONG(OVERWATCH_THEME) }
#        define UNICODE_SONG_MAC  SONG(MARIO_THEME)
#        define UNICODE_SONG_LNX  SONG(MARIO_POWERUP)
#        define UNICODE_SONG_WIN  SONG(MARIO_ONEUP)
#        define UNICODE_SONG_BSD  SONG(RICK_ROLL)
#        define UNICODE_SONG_WINC SONG(RICK_ROLL)
#    else
#        define STARTUP_SONG SONG(STARTUP_SOUND)
#        define GOODBYE_SONG SONG(GOODBYE_SOUND)
#        define DEFAULT_LAYER_SONGS \
            { SONG(QWERTY_SOUND), SONG(COLEMAK_SOUND), SONG(DVORAK_SOUND), SONG(WORKMAN_SOUND) }
#        define UNICODE_SONG_MAC  SONG(QWERTY_SOUND)
#        define UNICODE_SONG_LNX  SONG(COLEMAK_SOUND)
#        define UNICODE_SONG_WIN  SONG(DVORAK_SOUND)
#        define UNICODE_SONG_BSD  SONG(WORKMAN_SOUND)
#        define UNICODE_SONG_WINC SONG(PLOVER_GOODBYE_SOUND)
#    endif
#endif // !AUDIO_ENABLE

#define UNICODE_SELECTED_MODES UC_WIN, UC_MAC

#ifdef RGBLIGHT_ENABLE
#    define RGBLIGHT_SLEEP


@@ 56,18 71,16 @@
#    else
#        define RGBLIGHT_ANIMATIONS
#    endif
#    define RGBLIGHT_EFFECT_TWINKLE_LIFE  250
#    define RGBLIGHT_EFFECT_TWINKLE_PROBABILITY 1/24
#endif  // RGBLIGHT_ENABLE
#    define RGBLIGHT_EFFECT_TWINKLE_LIFE        250
#    define RGBLIGHT_EFFECT_TWINKLE_PROBABILITY 1 / 24
#endif // RGBLIGHT_ENABLE

#ifdef RGB_MATRIX_ENABLE
#    define RGB_MATRIX_KEYPRESSES  // reacts to keypresses (will slow down matrix scan by a lot)
#    define RGB_MATRIX_KEYPRESSES // reacts to keypresses (will slow down matrix scan by a lot)
// #   define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (not recommened)
#    define RGB_MATRIX_FRAMEBUFFER_EFFECTS
// #   define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
#    define RGB_DISABLE_WHEN_USB_SUSPENDED  // turn off effects when suspended
// #   define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
// #   define EECONFIG_RGB_MATRIX (uint32_t *)16
// #    define RGB_DISABLE_AFTER_TIMEOUT 0 // number of ticks to wait until disabling effects
// #    define RGB_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended

#    if defined(__AVR__) && !defined(__AVR_AT90USB1286__) && !defined(KEYBOARD_launchpad)
#        define DISABLE_RGB_MATRIX_ALPHAS_MODS


@@ 107,8 120,8 @@
#        define DISABLE_RGB_MATRIX_MULTISPLASH
#        define DISABLE_RGB_MATRIX_SOLID_SPLASH
#        define DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
#    endif  // AVR
#endif      // RGB_MATRIX_ENABLE
#    endif // AVR
#endif     // RGB_MATRIX_ENABLE

#ifdef OLED_DRIVER_ENABLE
#    ifdef SPLIT_KEYBOARD


@@ 120,7 133,7 @@
#    ifdef OLED_FONT_H
#        undef OLED_FONT_H
#    endif
#    define OLED_FONT_H "drashna_font.h"
#    define OLED_FONT_H   "drashna_font.h"
#    define OLED_FONT_END 255
// #    define OLED_FONT_5X5
// #    define OLED_FONT_AZTECH


@@ 135,16 148,16 @@

#ifndef ONESHOT_TAP_TOGGLE
#    define ONESHOT_TAP_TOGGLE 2
#endif  // !ONESHOT_TAP_TOGGLE
#endif // !ONESHOT_TAP_TOGGLE

#ifndef ONESHOT_TIMEOUT
#    define ONESHOT_TIMEOUT 3000
#endif  // !ONESHOT_TIMEOUT
#endif // !ONESHOT_TIMEOUT

#ifdef QMK_KEYS_PER_SCAN
#    undef QMK_KEYS_PER_SCAN
#    define QMK_KEYS_PER_SCAN 2
#endif  // !QMK_KEYS_PER_SCAN
#endif // !QMK_KEYS_PER_SCAN

// this makes it possible to do rolling combos (zx) with keys that
// convert to other keys on hold (z becomes ctrl when you hold it,


@@ 152,8 165,8 @@
// actually sends Ctrl-x. That's bad.)
#define IGNORE_MOD_TAP_INTERRUPT
#undef PERMISSIVE_HOLD
//#define TAPPING_FORCE_HOLD
//#define RETRO_TAPPING
//#define TAPPING_FORCE_HOLD_PER_KEY
//#define RETRO_TAPPING_PER_KEY
#ifndef KEYBOARD_kyria_rev1
#    define TAPPING_TERM_PER_KEY
#endif


@@ 166,7 179,7 @@

#ifdef TAPPING_TERM
#    undef TAPPING_TERM
#endif  // TAPPING_TERM
#endif // TAPPING_TERM
#if defined(KEYBOARD_ergodox_ez)
#    define TAPPING_TERM 185
#elif defined(KEYBOARD_crkbd)


@@ 185,14 198,16 @@
#    undef LOCKING_RESYNC_ENABLE
#endif

#define LAYER_STATE_16BIT

#ifdef CONVERT_TO_PROTON_C
// pins that are available but not present on Pro Micro
#    define A3 PAL_LINE(GPIOA, 3)
#    define A4 PAL_LINE(GPIOA, 4)
#    define A5 PAL_LINE(GPIOA, 5)
#    define A6 PAL_LINE(GPIOA, 6)
#    define A7 PAL_LINE(GPIOA, 7)
#    define A8 PAL_LINE(GPIOA, 8)
#    define A3  PAL_LINE(GPIOA, 3)
#    define A4  PAL_LINE(GPIOA, 4)
#    define A5  PAL_LINE(GPIOA, 5)
#    define A6  PAL_LINE(GPIOA, 6)
#    define A7  PAL_LINE(GPIOA, 7)
#    define A8  PAL_LINE(GPIOA, 8)
#    define A13 PAL_LINE(GPIOA, 13)
#    define A14 PAL_LINE(GPIOA, 14)
#    define A15 PAL_LINE(GPIOA, 15)


@@ 203,3 218,79 @@
#    define C14 PAL_LINE(GPIOC, 14)
#    define C15 PAL_LINE(GPIOC, 15)
#endif

#ifdef MOUSEKEY_ENABLE
// mouse movement config
#    ifdef MK_3_SPEED
#        undef MK_3_SPEED
#    endif
#    define MK_KINETIC_SPEED
#    ifdef MK_KINETIC_SPEED
#        ifndef MOUSEKEY_DELAY
#            define MOUSEKEY_DELAY 8
#        endif
#        ifndef MOUSEKEY_INTERVAL
#            define MOUSEKEY_INTERVAL 20
#        endif
#        ifdef MOUSEKEY_MOVE_DELTA
#            define MOUSEKEY_MOVE_DELTA 25
#        endif
#    else
#        ifndef MOUSEKEY_DELAY
#            define MOUSEKEY_DELAY 300
#        endif
#        ifndef MOUSEKEY_INTERVAL
#            define MOUSEKEY_INTERVAL 50
#        endif
#        ifndef MOUSEKEY_MOVE_DELTA
#            define MOUSEKEY_MOVE_DELTA 5
#        endif
#    endif
#    ifndef MOUSEKEY_MAX_SPEED
#        define MOUSEKEY_MAX_SPEED 7
#    endif
#    ifndef MOUSEKEY_TIME_TO_MAX
#        define MOUSEKEY_TIME_TO_MAX 60
#    endif
#    ifndef MOUSEKEY_INITIAL_SPEED
#        define MOUSEKEY_INITIAL_SPEED 100
#    endif
#    ifndef MOUSEKEY_BASE_SPEED
#        define MOUSEKEY_BASE_SPEED 1000
#    endif
#    ifndef MOUSEKEY_DECELERATED_SPEED
#        define MOUSEKEY_DECELERATED_SPEED 400
#    endif
#    ifndef MOUSEKEY_ACCELERATED_SPEED
#        define MOUSEKEY_ACCELERATED_SPEED 3000
#    endif
// mouse scroll config
#    ifndef MOUSEKEY_WHEEL_DELAY
#        define MOUSEKEY_WHEEL_DELAY 15
#    endif
#    ifndef MOUSEKEY_WHEEL_DELTA
#        define MOUSEKEY_WHEEL_DELTA 1
#    endif
#    ifndef MOUSEKEY_WHEEL_INTERVAL
#        define MOUSEKEY_WHEEL_INTERVAL 50
#    endif
#    ifndef MOUSEKEY_WHEEL_MAX_SPEED
#        define MOUSEKEY_WHEEL_MAX_SPEED 8
#    endif
#    ifndef MOUSEKEY_WHEEL_TIME_TO_MAX
#        define MOUSEKEY_WHEEL_TIME_TO_MAX 80
#    endif
// mouse scroll kinetic config
#    ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS
#        define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 8
#    endif
#    ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS
#        define MOUSEKEY_WHEEL_BASE_MOVEMENTS 48
#    endif
#    ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48
#    endif
#    ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS
#        define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8
#    endif
#endif // MOUSEKEY_ENABLE

M users/drashna/drashna.c => users/drashna/drashna.c +17 -14
@@ 86,6 86,9 @@ void keyboard_post_init_user(void) {
#if defined(RGB_MATRIX_ENABLE)
    keyboard_post_init_rgb_matrix();
#endif
#if defined(SPLIT_KEYBOARD) && defined(SPLIT_TRANSACTION_IDS_USER)
    keyboard_post_init_transport_sync();
#endif
    keyboard_post_init_keymap();
}



@@ 100,12 103,12 @@ void shutdown_user(void) {
    rgblight_enable_noeeprom();
    rgblight_mode_noeeprom(1);
    rgblight_setrgb_red();
#endif  // RGBLIGHT_ENABLE
#endif // RGBLIGHT_ENABLE
#ifdef RGB_MATRIX_ENABLE
    rgb_matrix_set_color_all(0xFF, 0x00, 0x00);
    rgb_matrix_update_pwm_buffers();

#endif  // RGB_MATRIX_ENABLE
#endif // RGB_MATRIX_ENABLE
    shutdown_keymap();
}



@@ 120,7 123,11 @@ void suspend_power_down_user(void) {

__attribute__((weak)) void suspend_wakeup_init_keymap(void) {}

void suspend_wakeup_init_user(void) { suspend_wakeup_init_keymap(); }
void suspend_wakeup_init_user(void) {
    if (layer_state_is(_GAMEPAD)) { layer_off(_GAMEPAD); }
    if (layer_state_is(_DIABLO)) { layer_off(_DIABLO); }
    suspend_wakeup_init_keymap();
}

__attribute__((weak)) void matrix_scan_keymap(void) {}



@@ 135,13 142,13 @@ void matrix_scan_user(void) {
        startup_user();
    }

#ifdef TAP_DANCE_ENABLE  // Run Diablo 3 macro checking code.
#ifdef TAP_DANCE_ENABLE // Run Diablo 3 macro checking code.
    run_diablo_macro_check();
#endif  // TAP_DANCE_ENABLE
#endif // TAP_DANCE_ENABLE

#if defined(RGBLIGHT_ENABLE)
    matrix_scan_rgb_light();
#endif  // RGBLIGHT_ENABLE
#endif // RGBLIGHT_ENABLE
#if defined(RGB_MATRIX_ENABLE)
    matrix_scan_rgb_matrix();
#endif


@@ 160,15 167,13 @@ __attribute__((weak)) layer_state_t layer_state_set_keymap(layer_state_t state) 
// on layer change, no matter where the change was initiated
// Then runs keymap's layer change check
layer_state_t layer_state_set_user(layer_state_t state) {
    if (!is_keyboard_master()) {
        return state;
    }
    if (!is_keyboard_master()) { return state; }

    state = layer_state_set_keymap(state);
    state = update_tri_layer_state(state, _RAISE, _LOWER, _ADJUST);
#if defined(RGBLIGHT_ENABLE)
    state = layer_state_set_rgb_light(state);
#endif  // RGBLIGHT_ENABLE
#endif // RGBLIGHT_ENABLE
#if defined(AUDIO_ENABLE) && !defined(__arm__)
    static bool is_gamepad_on = false;
    if (layer_state_cmp(state, _GAMEPAD) != is_gamepad_on) {


@@ 187,15 192,13 @@ __attribute__((weak)) layer_state_t default_layer_state_set_keymap(layer_state_t

// Runs state check and changes underglow color and animation
layer_state_t default_layer_state_set_user(layer_state_t state) {
    if (!is_keyboard_master()) {
        return state;
    }
    if (!is_keyboard_master()) { return state; }

    state = default_layer_state_set_keymap(state);
#if 0
#    if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
  state = default_layer_state_set_rgb(state);
#    endif  // RGBLIGHT_ENABLE
#    endif // RGBLIGHT_ENABLE
#endif
    return state;
}

M users/drashna/drashna.h => users/drashna/drashna.h +6 -3
@@ 22,7 22,7 @@
#include "process_records.h"
#ifdef TAP_DANCE_ENABLE
#    include "tap_dances.h"
#endif  // TAP_DANCE_ENABLE
#endif // TAP_DANCE_ENABLE
#if defined(RGBLIGHT_ENABLE)
#    include "rgb_stuff.h"
#endif


@@ 35,6 35,9 @@
#if defined(PIMORONI_TRACKBALL_ENABLE)
#    include "drivers/sensors/pimoroni_trackball.h"
#endif
#ifdef SPLIT_KEYBOARD
#    include "transport_sync.h"
#endif

/* Define layer names */
enum userspace_layers {


@@ 98,12 101,12 @@ We use custom codes here, so we can substitute the right stuff
#    define KC_D3_2 TD(TD_D3_2)
#    define KC_D3_3 TD(TD_D3_3)
#    define KC_D3_4 TD(TD_D3_4)
#else  // TAP_DANCE_ENABLE
#else // TAP_DANCE_ENABLE
#    define KC_D3_1 KC_1
#    define KC_D3_2 KC_2
#    define KC_D3_3 KC_3
#    define KC_D3_4 KC_4
#endif  // TAP_DANCE_ENABLE
#endif // TAP_DANCE_ENABLE

#if defined(DRASHNA_CUSTOM_TRANSPORT) && defined(POINTING_DEVICE_ENABLE)
void master_mouse_send(int8_t x, int8_t y);

M users/drashna/drashna_font.h => users/drashna/drashna_font.h +4 -1
@@ 5,8 5,9 @@

#include "progmem.h"

// clang-format off
static const unsigned char font[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00 0
    0x07, 0x08, 0x7F, 0x08, 0x07, 0x00, // 0x00 0
    0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x00, // 0x01 1
    0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x00, // 0x02 2
    0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x00, // 0x03 3 ♥


@@ 1304,3 1305,5 @@ static const unsigned char font[] PROGMEM = {

#endif
  };

// clang-format on

D users/drashna/drashna_transport.c => users/drashna/drashna_transport.c +0 -603
@@ 1,603 0,0 @@
/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * 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 <string.h>
#include <stddef.h>

#include "matrix.h"
#include QMK_KEYBOARD_H

#define ROWS_PER_HAND (MATRIX_ROWS / 2)
#define SYNC_TIMER_OFFSET 2

#ifdef RGBLIGHT_ENABLE
#    include "rgblight.h"
#endif

#ifdef BACKLIGHT_ENABLE
#    include "backlight.h"
#endif

#ifdef ENCODER_ENABLE
#    include "encoder.h"
static pin_t encoders_pad[] = ENCODERS_PAD_A;
#    define NUMBER_OF_ENCODERS (sizeof(encoders_pad) / sizeof(pin_t))
#endif

#ifdef POINTING_DEVICE_ENABLE
static uint16_t device_cpi    = 0;
static int8_t   split_mouse_x = 0, split_mouse_y = 0;
#endif

#ifdef OLED_DRIVER_ENABLE
#    include "oled_driver.h"
#endif

#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
#    include "led_matrix.h"
#endif
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
#    include "rgb_matrix.h"
#endif

#if defined(USE_I2C)

#    include "i2c_master.h"
#    include "i2c_slave.h"

typedef struct _I2C_slave_buffer_t {
#    ifndef DISABLE_SYNC_TIMER
    uint32_t sync_timer;
#    endif
#    ifdef SPLIT_TRANSPORT_MIRROR
    matrix_row_t mmatrix[ROWS_PER_HAND];
#    endif
    matrix_row_t smatrix[ROWS_PER_HAND];
#    ifdef SPLIT_MODS_ENABLE
    uint8_t real_mods;
    uint8_t weak_mods;
#        ifndef NO_ACTION_ONESHOT
    uint8_t oneshot_mods;
#        endif
#    endif
#    ifdef BACKLIGHT_ENABLE
    uint8_t backlight_level;
#    endif
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    rgblight_syncinfo_t rgblight_sync;
#    endif
#    ifdef ENCODER_ENABLE
    uint8_t encoder_state[NUMBER_OF_ENCODERS];
#    endif
#    ifdef WPM_ENABLE
    uint8_t current_wpm;
#    endif
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_eeconfig_t led_matrix;
    bool           led_suspend_state;
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_config_t rgb_matrix;
    bool         rgb_suspend_state;
#    endif
    int8_t        mouse_x;
    int8_t        mouse_y;
    uint16_t      device_cpi;
    bool          oled_on;
    layer_state_t t_layer_state;
    layer_state_t t_default_layer_state;
} __attribute__((packed)) I2C_slave_buffer_t;

static I2C_slave_buffer_t *const i2c_buffer = (I2C_slave_buffer_t *)i2c_slave_reg;

#    define I2C_BACKLIGHT_START offsetof(I2C_slave_buffer_t, backlight_level)
#    define I2C_RGB_START offsetof(I2C_slave_buffer_t, rgblight_sync)
#    define I2C_KEYMAP_MASTER_START offsetof(I2C_slave_buffer_t, mmatrix)
#    define I2C_KEYMAP_SLAVE_START offsetof(I2C_slave_buffer_t, smatrix)
#    define I2C_SYNC_TIME_START offsetof(I2C_slave_buffer_t, sync_timer)
#    define I2C_REAL_MODS_START offsetof(I2C_slave_buffer_t, real_mods)
#    define I2C_WEAK_MODS_START offsetof(I2C_slave_buffer_t, weak_mods)
#    define I2C_ONESHOT_MODS_START offsetof(I2C_slave_buffer_t, oneshot_mods)
#    define I2C_ENCODER_START offsetof(I2C_slave_buffer_t, encoder_state)
#    define I2C_WPM_START offsetof(I2C_slave_buffer_t, current_wpm)
#    define I2C_MOUSE_X_START offsetof(I2C_slave_buffer_t, mouse_x)
#    define I2C_MOUSE_Y_START offsetof(I2C_slave_buffer_t, mouse_y)
#    define I2C_MOUSE_DPI_START offsetof(I2C_slave_buffer_t, device_cpi)
#    define I2C_OLED_ON_START offsetof(I2C_slave_buffer_t, oled_on)
#    define I2C_LAYER_STATE_START offsetof(I2C_slave_buffer_t, t_layer_state)
#    define I2C_DEFAULT_LAYER_STATE_START offsetof(I2C_slave_buffer_t, t_default_layer_state)
#    define I2C_LED_MATRIX_START offsetof(I2C_slave_buffer_t, led_matrix)
#    define I2C_LED_SUSPEND_START offsetof(I2C_slave_buffer_t, led_suspend_state)
#    define I2C_RGB_MATRIX_START offsetof(I2C_slave_buffer_t, rgb_matrix)
#    define I2C_RGB_SUSPEND_START offsetof(I2C_slave_buffer_t, rgb_suspend_state)

#    define TIMEOUT 100

#    ifndef SLAVE_I2C_ADDRESS
#        define SLAVE_I2C_ADDRESS 0x32
#    endif

// Get rows from other half over i2c
bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_SLAVE_START, (void *)slave_matrix, sizeof(i2c_buffer->smatrix), TIMEOUT);
#    ifdef SPLIT_TRANSPORT_MIRROR
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_KEYMAP_MASTER_START, (void *)master_matrix, sizeof(i2c_buffer->mmatrix), TIMEOUT);
#    endif
    // write backlight info
#    ifdef BACKLIGHT_ENABLE
    uint8_t level = is_backlight_enabled() ? get_backlight_level() : 0;
    if (level != i2c_buffer->backlight_level) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_BACKLIGHT_START, (void *)&level, sizeof(level), TIMEOUT) >= 0) {
            i2c_buffer->backlight_level = level;
        }
    }
#    endif

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    if (rgblight_get_change_flags()) {
        rgblight_syncinfo_t rgblight_sync;
        rgblight_get_syncinfo(&rgblight_sync);
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_START, (void *)&rgblight_sync, sizeof(rgblight_sync), TIMEOUT) >= 0) {
            rgblight_clear_change_flags();
        }
    }
#    endif

#    ifdef ENCODER_ENABLE
    i2c_readReg(SLAVE_I2C_ADDRESS, I2C_ENCODER_START, (void *)i2c_buffer->encoder_state, sizeof(i2c_buffer->encoder_state), TIMEOUT);
    encoder_update_raw(i2c_buffer->encoder_state);
#    endif

#    ifdef WPM_ENABLE
    uint8_t current_wpm = get_current_wpm();
    if (current_wpm != i2c_buffer->current_wpm) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WPM_START, (void *)&current_wpm, sizeof(current_wpm), TIMEOUT) >= 0) {
            i2c_buffer->current_wpm = current_wpm;
        }
    }
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_left()) {
        report_mouse_t temp_report = pointing_device_get_report();
        i2c_readReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_X_START, (void *)&i2c_buffer->mouse_x, sizeof(i2c_buffer->mouse_x), TIMEOUT);
        temp_report.x = i2c_buffer->mouse_x;
        i2c_readReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_Y_START, (void *)&i2c_buffer->mouse_y, sizeof(i2c_buffer->mouse_y), TIMEOUT);
        temp_report.y = i2c_buffer->mouse_y;
        pointing_device_set_report(temp_report);

        if (device_cpi != i2c_buffer->device_cpi) {
            if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_DPI_START, (void *)&device_cpi, sizeof(device_cpi), TIMEOUT) >= 0) {
                i2c_buffer->device_cpi = device_cpi
            }
        }
    }
#    endif

#    ifdef SPLIT_MODS_ENABLE
    uint8_t real_mods = get_mods();
    if (real_mods != i2c_buffer->real_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_REAL_MODS_START, (void *)&real_mods, sizeof(real_mods), TIMEOUT) >= 0) {
            i2c_buffer->real_mods = real_mods;
        }
    }

    uint8_t weak_mods = get_weak_mods();
    if (weak_mods != i2c_buffer->weak_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_WEAK_MODS_START, (void *)&weak_mods, sizeof(weak_mods), TIMEOUT) >= 0) {
            i2c_buffer->weak_mods = weak_mods;
        }
    }

#        ifndef NO_ACTION_ONESHOT
    uint8_t oneshot_mods = get_oneshot_mods();
    if (oneshot_mods != i2c_buffer->oneshot_mods) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_ONESHOT_MODS_START, (void *)&oneshot_mods, sizeof(oneshot_mods), TIMEOUT) >= 0) {
            i2c_buffer->oneshot_mods = oneshot_mods;
        }
    }
#        endif
#    endif

    if (layer_state != i2c_buffer->t_layer_state) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LAYER_STATE_START, (void *)&layer_state, sizeof(layer_state), TIMEOUT) >= 0) {
            i2c_buffer->t_layer_state = layer_state;
        }
    }

    if (default_layer_state != i2c_buffer->t_default_layer_state) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_DEFAULT_LAYER_STATE_START, (void *)&default_layer_state, sizeof(default_layer_state), TIMEOUT) >= 0) {
            i2c_buffer->t_default_layer_state = default_layer_state;
        }
    }

#    ifdef OLED_DRIVER_ENABLE
    bool is_oled_on = is_oled_on();
    if (is_oled_on != i2c_buffer->oled_on) {
        if (i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LAYER_STATE_START, (void *)&is_oled_on, sizeof(is_oled_on), TIMEOUT) >= 0) {
            i2c_buffer->oled_on = is_oled_on;
        }
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_MATRIX_START, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix), TIMEOUT);
    bool suspend_state = led_matrix_get_suspend_state();
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_LED_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->led_suspend_state), TIMEOUT);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_MATRIX_START, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix), TIMEOUT);
    bool suspend_state = rgb_matrix_get_suspend_state();
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_RGB_SUSPEND_START, (void *)suspend_state, sizeof(i2c_buffer->rgb_suspend_state), TIMEOUT);
#    endif

#    ifndef DISABLE_SYNC_TIMER
    i2c_buffer->sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;
    i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_SYNC_TIME_START, (void *)&i2c_buffer->sync_timer, sizeof(i2c_buffer->sync_timer), TIMEOUT);
#    endif

    return true;
}

void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
#    ifndef DISABLE_SYNC_TIMER
    sync_timer_update(i2c_buffer->sync_timer);
#    endif
    // Copy matrix to I2C buffer
    memcpy((void *)i2c_buffer->smatrix, (void *)slave_matrix, sizeof(i2c_buffer->smatrix));
#    ifdef SPLIT_TRANSPORT_MIRROR
    memcpy((void *)master_matrix, (void *)i2c_buffer->mmatrix, sizeof(i2c_buffer->mmatrix));
#    endif

// Read Backlight Info
#    ifdef BACKLIGHT_ENABLE
    backlight_set(i2c_buffer->backlight_level);
#    endif

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    // Update the RGB with the new data
    if (i2c_buffer->rgblight_sync.status.change_flags != 0) {
        rgblight_update_sync(&i2c_buffer->rgblight_sync, false);
        i2c_buffer->rgblight_sync.status.change_flags = 0;
    }
#    endif

#    ifdef ENCODER_ENABLE
    encoder_state_raw(i2c_buffer->encoder_state);
#    endif

#    ifdef WPM_ENABLE
    set_current_wpm(i2c_buffer->current_wpm);
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (!is_keyboard_left()) {
        static uint16_t cpi;
        if (cpi != i2c_buffer->device_cpi) {
            cpi = i2c_buffer->device_cpi;
            pmw_set_cpi(cpi);
        }
        i2c_buffer->mouse_x = split_mouse_x;
        i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_X_START, (void *)&i2c_buffer->mouse_x, sizeof(i2c_buffer->mouse_x), TIMEOUT);
        i2c_buffer->mouse_y = split_mouse_y;
        i2c_writeReg(SLAVE_I2C_ADDRESS, I2C_MOUSE_Y_START, (void *)&i2c_buffer->mouse_y, sizeof(i2c_buffer->mouse_y), TIMEOUT);
    }

#    endif

#    ifdef SPLIT_MODS_ENABLE
    set_mods(i2c_buffer->real_mods);
    set_weak_mods(i2c_buffer->weak_mods);
#        ifndef NO_ACTION_ONESHOT
    set_oneshot_mods(i2c_buffer->oneshot_mods);
#        endif
#    endif

    if (layer_state != i2c_buffer->t_layer_state) {
        layer_state = i2c_buffer->t_layer_state;
    }
    if (default_layer_state != i2c_buffer->t_default_layer_state) {
        default_layer_state = i2c_buffer->t_default_layer_state;
    }

#    ifdef OLED_DRIVER_ENABLE
    if (i2c_buffer->oled_on) {
        oled_on();
    } else {
        oled_off();
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    memcpy((void *)i2c_buffer->led_matrix, (void *)led_matrix_eeconfig, sizeof(i2c_buffer->led_matrix));
    led_matrix_set_suspend_state(i2c_buffer->led_suspend_state);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    memcpy((void *)i2c_buffer->rgb_matrix, (void *)rgb_matrix_config, sizeof(i2c_buffer->rgb_matrix));
    rgb_matrix_set_suspend_state(i2c_buffer->rgb_suspend_state);
#    endif
}

void transport_master_init(void) { i2c_init(); }

void transport_slave_init(void) { i2c_slave_init(SLAVE_I2C_ADDRESS); }

#else  // USE_SERIAL

#    include "serial.h"

typedef struct _Serial_s2m_buffer_t {
    // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
    matrix_row_t smatrix[ROWS_PER_HAND];
#    ifdef ENCODER_ENABLE
    uint8_t      encoder_state[NUMBER_OF_ENCODERS];
#    endif
    int8_t       mouse_x;
    int8_t       mouse_y;
} __attribute__((packed)) Serial_s2m_buffer_t;

typedef struct _Serial_m2s_buffer_t {
#    ifdef SPLIT_MODS_ENABLE
    uint8_t       real_mods;
    uint8_t       weak_mods;
#        ifndef NO_ACTION_ONESHOT
    uint8_t       oneshot_mods;
#        endif
#    endif
#    ifndef DISABLE_SYNC_TIMER
    uint32_t     sync_timer;
#    endif
#    ifdef SPLIT_TRANSPORT_MIRROR
    matrix_row_t mmatrix[ROWS_PER_HAND];
#    endif
#    ifdef BACKLIGHT_ENABLE
    uint8_t       backlight_level;
#    endif
#    ifdef WPM_ENABLE
    uint8_t       current_wpm;
#    endif
    uint16_t      device_cpi;
    bool          oled_on;
    layer_state_t t_layer_state;
    layer_state_t t_default_layer_state;
#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_eeconfig_t led_matrix;
    bool           led_suspend_state;
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_config_t   rgb_matrix;
    bool           rgb_suspend_state;
#    endif
} __attribute__((packed)) Serial_m2s_buffer_t;

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
// When MCUs on both sides drive their respective RGB LED chains,
// it is necessary to synchronize, so it is necessary to communicate RGB
// information. In that case, define RGBLIGHT_SPLIT with info on the number
// of LEDs on each half.
//
// Otherwise, if the master side MCU drives both sides RGB LED chains,
// there is no need to communicate.

typedef struct _Serial_rgblight_t {
    rgblight_syncinfo_t rgblight_sync;
} Serial_rgblight_t;

volatile Serial_rgblight_t serial_rgblight = {};
uint8_t volatile status_rgblight           = 0;
#    endif

volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
uint8_t volatile status0                       = 0;

enum serial_transaction_id {
    GET_SLAVE_MATRIX = 0,
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    PUT_RGBLIGHT,
#    endif
};

SSTD_t transactions[] = {
    [GET_SLAVE_MATRIX] =
        {
            (uint8_t *)&status0,
            sizeof(serial_m2s_buffer),
            (uint8_t *)&serial_m2s_buffer,
            sizeof(serial_s2m_buffer),
            (uint8_t *)&serial_s2m_buffer,
        },
#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
    [PUT_RGBLIGHT] =
        {
            (uint8_t *)&status_rgblight, sizeof(serial_rgblight), (uint8_t *)&serial_rgblight, 0, NULL  // no slave to master transfer
        },
#    endif
};

void transport_master_init(void) { soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }

void transport_slave_init(void) { soft_serial_target_init(transactions, TID_LIMIT(transactions)); }

#    if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)

// rgblight synchronization information communication.

void transport_rgblight_master(void) {
    if (rgblight_get_change_flags()) {
        rgblight_get_syncinfo((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync);
        if (soft_serial_transaction(PUT_RGBLIGHT) == TRANSACTION_END) {
            rgblight_clear_change_flags();
        }
    }
}

void transport_rgblight_slave(void) {
    if (status_rgblight == TRANSACTION_ACCEPTED) {
        rgblight_update_sync((rgblight_syncinfo_t *)&serial_rgblight.rgblight_sync, false);
        status_rgblight = TRANSACTION_END;
    }
}

#    else
#        define transport_rgblight_master()
#        define transport_rgblight_slave()
#    endif

bool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
#    ifndef SERIAL_USE_MULTI_TRANSACTION
    if (soft_serial_transaction() != TRANSACTION_END) {
        return false;
    }
#    else
    transport_rgblight_master();
    if (soft_serial_transaction(GET_SLAVE_MATRIX) != TRANSACTION_END) {
        return false;
    }
#    endif

    // TODO:  if MATRIX_COLS > 8 change to unpack()
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        slave_matrix[i] = serial_s2m_buffer.smatrix[i];
#    ifdef SPLIT_TRANSPORT_MIRROR
        serial_m2s_buffer.mmatrix[i] = master_matrix[i];
#    endif
    }

#    ifdef BACKLIGHT_ENABLE
    // Write backlight level for slave to read
    serial_m2s_buffer.backlight_level = is_backlight_enabled() ? get_backlight_level() : 0;
#    endif

#    ifdef ENCODER_ENABLE
    encoder_update_raw((uint8_t *)serial_s2m_buffer.encoder_state);
#    endif

#    ifdef WPM_ENABLE
    // Write wpm to slave
    serial_m2s_buffer.current_wpm  = get_current_wpm();
#    endif

#    ifdef SPLIT_MODS_ENABLE
    serial_m2s_buffer.real_mods    = get_mods();
    serial_m2s_buffer.weak_mods    = get_weak_mods();
#        ifndef NO_ACTION_ONESHOT
    serial_m2s_buffer.oneshot_mods = get_oneshot_mods();
#        endif
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (is_keyboard_left()) {
        report_mouse_t temp_report = pointing_device_get_report();
        temp_report.x              = serial_s2m_buffer.mouse_x;
        temp_report.y              = serial_s2m_buffer.mouse_y;
        pointing_device_set_report(temp_report);
        serial_m2s_buffer.device_cpi = device_cpi;
    }
#    endif

    serial_m2s_buffer.t_layer_state           = layer_state;
    serial_m2s_buffer.t_default_layer_state   = default_layer_state;
#    ifdef OLED_DRIVER_ENABLE
    serial_m2s_buffer.oled_on                 = is_oled_on();
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    serial_m2s_buffer.led_matrix        = led_matrix_eeconfig;
    serial_m2s_buffer.led_suspend_state = led_matrix_get_suspend_state();
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    serial_m2s_buffer.rgb_matrix        = rgb_matrix_config;
    serial_m2s_buffer.rgb_suspend_state = rgb_matrix_get_suspend_state();
#    endif

#    ifndef DISABLE_SYNC_TIMER
    serial_m2s_buffer.sync_timer   = sync_timer_read32() + SYNC_TIMER_OFFSET;
#    endif
    return true;
}

void transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {
    transport_rgblight_slave();
#    ifndef DISABLE_SYNC_TIMER
    sync_timer_update(serial_m2s_buffer.sync_timer);
#    endif

    // TODO: if MATRIX_COLS > 8 change to pack()
    for (int i = 0; i < ROWS_PER_HAND; ++i) {
        serial_s2m_buffer.smatrix[i] = slave_matrix[i];
#    ifdef SPLIT_TRANSPORT_MIRROR
        master_matrix[i]             = serial_m2s_buffer.mmatrix[i];
#    endif
    }

#    ifdef BACKLIGHT_ENABLE
    backlight_set(serial_m2s_buffer.backlight_level);
#    endif

#    ifdef ENCODER_ENABLE
    encoder_state_raw((uint8_t *)serial_s2m_buffer.encoder_state);
#    endif

#    ifdef WPM_ENABLE
    set_current_wpm(serial_m2s_buffer.current_wpm);
#    endif

#    ifdef SPLIT_MODS_ENABLE
    set_mods(serial_m2s_buffer.real_mods);
    set_weak_mods(serial_m2s_buffer.weak_mods);
#        ifndef NO_ACTION_ONESHOT
    set_oneshot_mods(serial_m2s_buffer.oneshot_mods);
#        endif
#    endif

#    ifdef POINTING_DEVICE_ENABLE
    if (!is_keyboard_left()) {
        static uint16_t cpi;
        if (cpi != serial_m2s_buffer.device_cpi) {
            cpi = serial_m2s_buffer.device_cpi;
            pmw_set_cpi(cpi);
        }
        serial_s2m_buffer.mouse_x = split_mouse_x;
        serial_s2m_buffer.mouse_y = split_mouse_y;
    }
#    endif

    if (layer_state != serial_m2s_buffer.t_layer_state) {
        layer_state = serial_m2s_buffer.t_layer_state;
    }
    if (default_layer_state != serial_m2s_buffer.t_default_layer_state) {
        default_layer_state = serial_m2s_buffer.t_default_layer_state;
    }
#    ifdef OLED_DRIVER_ENABLE
    if (serial_m2s_buffer.oled_on) {
        oled_on();
    } else {
        oled_off();
    }
#    endif

#    if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)
    led_matrix_eeconfig = serial_m2s_buffer.led_matrix;
    led_matrix_set_suspend_state(serial_m2s_buffer.led_suspend_state);
#    endif
#    if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)
    rgb_matrix_config = serial_m2s_buffer.rgb_matrix;
    rgb_matrix_set_suspend_state(serial_m2s_buffer.rgb_suspend_state);
#    endif
}

#endif

M users/drashna/oled_stuff.c => users/drashna/oled_stuff.c +40 -43
@@ 25,7 25,7 @@
#endif

uint32_t        oled_timer                       = 0;
static char     keylog_str[KEYLOGGER_LENGTH + 1] = {"\n"};
static char     keylog_str[KEYLOGGER_LENGTH + 1] = { 0 };
static uint16_t log_timer                        = 0;

// clang-format off


@@ 57,12 57,12 @@ void add_keylog(uint16_t keycode) {
        keycode = 0;
    }

    for (uint8_t i = (KEYLOGGER_LENGTH - 1); i > 0; --i) {
        keylog_str[i] = keylog_str[i - 1];
    for (uint8_t i = 1; i < KEYLOGGER_LENGTH; i++) {
        keylog_str[i-1] = keylog_str[i];
    }

    if (keycode < (sizeof(code_to_name) / sizeof(char))) {
        keylog_str[0] = pgm_read_byte(&code_to_name[keycode]);
        keylog_str[(KEYLOGGER_LENGTH - 1)] = pgm_read_byte(&code_to_name[keycode]);
    }

    log_timer = timer_read();


@@ 92,30 92,14 @@ void render_keylogger_status(void) {
void render_default_layer_state(void) {
    oled_write_P(PSTR(OLED_RENDER_LAYOUT_NAME), false);
    switch (get_highest_layer(default_layer_state)) {
        case _QWERTY:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_QWERTY), false);
            break;
        case _COLEMAK:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_COLEMAK), false);
            break;
        case _DVORAK:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_DVORAK), false);
            break;
        case _WORKMAN:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_WORKMAN), false);
            break;
        case _NORMAN:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_NORMAN), false);
            break;
        case _MALTRON:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_MALTRON), false);
            break;
        case _EUCALYN:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_EUCALYN), false);
            break;
        case _CARPLAX:
            oled_write_P(PSTR(OLED_RENDER_LAYOUT_CARPLAX), false);
            break;
        case _QWERTY: oled_write_P(PSTR(OLED_RENDER_LAYOUT_QWERTY), false); break;
        case _COLEMAK: oled_write_P(PSTR(OLED_RENDER_LAYOUT_COLEMAK), false); break;
        case _DVORAK: oled_write_P(PSTR(OLED_RENDER_LAYOUT_DVORAK), false); break;
        case _WORKMAN: oled_write_P(PSTR(OLED_RENDER_LAYOUT_WORKMAN), false); break;
        case _NORMAN: oled_write_P(PSTR(OLED_RENDER_LAYOUT_NORMAN), false); break;
        case _MALTRON: oled_write_P(PSTR(OLED_RENDER_LAYOUT_MALTRON), false); break;
        case _EUCALYN: oled_write_P(PSTR(OLED_RENDER_LAYOUT_EUCALYN), false); break;
        case _CARPLAX: oled_write_P(PSTR(OLED_RENDER_LAYOUT_CARPLAX), false); break;
    }
#ifdef OLED_DISPLAY_128X64
    oled_advance_page(true);


@@ 159,12 143,12 @@ void render_keylock_status(uint8_t led_usb_state) {
}
void render_matrix_scan_rate(void) {
#ifdef DEBUG_MATRIX_SCAN_RATE
    char matrix_rate[5];
    uint16_t n = get_matrix_scan_rate();
    char     matrix_rate[5];
    uint16_t n     = get_matrix_scan_rate();
    matrix_rate[4] = '\0';
    matrix_rate[3] = '0' + n % 10;
    matrix_rate[2] = ( n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    matrix_rate[1] =  n / 10 ? '0' + n / 10 : ' ';
    matrix_rate[2] = (n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    matrix_rate[1] = n / 10 ? '0' + n / 10 : ' ';
    matrix_rate[0] = ' ';
    oled_write_P(PSTR("MS:"), false);
    oled_write(matrix_rate, false);


@@ 284,6 268,10 @@ void render_user_status(void) {
    oled_write_P(rgb_layer_status[userspace_config.rgb_layer_change], false);
    static const char PROGMEM nukem_good[2][3] = {{0xF8, 0xF9, 0}, {0xF6, 0xF7, 0}};
    oled_write_P(nukem_good[0], userspace_config.nuke_switch);
#if defined(UNICODE_ENABLE)
    static const char PROGMEM uc_mod_status[5][3] = {{0xEA, 0xEB, 0}, {0xEC, 0xED, 0}};
    oled_write_P(uc_mod_status[get_unicode_input_mode() == UC_MAC], false);
#endif
#if defined(OLED_DISPLAY_128X64)
    oled_advance_page(true);
#endif


@@ 306,34 294,34 @@ void render_wpm(void) {
    char wpm_counter[4];
    wpm_counter[3] = '\0';
    wpm_counter[2] = '0' + n % 10;
    wpm_counter[1] = ( n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    wpm_counter[1] = (n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    wpm_counter[0] = n / 10 ? '0' + n / 10 : ' ';
#    else
    char wpm_counter[6];
    wpm_counter[5] = '\0';
    wpm_counter[4] = '0' + n % 10;
    wpm_counter[3] = ( n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    wpm_counter[3] = (n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    wpm_counter[2] = n / 10 ? '0' + n / 10 : ' ';
    wpm_counter[1] = ' ';
    wpm_counter[0] = ' ';
    #    endif
#    endif
    oled_write_P(PSTR(OLED_RENDER_WPM_COUNTER), false);
    oled_write(wpm_counter, false);
#endif
}

#ifdef KEYBOARD_handwired_dactyl_manuform_5x6_right_trackball
#if defined(KEYBOARD_handwired_tractyl_manuform_5x6_right)
extern keyboard_config_t keyboard_config;
extern uint16_t          dpi_array[];

void render_pointing_dpi_status(void) {
    char dpi_status[6];
    uint16_t n = dpi_array[keyboard_config.dpi_config];
    char     dpi_status[6];
    uint16_t n    = dpi_array[keyboard_config.dpi_config];
    dpi_status[5] = '\0';
    dpi_status[4] = '0' + n % 10;
    dpi_status[3] = ( n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    dpi_status[2] = ( n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    dpi_status[1] =  n / 10 ? '0' + n / 10 : ' ';
    dpi_status[3] = (n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    dpi_status[2] = (n /= 10) % 10 ? '0' + (n) % 10 : (n / 10) % 10 ? '0' : ' ';
    dpi_status[1] = n / 10 ? '0' + n / 10 : ' ';
    dpi_status[0] = ' ';
    oled_write_P(PSTR("  DPI: "), false);
    oled_write(dpi_status, false);


@@ 360,7 348,7 @@ void render_status_main(void) {
#    else
    render_wpm();
#    endif
#    ifdef KEYBOARD_handwired_dactyl_manuform_5x6_right_trackball
#    if defined(KEYBOARD_handwired_tractyl_manuform_5x6_right)
    render_pointing_dpi_status();
#    endif
    oled_write_P(PSTR("\n"), false);


@@ 375,6 363,15 @@ void render_status_main(void) {
    render_keylogger_status();
}

__attribute__((weak)) oled_rotation_t oled_init_keymap(oled_rotation_t rotation) { return rotation; }

oled_rotation_t oled_init_user(oled_rotation_t rotation) {
    for (uint8_t i = 0; i < (KEYLOGGER_LENGTH - 1); i++) {
        add_keylog(0);
    }
    return oled_init_keymap(rotation);
}

void oled_task_user(void) {
    update_log();



@@ 385,7 382,7 @@ void oled_task_user(void) {
        } else {
            oled_on();
        }
        render_status_main();  // Renders the current keyboard state (layer, lock, caps, scroll, etc)
        render_status_main(); // Renders the current keyboard state (layer, lock, caps, scroll, etc)
    } else {
        render_status_secondary();
    }

M users/drashna/oled_stuff.h => users/drashna/oled_stuff.h +83 -82
@@ 19,97 19,98 @@
#include "quantum.h"
#include "oled_driver.h"

void oled_driver_render_logo(void);
bool process_record_user_oled(uint16_t keycode, keyrecord_t *record);
void            oled_driver_render_logo(void);
bool            process_record_user_oled(uint16_t keycode, keyrecord_t *record);
oled_rotation_t oled_init_keymap(oled_rotation_t rotation);
extern uint32_t oled_timer;

#ifdef OLED_DISPLAY_128X64
#    define OLED_RENDER_KEYLOGGER "Keylogger: "

#    define OLED_RENDER_LAYOUT_NAME    "Layout: "
#    define OLED_RENDER_LAYOUT_QWERTY  "Qwerty"
#    define OLED_RENDER_LAYOUT_COLEMAK "Colemak"
#    define OLED_RENDER_LAYOUT_DVORAK  "Dvorak"
#    define OLED_RENDER_LAYOUT_WORKMAN "Workman"
#    define OLED_RENDER_LAYOUT_NORMAN  "Norman"
#    define OLED_RENDER_LAYOUT_MALTRON "Matron"
#    define OLED_RENDER_LAYOUT_EUCALYN "Eucalyn"
#    define OLED_RENDER_LAYOUT_CARPLAX "Carplax"

#    define OLED_RENDER_LAYER_NAME   "Layer:"
#    define OLED_RENDER_LAYER_LOWER  "Lower"
#    define OLED_RENDER_LAYER_RAISE  "Raise"
#    define OLED_RENDER_LAYER_ADJUST "Adjust"
#    define OLED_RENDER_LAYER_MODS   "Mods"

#    define OLED_RENDER_LOCK_NAME "Lock: "
#    define OLED_RENDER_LOCK_NUML "NUML"
#    define OLED_RENDER_LOCK_CAPS "CAPS"
#    define OLED_RENDER_LOCK_SCLK "SCLK"

#    define OLED_RENDER_MODS_NAME "Mods:"
#    define OLED_RENDER_MODS_SFT  "Sft"
#    define OLED_RENDER_MODS_CTL  "Ctl"
#    define OLED_RENDER_MODS_ALT  "Alt"
#    define OLED_RENDER_MODS_GUI  "GUI"

#    define OLED_RENDER_BOOTMAGIC_NAME  "Boot  "
#    define OLED_RENDER_BOOTMAGIC_NKRO  "NKRO"
#    define OLED_RENDER_BOOTMAGIC_NOGUI "nGUI"
#    define OLED_RENDER_BOOTMAGIC_GRV   "GRV"
#    define OLED_RENDER_KEYLOGGER         "Keylogger: "

#    define OLED_RENDER_LAYOUT_NAME       "Layout: "
#    define OLED_RENDER_LAYOUT_QWERTY     "Qwerty"
#    define OLED_RENDER_LAYOUT_COLEMAK    "Colemak"
#    define OLED_RENDER_LAYOUT_DVORAK     "Dvorak"
#    define OLED_RENDER_LAYOUT_WORKMAN    "Workman"
#    define OLED_RENDER_LAYOUT_NORMAN     "Norman"
#    define OLED_RENDER_LAYOUT_MALTRON    "Matron"
#    define OLED_RENDER_LAYOUT_EUCALYN    "Eucalyn"
#    define OLED_RENDER_LAYOUT_CARPLAX    "Carplax"

#    define OLED_RENDER_LAYER_NAME        "Layer:"
#    define OLED_RENDER_LAYER_LOWER       "Lower"
#    define OLED_RENDER_LAYER_RAISE       "Raise"
#    define OLED_RENDER_LAYER_ADJUST      "Adjust"
#    define OLED_RENDER_LAYER_MODS        "Mods"

#    define OLED_RENDER_LOCK_NAME         "Lock: "
#    define OLED_RENDER_LOCK_NUML         "NUML"
#    define OLED_RENDER_LOCK_CAPS         "CAPS"
#    define OLED_RENDER_LOCK_SCLK         "SCLK"

#    define OLED_RENDER_MODS_NAME         "Mods:"
#    define OLED_RENDER_MODS_SFT          "Sft"
#    define OLED_RENDER_MODS_CTL          "Ctl"
#    define OLED_RENDER_MODS_ALT          "Alt"
#    define OLED_RENDER_MODS_GUI          "GUI"

#    define OLED_RENDER_BOOTMAGIC_NAME    "Boot  "
#    define OLED_RENDER_BOOTMAGIC_NKRO    "NKRO"
#    define OLED_RENDER_BOOTMAGIC_NOGUI   "nGUI"
#    define OLED_RENDER_BOOTMAGIC_GRV     "GRV"
#    define OLED_RENDER_BOOTMAGIC_ONESHOT "1SHT"
#    define OLED_RENDER_BOOTMAGIC_SWAP "SWAP"
#    define OLED_RENDER_BOOTMAGIC_CAPS  "CAPS"
#    define OLED_RENDER_BOOTMAGIC_SWAP    "SWAP"
#    define OLED_RENDER_BOOTMAGIC_CAPS    "CAPS"

#    define OLED_RENDER_USER_NAME "USER:"
#    define OLED_RENDER_USER_ANIM "Anim"
#    define OLED_RENDER_USER_LAYR "Layr"
#    define OLED_RENDER_USER_NUKE "Nuke"
#    define OLED_RENDER_USER_NAME         "USER:"
#    define OLED_RENDER_USER_ANIM         "Anim"
#    define OLED_RENDER_USER_LAYR         "Layr"
#    define OLED_RENDER_USER_NUKE         "Nuke"

#    define OLED_RENDER_WPM_COUNTER "WPM: "
#    define OLED_RENDER_WPM_COUNTER       "WPM: "
#else
#    define OLED_RENDER_KEYLOGGER "KLogr"

#    define OLED_RENDER_LAYOUT_NAME    "Lyout"
#    define OLED_RENDER_LAYOUT_QWERTY  " QRTY"
#    define OLED_RENDER_LAYOUT_COLEMAK " COLE"
#    define OLED_RENDER_LAYOUT_DVORAK  " DVRK"
#    define OLED_RENDER_LAYOUT_WORKMAN " WKMN"
#    define OLED_RENDER_LAYOUT_NORMAN  " NORM"
#    define OLED_RENDER_LAYOUT_MALTRON " MLTN"
#    define OLED_RENDER_LAYOUT_EUCALYN " ECLN"
#    define OLED_RENDER_LAYOUT_CARPLAX " CRPX"

#    define OLED_RENDER_LAYER_NAME   "LAYER"
#    define OLED_RENDER_LAYER_LOWER  "Lower"
#    define OLED_RENDER_LAYER_RAISE  "Raise"
#    define OLED_RENDER_LAYER_ADJUST "Adjst"
#    define OLED_RENDER_LAYER_MODS   " Mods"

#    define OLED_RENDER_LOCK_NAME "Lock:"
#    define OLED_RENDER_LOCK_NUML "NumL"
#    define OLED_RENDER_LOCK_CAPS "CapL"
#    define OLED_RENDER_LOCK_SCLK "ScrL"

#    define OLED_RENDER_MODS_NAME "Mods: "
#    define OLED_RENDER_MODS_SFT  "Shft"
#    define OLED_RENDER_MODS_CTL  "Ctrl"
#    define OLED_RENDER_MODS_ALT  "Alt\n"
#    define OLED_RENDER_MODS_GUI  "GUI\n"

#    define OLED_RENDER_BOOTMAGIC_NAME  "BTMGK"
#    define OLED_RENDER_BOOTMAGIC_NKRO  "NKRO"
#    define OLED_RENDER_BOOTMAGIC_NOGUI "nGUI"
#    define OLED_RENDER_BOOTMAGIC_GRV   "GRV"
#    define OLED_RENDER_KEYLOGGER         "KLogr"

#    define OLED_RENDER_LAYOUT_NAME       "Lyout"
#    define OLED_RENDER_LAYOUT_QWERTY     " QRTY"
#    define OLED_RENDER_LAYOUT_COLEMAK    " COLE"
#    define OLED_RENDER_LAYOUT_DVORAK     " DVRK"
#    define OLED_RENDER_LAYOUT_WORKMAN    " WKMN"
#    define OLED_RENDER_LAYOUT_NORMAN     " NORM"
#    define OLED_RENDER_LAYOUT_MALTRON    " MLTN"
#    define OLED_RENDER_LAYOUT_EUCALYN    " ECLN"
#    define OLED_RENDER_LAYOUT_CARPLAX    " CRPX"

#    define OLED_RENDER_LAYER_NAME        "LAYER"
#    define OLED_RENDER_LAYER_LOWER       "Lower"
#    define OLED_RENDER_LAYER_RAISE       "Raise"
#    define OLED_RENDER_LAYER_ADJUST      "Adjst"
#    define OLED_RENDER_LAYER_MODS        " Mods"

#    define OLED_RENDER_LOCK_NAME         "Lock:"
#    define OLED_RENDER_LOCK_NUML         "NumL"
#    define OLED_RENDER_LOCK_CAPS         "CapL"
#    define OLED_RENDER_LOCK_SCLK         "ScrL"

#    define OLED_RENDER_MODS_NAME         "Mods: "
#    define OLED_RENDER_MODS_SFT          "Shft"
#    define OLED_RENDER_MODS_CTL          "Ctrl"
#    define OLED_RENDER_MODS_ALT          "Alt\n"
#    define OLED_RENDER_MODS_GUI          "GUI\n"

#    define OLED_RENDER_BOOTMAGIC_NAME    "BTMGK"
#    define OLED_RENDER_BOOTMAGIC_NKRO    "NKRO"
#    define OLED_RENDER_BOOTMAGIC_NOGUI   "nGUI"
#    define OLED_RENDER_BOOTMAGIC_GRV     "GRV"
#    define OLED_RENDER_BOOTMAGIC_ONESHOT "1SHT"
#    define OLED_RENDER_BOOTMAGIC_SWAP "SWAP"
#    define OLED_RENDER_BOOTMAGIC_CAPS  "CAPS"
#    define OLED_RENDER_BOOTMAGIC_SWAP    "SWAP"
#    define OLED_RENDER_BOOTMAGIC_CAPS    "CAPS"

#    define OLED_RENDER_USER_NAME "USER:"
#    define OLED_RENDER_USER_ANIM "Anim"
#    define OLED_RENDER_USER_LAYR "Layr"
#    define OLED_RENDER_USER_NUKE "Nuke"
#    define OLED_RENDER_USER_NAME         "USER:"
#    define OLED_RENDER_USER_ANIM         "Anim"
#    define OLED_RENDER_USER_LAYR         "Layr"
#    define OLED_RENDER_USER_NUKE         "Nuke"

#    define OLED_RENDER_WPM_COUNTER "WPM: "
#    define OLED_RENDER_WPM_COUNTER       "WPM: "

#endif

M users/drashna/process_records.c => users/drashna/process_records.c +24 -38
@@ 29,10 29,10 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    // If console is enabled, it will print the matrix position and status of each key pressed
#ifdef KEYLOGGER_ENABLE
    uprintf("KL: kc: 0x%04X, col: %2u, row: %2u, pressed: %b, time: %5u, int: %b, count: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed, record->event.time, record->tap.interrupted, record->tap.count);
#endif  // KEYLOGGER_ENABLE
#endif // KEYLOGGER_ENABLE
#ifdef OLED_DRIVER_ENABLE
    process_record_user_oled(keycode, record);
#endif  // OLED
#endif // OLED

    if (!(process_record_keymap(keycode, record) && process_record_secrets(keycode, record)
#ifdef RGB_MATRIX_ENABLE


@@ 59,7 59,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
            }
            break;

        case KC_MAKE:  // Compiles the firmware, and adds the flash command based on keyboard bootloader
        case KC_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader
            if (!record->event.pressed) {
#ifndef MAKE_BOOTLOADER
                uint8_t temp_mod = mod_config(get_mods());


@@ 90,56 90,44 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {

            break;

        case VRSN:  // Prints firmware version
            if (record->event.pressed) {
                send_string_with_delay_P(PSTR(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION ", Built on: " QMK_BUILDDATE), TAP_CODE_DELAY);
            }
        case VRSN: // Prints firmware version
            if (record->event.pressed) { send_string_with_delay_P(PSTR(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION ", Built on: " QMK_BUILDDATE), TAP_CODE_DELAY); }
            break;

        case KC_DIABLO_CLEAR:  // reset all Diablo timers, disabling them
        case KC_DIABLO_CLEAR: // reset all Diablo timers, disabling them
#ifdef TAP_DANCE_ENABLE
            if (record->event.pressed) {
                for (uint8_t index = 0; index < 4; index++) {
                    diablo_timer[index].key_interval = 0;
                }
                for (uint8_t index = 0; index < 4; index++) { diablo_timer[index].key_interval = 0; }
            }
#endif  // TAP_DANCE_ENABLE
#endif // TAP_DANCE_ENABLE
            break;

        case KC_CCCV:  // One key copy/paste
        case KC_CCCV: // One key copy/paste
            if (record->event.pressed) {
                copy_paste_timer = timer_read();
            } else {
                if (timer_elapsed(copy_paste_timer) > TAPPING_TERM) {  // Hold, copy
                if (timer_elapsed(copy_paste_timer) > TAPPING_TERM) { // Hold, copy
                    tap_code16(LCTL(KC_C));
                } else {  // Tap, paste
                } else { // Tap, paste
                    tap_code16(LCTL(KC_V));
                }
            }
            break;
#ifdef UNICODE_ENABLE
        case UC_FLIP:  // (ノಠ痊ಠ)ノ彡┻━┻
            if (record->event.pressed) {
                send_unicode_string("(ノಠ痊ಠ)ノ彡┻━┻");
            }
        case UC_FLIP: // (ノಠ痊ಠ)ノ彡┻━┻
            if (record->event.pressed) { send_unicode_string("(ノಠ痊ಠ)ノ彡┻━┻"); }
            break;
        case UC_TABL:  // ┬─┬ノ( º _ ºノ)
            if (record->event.pressed) {
                send_unicode_string("┬─┬ノ( º _ ºノ)");
            }
        case UC_TABL: // ┬─┬ノ( º _ ºノ)
            if (record->event.pressed) { send_unicode_string("┬─┬ノ( º _ ºノ)"); }
            break;
        case UC_SHRG:  // ¯\_(ツ)_/¯
            if (record->event.pressed) {
                send_unicode_string("¯\\_(ツ)_/¯");
            }
        case UC_SHRG: // ¯\_(ツ)_/¯
            if (record->event.pressed) { send_unicode_string("¯\\_(ツ)_/¯"); }
            break;
        case UC_DISA:  // ಠ_ಠ
            if (record->event.pressed) {
                send_unicode_string("ಠ_ಠ");
            }
        case UC_DISA: // ಠ_ಠ
            if (record->event.pressed) { send_unicode_string("ಠ_ಠ"); }
            break;
#endif
        case KC_RGB_T:  // This allows me to use underglow as layer indication, or as normal
        case KC_RGB_T: // This allows me to use underglow as layer indication, or as normal
#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
            if (record->event.pressed) {
                userspace_config.rgb_layer_change ^= 1;


@@ 149,14 137,14 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
#    if defined(RGBLIGHT_ENABLE) && defined(RGB_MATRIX_ENABLE)
                    rgblight_enable_noeeprom();
#    endif
                    layer_state_set(layer_state);  // This is needed to immediately set the layer color (looks better)
                    layer_state_set(layer_state); // This is needed to immediately set the layer color (looks better)
#    if defined(RGBLIGHT_ENABLE) && defined(RGB_MATRIX_ENABLE)
                } else {
                    rgblight_disable_noeeprom();
#    endif
                }
            }
#endif  // RGBLIGHT_ENABLE
#endif // RGBLIGHT_ENABLE
            break;

#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)


@@ 176,7 164,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
            }
            return false;
            break;
        case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT:  // quantum_keycodes.h L400 for definitions
        case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // quantum_keycodes.h L400 for definitions
            if (record->event.pressed) {
                bool is_eeprom_updated;
#    if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES)


@@ 194,9 182,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
                    is_eeprom_updated = true;
                }
#    endif
                if (is_eeprom_updated) {
                    eeconfig_update_user(userspace_config.raw);
                }
                if (is_eeprom_updated) { eeconfig_update_user(userspace_config.raw); }
            }
#endif
    }

M users/drashna/process_records.h => users/drashna/process_records.h +41 -41
@@ 17,58 17,58 @@
#pragma once
#include "drashna.h"

#if defined(KEYBOARD_handwired_dactyl_manuform_5x6_right_trackball)
#if defined(KEYBOARD_handwired_tractyl_manuform_5x6_right)
#    define PLACEHOLDER_SAFE_RANGE KEYMAP_SAFE_RANGE
#else
#    define PLACEHOLDER_SAFE_RANGE SAFE_RANGE
#endif

enum userspace_custom_keycodes {
    VRSN = PLACEHOLDER_SAFE_RANGE,  // Prints QMK Firmware and board info
    KC_QWERTY,                      // Sets default layer to QWERTY
    KC_COLEMAK,                     // Sets default layer to COLEMAK
    KC_DVORAK,                      // Sets default layer to DVORAK
    KC_WORKMAN,                     // Sets default layer to WORKMAN
    KC_DIABLO_CLEAR,                // Clears all Diablo Timers
    KC_MAKE,                        // Run keyboard's customized make command
    KC_RGB_T,                       // Toggles RGB Layer Indication mode
    RGB_IDL,                        // RGB Idling animations
    KC_SECRET_1,                    // test1
    KC_SECRET_2,                    // test2
    KC_SECRET_3,                    // test3
    KC_SECRET_4,                    // test4
    KC_SECRET_5,                    // test5
    KC_CCCV,                        // Hold to copy, tap to paste
    KC_NUKE,                        // NUCLEAR LAUNCH DETECTED!!!
    UC_FLIP,                        // (ಠ痊ಠ)┻━┻
    UC_TABL,                        // ┬─┬ノ( º _ ºノ)
    UC_SHRG,                        // ¯\_(ツ)_/¯
    UC_DISA,                        // ಠ_ಠ
    NEW_SAFE_RANGE                  // use "NEWPLACEHOLDER for keymap specific codes
    VRSN = PLACEHOLDER_SAFE_RANGE, // Prints QMK Firmware and board info
    KC_QWERTY,                     // Sets default layer to QWERTY
    KC_COLEMAK,                    // Sets default layer to COLEMAK
    KC_DVORAK,                     // Sets default layer to DVORAK
    KC_WORKMAN,                    // Sets default layer to WORKMAN
    KC_DIABLO_CLEAR,               // Clears all Diablo Timers
    KC_MAKE,                       // Run keyboard's customized make command
    KC_RGB_T,                      // Toggles RGB Layer Indication mode
    RGB_IDL,                       // RGB Idling animations
    KC_SECRET_1,                   // test1
    KC_SECRET_2,                   // test2
    KC_SECRET_3,                   // test3
    KC_SECRET_4,                   // test4
    KC_SECRET_5,                   // test5
    KC_CCCV,                       // Hold to copy, tap to paste
    KC_NUKE,                       // NUCLEAR LAUNCH DETECTED!!!
    UC_FLIP,                       // (ಠ痊ಠ)┻━┻
    UC_TABL,                       // ┬─┬ノ( º _ ºノ)
    UC_SHRG,                       // ¯\_(ツ)_/¯
    UC_DISA,                       // ಠ_ಠ
    NEW_SAFE_RANGE                 // use "NEWPLACEHOLDER for keymap specific codes
};

bool process_record_secrets(uint16_t keycode, keyrecord_t *record);
bool process_record_keymap(uint16_t keycode, keyrecord_t *record);

#define LOWER   MO(_LOWER)
#define RAISE   MO(_RAISE)
#define ADJUST  MO(_ADJUST)
#define TG_MODS OS_TOGG
#define TG_GAME TG(_GAMEPAD)
#define TG_DBLO TG(_DIABLO)
#define OS_LWR  OSL(_LOWER)
#define OS_RSE  OSL(_RAISE)
#define LOWER    MO(_LOWER)
#define RAISE    MO(_RAISE)
#define ADJUST   MO(_ADJUST)
#define TG_MODS  OS_TOGG
#define TG_GAME  TG(_GAMEPAD)
#define TG_DBLO  TG(_DIABLO)
#define OS_LWR   OSL(_LOWER)
#define OS_RSE   OSL(_RAISE)

#define KC_SEC1 KC_SECRET_1
#define KC_SEC2 KC_SECRET_2
#define KC_SEC3 KC_SECRET_3
#define KC_SEC4 KC_SECRET_4
#define KC_SEC5 KC_SECRET_5
#define KC_SEC1  KC_SECRET_1
#define KC_SEC2  KC_SECRET_2
#define KC_SEC3  KC_SECRET_3
#define KC_SEC4  KC_SECRET_4
#define KC_SEC5  KC_SECRET_5

#define QWERTY  KC_QWERTY
#define DVORAK  KC_DVORAK
#define COLEMAK KC_COLEMAK
#define WORKMAN KC_WORKMAN
#define QWERTY   KC_QWERTY
#define DVORAK   KC_DVORAK
#define COLEMAK  KC_COLEMAK
#define WORKMAN  KC_WORKMAN

#define KC_RESET RESET
#define KC_RST   KC_RESET


@@ 77,9 77,9 @@ bool process_record_keymap(uint16_t keycode, keyrecord_t *record);
#    define KC_C1R3 SH_T(KC_TAB)
#elif defined(DRASHNA_LP)
#    define KC_C1R3 TG(_GAMEPAD)
#else  // SWAP_HANDS_ENABLE
#else // SWAP_HANDS_ENABLE
#    define KC_C1R3 KC_TAB
#endif  // SWAP_HANDS_ENABLE
#endif // SWAP_HANDS_ENABLE

#define BK_LWER LT(_LOWER, KC_BSPC)
#define SP_LWER LT(_LOWER, KC_SPC)

M users/drashna/rgb_matrix_stuff.c => users/drashna/rgb_matrix_stuff.c +20 -34
@@ 28,33 28,27 @@ static uint32_t hypno_timer;

void rgb_matrix_layer_helper(uint8_t hue, uint8_t sat, uint8_t val, uint8_t mode, uint8_t speed, uint8_t led_type, uint8_t led_min, uint8_t led_max) {
    HSV hsv = {hue, sat, val};
    if (hsv.v > rgb_matrix_get_val()) {
        hsv.v = rgb_matrix_get_val();
    }
    if (hsv.v > rgb_matrix_get_val()) { hsv.v = rgb_matrix_get_val(); }

    switch (mode) {
        case 1:  // breathing
        {
            uint16_t time = scale16by8(g_rgb_timer, speed / 8);
            hsv.v         = scale8(abs8(sin8(time) - 128) * 2, hsv.v);
            RGB rgb       = hsv_to_rgb(hsv);
            for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
                if (HAS_FLAGS(g_led_config.flags[i], led_type)) {
                    RGB_MATRIX_INDICATOR_SET_COLOR(i, rgb.r, rgb.g, rgb.b);
        case 1: // breathing
            {
                uint16_t time = scale16by8(g_rgb_timer, speed / 8);
                hsv.v         = scale8(abs8(sin8(time) - 128) * 2, hsv.v);
                RGB rgb       = hsv_to_rgb(hsv);
                for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
                    if (HAS_FLAGS(g_led_config.flags[i], led_type)) { RGB_MATRIX_INDICATOR_SET_COLOR(i, rgb.r, rgb.g, rgb.b); }
                }
                break;
            }
            break;
        }
        default:  // Solid Color
        {
            RGB rgb = hsv_to_rgb(hsv);
            for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
                if (HAS_FLAGS(g_led_config.flags[i], led_type)) {
                    RGB_MATRIX_INDICATOR_SET_COLOR(i, rgb.r, rgb.g, rgb.b);
        default: // Solid Color
            {
                RGB rgb = hsv_to_rgb(hsv);
                for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
                    if (HAS_FLAGS(g_led_config.flags[i], led_type)) { RGB_MATRIX_INDICATOR_SET_COLOR(i, rgb.r, rgb.g, rgb.b); }
                }
                break;
            }
            break;
        }
    }
}



@@ 62,38 56,30 @@ __attribute__((weak)) void rgb_matrix_indicator_keymap(void) {}

void matrix_scan_rgb_matrix(void) {
#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
    if (userspace_config.rgb_matrix_idle_anim && rgb_matrix_get_mode() == RGB_MATRIX_TYPING_HEATMAP && sync_timer_elapsed32(hypno_timer) > 15000) {
        rgb_matrix_mode_noeeprom(RGB_MATRIX_REST_MODE);
    }
    if (userspace_config.rgb_matrix_idle_anim && rgb_matrix_get_mode() == RGB_MATRIX_TYPING_HEATMAP && sync_timer_elapsed32(hypno_timer) > 15000) { rgb_matrix_mode_noeeprom(RGB_MATRIX_REST_MODE); }
#endif
    rgb_matrix_indicator_keymap();
}

void keyboard_post_init_rgb_matrix(void) {
#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
    if (userspace_config.rgb_matrix_idle_anim) {
        rgb_matrix_mode_noeeprom(RGB_MATRIX_REST_MODE);
    }
    if (userspace_config.rgb_matrix_idle_anim) { rgb_matrix_mode_noeeprom(RGB_MATRIX_REST_MODE); }
#endif
}

bool process_record_user_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
    hypno_timer = sync_timer_read32();
    if (userspace_config.rgb_matrix_idle_anim && rgb_matrix_get_mode() == RGB_MATRIX_REST_MODE) {
        rgb_matrix_mode_noeeprom(RGB_MATRIX_TYPING_HEATMAP);
    }
    if (userspace_config.rgb_matrix_idle_anim && rgb_matrix_get_mode() == RGB_MATRIX_REST_MODE) { rgb_matrix_mode_noeeprom(RGB_MATRIX_TYPING_HEATMAP); }
#endif
    switch (keycode) {
        case RGB_IDL:  // This allows me to use underglow as layer indication, or as normal
        case RGB_IDL: // This allows me to use underglow as layer indication, or as normal
#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS)
            if (record->event.pressed) {
                userspace_config.rgb_matrix_idle_anim ^= 1;
                dprintf("RGB Matrix Idle Animation [EEPROM]: %u\n", userspace_config.rgb_matrix_idle_anim);
                eeconfig_update_user(userspace_config.raw);
                if (userspace_config.rgb_matrix_idle_anim) {
                    rgb_matrix_mode_noeeprom(RGB_MATRIX_TYPING_HEATMAP);
                }
                if (userspace_config.rgb_matrix_idle_anim) { rgb_matrix_mode_noeeprom(RGB_MATRIX_TYPING_HEATMAP); }
            }
#endif
            break;

M users/drashna/rgb_stuff.c => users/drashna/rgb_stuff.c +27 -73
@@ 28,7 28,7 @@ void rgblight_sethsv_default_helper(uint8_t index) { rgblight_sethsv_at(rgblight
static rgblight_fadeout lights[RGBLED_NUM];

/* Handler for fading/twinkling effect */
void scan_rgblight_fadeout(void) {  // Don't effing change this function .... rgblight_sethsv is supppppper intensive
void scan_rgblight_fadeout(void) { // Don't effing change this function .... rgblight_sethsv is supppppper intensive
    bool litup = false;

    for (uint8_t light_index = 0; light_index < RGBLED_NUM; ++light_index) {


@@ 38,21 38,15 @@ void scan_rgblight_fadeout(void) {  // Don't effing change this function .... rg

            if (light->life) {
                light->life -= 1;
                if (get_highest_layer(layer_state) == 0) {
                    sethsv(light->hue + rand() % 0xF, 255, light->life, (LED_TYPE *)&led[light_index]);
                }
                if (get_highest_layer(layer_state) == 0) { sethsv(light->hue + rand() % 0xF, 255, light->life, (LED_TYPE *)&led[light_index]); }
                light->timer = sync_timer_read();
            } else {
                if (light->enabled && get_highest_layer(layer_state) == 0) {
                    rgblight_sethsv_default_helper(light_index);
                }
                if (light->enabled && get_highest_layer(layer_state) == 0) { rgblight_sethsv_default_helper(light_index); }
                litup = light->enabled = false;
            }
        }
    }
    if (litup && get_highest_layer(layer_state) == 0) {
        rgblight_set();
    }
    if (litup && get_highest_layer(layer_state) == 0) { rgblight_set(); }
}

/* Triggers a LED to fade/twinkle.


@@ 98,9 92,7 @@ void start_rgb_light(void) {
bool process_record_user_rgb_light(uint16_t keycode, keyrecord_t *record) {
    uint16_t temp_keycode = keycode;
    // Filter out the actual keycode from MT and LT keys.
    if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) {
        temp_keycode &= 0xFF;
    }
    if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) { temp_keycode &= 0xFF; }

    switch (temp_keycode) {
#    ifdef RGBLIGHT_TWINKLE


@@ 110,11 102,9 @@ bool process_record_user_rgb_light(uint16_t keycode, keyrecord_t *record) {
        case KC_KP_SLASH ... KC_KP_DOT:
        case KC_F13 ... KC_F24:
        case KC_AUDIO_MUTE ... KC_MEDIA_REWIND:
            if (record->event.pressed) {
                start_rgb_light();
            }
            if (record->event.pressed) { start_rgb_light(); }
            break;
#    endif  // RGBLIGHT_TWINKLE
#    endif // RGBLIGHT_TWINKLE
    }
    return true;
}


@@ 129,9 119,7 @@ static uint16_t rgblight_startup_loop_timer;
void keyboard_post_init_rgb_light(void) {
#    if defined(RGBLIGHT_STARTUP_ANIMATION)
    is_enabled = rgblight_is_enabled();
    if (userspace_config.rgb_layer_change) {
        rgblight_enable_noeeprom();
    }
    if (userspace_config.rgb_layer_change) { rgblight_enable_noeeprom(); }
    if (rgblight_is_enabled()) {
        layer_state_set_rgb_light(layer_state);
        old_hue = rgblight_get_hue();


@@ 145,7 133,7 @@ void keyboard_post_init_rgb_light(void) {
void matrix_scan_rgb_light(void) {
#    ifdef RGBLIGHT_TWINKLE
    scan_rgblight_fadeout();
#    endif  // RGBLIGHT_ENABLE
#    endif // RGBLIGHT_ENABLE

#    if defined(RGBLIGHT_STARTUP_ANIMATION)
    if (is_rgblight_startup && is_keyboard_master()) {


@@ 156,12 144,8 @@ void matrix_scan_rgb_light(void) {
            rgblight_startup_loop_timer = sync_timer_read();
            if (counter == 255) {
                is_rgblight_startup = false;
                if (!is_enabled) {
                    rgblight_disable_noeeprom();
                }
                if (userspace_config.rgb_layer_change) {
                    layer_state_set_rgb_light(layer_state);
                }
                if (!is_enabled) { rgblight_disable_noeeprom(); }
                if (userspace_config.rgb_layer_change) { layer_state_set_rgb_light(layer_state); }
            }
        }
    }


@@ 179,55 163,25 @@ layer_state_t layer_state_set_rgb_light(layer_state_t state) {
    if (userspace_config.rgb_layer_change) {
        switch (get_highest_layer(state | default_layer_state)) {
            case _MACROS: // mouse
                if (!layer_state_cmp(state, _GAMEPAD) && !layer_state_cmp(state, _DIABLO)) {
                    rgblight_set_hsv_and_mode(HSV_CHARTREUSE, RGBLIGHT_MODE_BREATHING + 3);
                }
                break;
            case _MEDIA:
                rgblight_set_hsv_and_mode(HSV_CHARTREUSE, RGBLIGHT_MODE_KNIGHT + 1);
                break;
            case _GAMEPAD:
                rgblight_set_hsv_and_mode(HSV_ORANGE, RGBLIGHT_MODE_SNAKE + 2);
                break;
            case _DIABLO:
                rgblight_set_hsv_and_mode(HSV_RED, RGBLIGHT_MODE_BREATHING + 3);
                break;
            case _RAISE:
                rgblight_set_hsv_and_mode(HSV_YELLOW, RGBLIGHT_MODE_BREATHING + 3);
                break;
            case _LOWER:
                rgblight_set_hsv_and_mode(HSV_GREEN, RGBLIGHT_MODE_BREATHING + 3);
                break;
            case _ADJUST:
                rgblight_set_hsv_and_mode(HSV_RED, RGBLIGHT_MODE_KNIGHT + 2);
                break;
            case _COLEMAK:
                rgblight_set_hsv_and_mode(HSV_MAGENTA, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _DVORAK:
                rgblight_set_hsv_and_mode(HSV_SPRINGGREEN, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _WORKMAN:
                rgblight_set_hsv_and_mode(HSV_GOLDENROD, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _NORMAN:
                rgblight_set_hsv_and_mode(HSV_CORAL, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _MALTRON:
                rgblight_set_hsv_and_mode(HSV_YELLOW, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _EUCALYN:
                rgblight_set_hsv_and_mode(HSV_PINK, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            case _CARPLAX:
                rgblight_set_hsv_and_mode(HSV_BLUE, RGBLIGHT_MODE_STATIC_LIGHT);
                break;
            default:
                rgblight_set_hsv_and_mode(HSV_CYAN, RGBLIGHT_MODE_STATIC_LIGHT);
                if (!layer_state_cmp(state, _GAMEPAD) && !layer_state_cmp(state, _DIABLO)) { rgblight_set_hsv_and_mode(HSV_CHARTREUSE, RGBLIGHT_MODE_BREATHING + 3); }
                break;
            case _MEDIA: rgblight_set_hsv_and_mode(HSV_CHARTREUSE, RGBLIGHT_MODE_KNIGHT + 1); break;
            case _GAMEPAD: rgblight_set_hsv_and_mode(HSV_ORANGE, RGBLIGHT_MODE_SNAKE + 2); break;
            case _DIABLO: rgblight_set_hsv_and_mode(HSV_RED, RGBLIGHT_MODE_BREATHING + 3); break;
            case _RAISE: rgblight_set_hsv_and_mode(HSV_YELLOW, RGBLIGHT_MODE_BREATHING + 3); break;
            case _LOWER: rgblight_set_hsv_and_mode(HSV_GREEN, RGBLIGHT_MODE_BREATHING + 3); break;
            case _ADJUST: rgblight_set_hsv_and_mode(HSV_RED, RGBLIGHT_MODE_KNIGHT + 2); break;
            case _COLEMAK: rgblight_set_hsv_and_mode(HSV_MAGENTA, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _DVORAK: rgblight_set_hsv_and_mode(HSV_SPRINGGREEN, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _WORKMAN: rgblight_set_hsv_and_mode(HSV_GOLDENROD, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _NORMAN: rgblight_set_hsv_and_mode(HSV_CORAL, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _MALTRON: rgblight_set_hsv_and_mode(HSV_YELLOW, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _EUCALYN: rgblight_set_hsv_and_mode(HSV_PINK, RGBLIGHT_MODE_STATIC_LIGHT); break;
            case _CARPLAX: rgblight_set_hsv_and_mode(HSV_BLUE, RGBLIGHT_MODE_STATIC_LIGHT); break;
            default: rgblight_set_hsv_and_mode(HSV_CYAN, RGBLIGHT_MODE_STATIC_LIGHT); break;
        }
    }
#    endif  // RGBLIGHT_ENABLE
#    endif // RGBLIGHT_ENABLE

    return state;
}

M users/drashna/rules.mk => users/drashna/rules.mk +22 -21
@@ 80,28 80,29 @@ endif

CUSTOM_SPLIT_TRANSPORT ?= yes
ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
    ifneq ($(strip $(SPLIT_TRANSPORT)), custom)
        ifeq ($(strip $(CUSTOM_SPLIT_TRANSPORT)), yes)
            SPLIT_TRANSPORT = custom
            QUANTUM_LIB_SRC += drashna_transport.c
            OPT_DEFS += -DDRASHNA_CUSTOM_TRANSPORT
            # Unused functions are pruned away, which is why we can add multiple drivers here without bloat.
            ifeq ($(PLATFORM),AVR)
                ifneq ($(NO_I2C),yes)
                    QUANTUM_LIB_SRC += i2c_master.c \
                                    i2c_slave.c
                endif
            endif
    QUANTUM_LIB_SRC += transport_sync.c
    # ifneq ($(strip $(SPLIT_TRANSPORT)), custom)
    #     ifeq ($(strip $(CUSTOM_SPLIT_TRANSPORT)), yes)
    #         SPLIT_TRANSPORT = custom
    #         QUANTUM_LIB_SRC += drashna_transport.c
    #         OPT_DEFS += -DDRASHNA_CUSTOM_TRANSPORT
    #         # Unused functions are pruned away, which is why we can add multiple drivers here without bloat.
    #         ifeq ($(PLATFORM),AVR)
    #             ifneq ($(NO_I2C),yes)
    #                 QUANTUM_LIB_SRC += i2c_master.c \
    #                                 i2c_slave.c
    #             endif
    #         endif

            SERIAL_DRIVER ?= bitbang
            OPT_DEFS += -DSERIAL_DRIVER_$(strip $(shell echo $(SERIAL_DRIVER) | tr '[:lower:]' '[:upper:]'))
            ifeq ($(strip $(SERIAL_DRIVER)), bitbang)
                QUANTUM_LIB_SRC += serial.c
            else
                QUANTUM_LIB_SRC += serial_$(strip $(SERIAL_DRIVER)).c
            endif
        endif
    endif
    #         SERIAL_DRIVER ?= bitbang
    #         OPT_DEFS += -DSERIAL_DRIVER_$(strip $(shell echo $(SERIAL_DRIVER) | tr '[:lower:]' '[:upper:]'))
    #         ifeq ($(strip $(SERIAL_DRIVER)), bitbang)
    #             QUANTUM_LIB_SRC += serial.c
    #         else
    #             QUANTUM_LIB_SRC += serial_$(strip $(SERIAL_DRIVER)).c
    #         endif
    #     endif
    # endif
endif

# DEBUG_MATRIX_SCAN_RATE_ENABLE = api

M users/drashna/tap_dances.c => users/drashna/tap_dances.c +3 -5
@@ 26,7 26,7 @@ uint8_t diablo_times[] = {0, 1, 3, 5, 10, 30};

// Cycle through the times for the macro, starting at 0, for disabled.
void diablo_tapdance_master(qk_tap_dance_state_t *state, void *user_data) {
    diable_keys_t *diablo_keys = (diable_keys_t *)user_data;
    diable_keys_t *diablo_keys               = (diable_keys_t *)user_data;
    // Sets the keycode based on the index
    diablo_timer[diablo_keys->index].keycode = diablo_keys->keycode;



@@ 34,7 34,7 @@ void diablo_tapdance_master(qk_tap_dance_state_t *state, void *user_data) {
    if (state->count >= (sizeof(diablo_times) / sizeof(uint8_t))) {
        diablo_timer[diablo_keys->index].key_interval = 0;
        reset_tap_dance(state);
    } else {  // else set the interval (tapdance count starts at 1, array starts at 0, so offset by one)
    } else { // else set the interval (tapdance count starts at 1, array starts at 0, so offset by one)
        diablo_timer[diablo_keys->index].key_interval = diablo_times[state->count - 1];
    }
}


@@ 64,9 64,7 @@ void run_diablo_macro_check(void) {
            // reset the timer, since enough time has passed
            diablo_timer[index].timer = timer_read();
            // send keycode ONLY if we're on the diablo layer.
            if (IS_LAYER_ON(_DIABLO)) {
                tap_code(diablo_timer[index].keycode);
            }
            if (IS_LAYER_ON(_DIABLO)) { tap_code(diablo_timer[index].keycode); }
        }
    }
}

M users/drashna/tap_dances.h => users/drashna/tap_dances.h +1 -1
@@ 43,4 43,4 @@ enum {
    TD_D3_4
};
// clang-format on
#endif  // TAP_DANCE_ENABLE
#endif // TAP_DANCE_ENABLE

M users/drashna/template.c => users/drashna/template.c +2 -4
@@ 54,9 54,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
            break;

        case VRSN:
            if (record->event.pressed) {
                SEND_STRING(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION);
            }
            if (record->event.pressed) { SEND_STRING(QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION); }
            return false;
            break;
    }


@@ 89,7 87,7 @@ __attribute__((weak)) void startup_keymap(void) {}
void startup_user(void) {
#ifdef RGBLIGHT_ENABLE
    matrix_init_rgb();
#endif  // RGBLIGHT_ENABLE
#endif // RGBLIGHT_ENABLE
    startup_keymap();
}


M users/drashna/template.h => users/drashna/template.h +2 -2
@@ 24,8 24,8 @@
#define BASE 0

enum custom_keycodes {
    VRSN = SAFE_RANGE,  // can always be here
    VRSN = SAFE_RANGE, // can always be here
    KC_MAKE,
    KC_RESET,
    NEWPLACEHOLDER  // use "NEWPLACEHOLDER for keymap specific codes
    NEWPLACEHOLDER // use "NEWPLACEHOLDER for keymap specific codes
};

A users/drashna/transport_sync.c => users/drashna/transport_sync.c +80 -0
@@ 0,0 1,80 @@
#ifdef SPLIT_TRANSACTION_IDS_USER
#include "transport_sync.h"
#include "transactions.h"
#include <string.h>

typedef struct {
    bool            oled_on;
    uint16_t keymap_config;
} user_runtime_config_t;

user_runtime_config_t user_state;

void user_sync(uint8_t initiator2target_buffer_size, const void* initiator2target_buffer, uint8_t target2initiator_buffer_size, void* target2initiator_buffer) {
    if (initiator2target_buffer_size == sizeof(user_state)) {
        memcpy(&user_state, initiator2target_buffer, initiator2target_buffer_size);
    }
}

void keyboard_post_init_transport_sync(void) {
    // Register keyboard state sync split transaction
    transaction_register_rpc(RPC_ID_USER_STATE_SYNC, user_sync);
}

void user_state_update(void) {
    if (is_keyboard_master()) {

#ifdef OLED_DRIVER_ENABLE
        user_state.oled_on = is_oled_on();
#endif

        user_state.keymap_config = keymap_config.raw;
    } else {
#ifdef OLED_DRIVER_ENABLE
        if (user_state.oled_on) {
            oled_on();
        } else {
            oled_off();
        }
#endif
        if (keymap_config.raw != user_state.keymap_config) {
            keymap_config.raw = user_state.keymap_config;
        }
    }
}

void user_state_sync(void) {
    if (is_keyboard_master()) {
        // Keep track of the last state, so that we can tell if we need to propagate to slave
        static user_runtime_config_t last_user_state;
        static uint32_t              last_sync;
        bool                         needs_sync = false;

        // Check if the state values are different
        if (memcmp(&user_state, &last_user_state, sizeof(user_state))) {
            needs_sync = true;
            memcpy(&last_user_state, &user_state, sizeof(user_state));
        }

        // Send to slave every 500ms regardless of state change
        if (timer_elapsed32(last_sync) > 250) {
            needs_sync = true;
        }

        // Perform the sync if requested
        if (needs_sync) {
            if (transaction_rpc_send(RPC_ID_USER_STATE_SYNC, sizeof(user_state), &user_state)) {
                last_sync = timer_read32();
            }
        }
    }
}

void housekeeping_task_user(void) {
    // Update kb_state so we can send to slave
    user_state_update();

    // Data sync from master to slave
    user_state_sync();
}
#endif

R keyboards/handwired/dactyl_manuform/5x6_right_trackball/keymaps/default/config.h => users/drashna/transport_sync.h +3 -3
@@ 1,3 1,4 @@

/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>
 *
 * This program is free software: you can redistribute it and/or modify


@@ 14,9 15,8 @@
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



#pragma once

#include "drashna.h"

#include "../drashna/config.h"
void keyboard_post_init_transport_sync(void);