Co-authored-by: Ryan <fauxpark@gmail.com>pull/13403/head
@ -1,17 +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/>. | |||
*/ | |||
// placeholder |
@ -1,4 +0,0 @@ | |||
USER_NAME := drashna | |||
SRC += ../drashna/keymap.c | |||
include $(KEYBOARD_PATH_1)/keymaps/drashna/rules.mk |
@ -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 *)¤t_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 |
@ -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 |
@ -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 |
@ -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 |
@ -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}, |
@ -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 ,_______, | |||
_______,_______, _______, | |||
_______,_______, _______, | |||
_______,_______, _______,_______ | |||
), | |||
}; |
@ -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(); | |||
} |
@ -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 |
@ -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 |
@ -0,0 +1,35 @@ | |||
/* 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 "5x6_right.h" | |||
#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 |
@ -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} | |||
] | |||
} | |||
} | |||
} |
@ -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 ,_______, | |||
_______,_______, _______, | |||
_______,_______, _______, | |||
_______,_______, _______,_______ | |||
), | |||
}; |
@ -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 |
@ -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). |
@ -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 |
@ -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 |
@ -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. | |||
``` |
@ -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 |
@ -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; |
@ -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 *)¤t_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 |
@ -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 |