Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Retro Shift (Auto Shift for Tap Hold via Retro Tapping) #9889

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,24 @@ endef
define PARSE_RULE
RULE := $1
COMMANDS :=
REQUIRE_PLATFORM_KEY :=
# If the rule starts with all, then continue the parsing from
# PARSE_ALL_KEYBOARDS
ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)
KEYBOARD_RULE=all
$$(eval $$(call PARSE_ALL_KEYBOARDS))
else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-avr),true)
KEYBOARD_RULE=all
REQUIRE_PLATFORM_KEY := avr
$$(eval $$(call PARSE_ALL_KEYBOARDS))
else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-chibios),true)
KEYBOARD_RULE=all
REQUIRE_PLATFORM_KEY := chibios
$$(eval $$(call PARSE_ALL_KEYBOARDS))
else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all-arm_atsam),true)
KEYBOARD_RULE=all
REQUIRE_PLATFORM_KEY := arm_atsam
$$(eval $$(call PARSE_ALL_KEYBOARDS))
else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,test),true)
$$(eval $$(call PARSE_TEST))
# If the rule starts with the name of a known keyboard, then continue
Expand Down Expand Up @@ -447,7 +460,7 @@ define PARSE_KEYMAP
# Format it in bold
KB_SP := $(BOLD)$$(KB_SP)$(NO_COLOR)
# Specify the variables that we are passing forward to submake
MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM)
MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM) REQUIRE_PLATFORM_KEY=$$(REQUIRE_PLATFORM_KEY)
# And the first part of the make command
MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f build_keyboard.mk $$(MAKE_TARGET)
# The message to display
Expand All @@ -466,6 +479,8 @@ define BUILD
LOG=$$$$($$(MAKE_CMD) $$(MAKE_VARS) SILENT=true 2>&1) ; \
if [ $$$$? -gt 0 ]; \
then $$(PRINT_ERROR_PLAIN); \
elif [ "$$$$LOG" = "skipped" ] ; \
then $$(PRINT_SKIPPED_PLAIN); \
elif [ "$$$$LOG" != "" ] ; \
then $$(PRINT_WARNING_PLAIN); \
else \
Expand Down
13 changes: 13 additions & 0 deletions build_keyboard.mk
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ SRC += $(TMK_COMMON_SRC)
OPT_DEFS += $(TMK_COMMON_DEFS)
EXTRALDFLAGS += $(TMK_COMMON_LDFLAGS)

SKIP_COMPILE := no
ifneq ($(REQUIRE_PLATFORM_KEY),)
ifneq ($(REQUIRE_PLATFORM_KEY),$(PLATFORM_KEY))
SKIP_COMPILE := yes
endif
endif

include $(TMK_PATH)/$(PLATFORM_KEY).mk
ifneq ($(strip $(PROTOCOL)),)
include $(TMK_PATH)/protocol/$(strip $(shell echo $(PROTOCOL) | tr '[:upper:]' '[:lower:]')).mk
Expand Down Expand Up @@ -352,7 +359,13 @@ $(KEYBOARD_OUTPUT)_INC := $(PROJECT_INC) $(GFXINC)
$(KEYBOARD_OUTPUT)_CONFIG := $(PROJECT_CONFIG)

# Default target.
ifeq ($(SKIP_COMPILE),no)
all: build check-size
else
all:
echo "skipped" >&2
endif

build: elf cpfirmware
check-size: build
objs-size: build
Expand Down
6 changes: 4 additions & 2 deletions common_features.mk
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,10 @@ ifeq ($(strip $(SPLIT_KEYBOARD)), yes)
# Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called.
# Unused functions are pruned away, which is why we can add multiple drivers here without bloat.
ifeq ($(PLATFORM),AVR)
QUANTUM_LIB_SRC += i2c_master.c \
i2c_slave.c
ifneq ($(NO_I2C),yes)
QUANTUM_LIB_SRC += i2c_master.c \
i2c_slave.c
endif
endif

SERIAL_DRIVER ?= bitbang
Expand Down
2 changes: 2 additions & 0 deletions docs/feature_auto_shift.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Yes, unfortunately.
for a shifted version, but we did not. On the other hand, we may think we are
tapping the keys, but really we have held it for a little longer than
anticipated.
3. Auto Shift does not apply to Tap Hold keys. For automatic shifting of Tap Hold
keys see [Retro Shift](tap_hold.md#retro-shift).

## How Do I Enable Auto Shift?

Expand Down
9 changes: 8 additions & 1 deletion docs/feature_encoders.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,20 @@ Additionally, the resolution, which defines how many pulses the encoder register
#define ENCODER_RESOLUTION 4
```

It can also be defined per-encoder, by instead defining:

```c
#define ENCODER_RESOLUTIONS { 4, 2 }
```

## Split Keyboards

If you are using different pinouts for the encoders on each half of a split keyboard, you can define the pinout for the right half like this:
If you are using different pinouts for the encoders on each half of a split keyboard, you can define the pinout (and optionally, resolutions) for the right half like this:

```c
#define ENCODERS_PAD_A_RIGHT { encoder1a, encoder2a }
#define ENCODERS_PAD_B_RIGHT { encoder1b, encoder2b }
#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
```

## Callbacks
Expand Down
6 changes: 6 additions & 0 deletions docs/feature_joystick.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
```

### Axis Resolution

By default, the resolution of each axis is 8 bit, giving a range of -127 to +127. If you need higher precision, you can increase it by defining eg. `JOYSTICK_AXES_RESOLUTION 12` in your `config.h`. The resolution must be between 8 and 16.

Note that the supported AVR MCUs have a 10-bit ADC, and 12-bit for most STM32 MCUs.

### Triggering Joystick Buttons

Joystick buttons are normal Quantum keycodes, defined as `JS_BUTTON0` to `JS_BUTTON31`, depending on the number of buttons you have configured.
Expand Down
4 changes: 4 additions & 0 deletions docs/feature_mouse_keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,3 +140,7 @@ To use constant speed mode, you must at least define `MK_COMBINED` in your keyma
```c
#define MK_COMBINED
```

## Use with PS/2 Mouse and Pointing Device

Mouse keys button state is shared with [PS/2 mouse](feature_ps2_mouse.md) and [pointing device](feature_pointing_device.md) so mouse keys button presses can be used for clicks and drags.
8 changes: 8 additions & 0 deletions docs/feature_rgb_matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,14 @@ void rgb_matrix_indicators_kb(void) {
}
```

In addition, there are the advanced indicator functions. These are aimed at those with heavily customized displays, where rendering every LED per cycle is expensive. Such as some of the "drashna" layouts. This includes a special macro to help make this easier to use: `RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b)`.

```c
void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
RGB_MATRIX_INDICATOR_SET_COLOR(index, red, green, blue);
}
```

### Suspended state :id=suspended-state
To use the suspend feature, make sure that `#define RGB_DISABLE_WHEN_USB_SUSPENDED true` is added to the `config.h` file.

Expand Down
20 changes: 20 additions & 0 deletions docs/tap_hold.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,26 @@ Holding and releasing a dual function key without pressing another key will resu

For instance, holding and releasing `LT(2, KC_SPACE)` without hitting another key will result in nothing happening. With this enabled, it will send `KC_SPACE` instead.

## Retro Shift

Holding and releasing a Tap Hold key without pressing another key will result in only the hold. With Retro Shift enabled this action will also produce a shifted version of the tap keycode on release.

This is a supplement to [Auto Shift](feature_auto_shift.md), which does not support Tap Hold. Auto Shift is not required to be enabled, but for consistency it should be enabled and configured with the Auto Shift timeout matching the tapping term. Retro Shift applies to the same tap keycodes as Auto Shift and uses the Auto Shift options `NO_AUTO_SHIFT_SPECIAL`, `NO_AUTO_SHIFT_NUMERIC`, `NO_AUTO_SHIFT_ALPHA`, and `AUTO_SHIFT_MODIFIERS` if defined.

Retro Shift does not require [Retro Tapping](#retro-tapping) to be enabled, and if both are enabled Retro Tapping will only apply if the tap keycode is not matched by Retro Shift.

To enable Retro Shift, add the following to your `config.h`:

```c
#define RETRO_SHIFT
```

If `RETRO_SHIFT` is defined to a value, hold times greater than that value will not produce a tap on release. This enables modifiers to be held for combining with mouse clicks without generating taps on release. For example:

```c
#define RETRO_SHIFT 500
```

## Why do we include the key record for the per key functions?

One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that.
Expand Down
10 changes: 5 additions & 5 deletions drivers/avr/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@

#ifdef SOFT_SERIAL_PIN

# ifdef __AVR_ATmega32U4__
// if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
# if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)
// if using ATmegaxxU4 I2C, can not use PD0 and PD1 in soft serial.
# ifdef USE_AVR_I2C
# if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
# error Using ATmega32U4 I2C, so can not use PD0, PD1
# error Using ATmegaxxU4 I2C, so can not use PD0, PD1
# endif
# endif

Expand Down Expand Up @@ -52,7 +52,7 @@
# define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))
# define SERIAL_PIN_INTERRUPT INT3_vect
# endif
# elif SOFT_SERIAL_PIN == E6
# elif (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) && SOFT_SERIAL_PIN == E6
# define EIMSK_BIT _BV(INT6)
# define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))
# define SERIAL_PIN_INTERRUPT INT6_vect
Expand All @@ -61,7 +61,7 @@
# endif

# else
# error serial.c now support ATmega32U4 only
# error serial.c currently only supports ATmegaxxU2 and ATmegaxxU4
# endif

# define ALWAYS_INLINE __attribute__((always_inline))
Expand Down
3 changes: 3 additions & 0 deletions message.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ ifeq ($(COLOR),true)
OK_COLOR=\033[32;01m
ERROR_COLOR=\033[31;01m
WARN_COLOR=\033[33;01m
SKIPPED_COLOR=\033[36;01m
BLUE=\033[0;34m
BOLD=\033[1m
endif
Expand All @@ -20,6 +21,7 @@ ON_ERROR ?= exit 1
OK_STRING=$(OK_COLOR)[OK]$(NO_COLOR)\n
ERROR_STRING=$(ERROR_COLOR)[ERRORS]$(NO_COLOR)\n
WARN_STRING=$(WARN_COLOR)[WARNINGS]$(NO_COLOR)\n
SKIPPED_STRING=$(SKIPPED_COLOR)[SKIPPED]$(NO_COLOR)\n

TAB_LOG = printf "\n%s\n\n" "$$LOG" | $(AWK) '{ sub(/^/," | "); print }'
TAB_LOG_PLAIN = printf "%s\n" "$$LOG"
Expand All @@ -29,6 +31,7 @@ PRINT_ERROR = ($(SILENT) ||printf " $(ERROR_STRING)" | $(AWK_STATUS)) && $(TAB_L
PRINT_WARNING = ($(SILENT) || printf " $(WARN_STRING)" | $(AWK_STATUS)) && $(TAB_LOG)
PRINT_ERROR_PLAIN = ($(SILENT) ||printf " $(ERROR_STRING)" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN) && $(ON_ERROR)
PRINT_WARNING_PLAIN = ($(SILENT) || printf " $(WARN_STRING)" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN)
PRINT_SKIPPED_PLAIN = ($(SILENT) || printf " $(SKIPPED_STRING)" | $(AWK_STATUS))
PRINT_OK = $(SILENT) || printf " $(OK_STRING)" | $(AWK_STATUS)
BUILD_CMD = LOG=$$($(CMD) 2>&1) ; if [ $$? -gt 0 ]; then $(PRINT_ERROR); elif [ "$$LOG" != "" ] ; then $(PRINT_WARNING); else $(PRINT_OK); fi;
MAKE_MSG_FORMAT = $(AWK) '{ printf "%-118s", $$0;}'
Expand Down
24 changes: 20 additions & 4 deletions quantum/encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
// for memcpy
#include <string.h>

#ifndef ENCODER_RESOLUTION
#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)
# define ENCODER_RESOLUTION 4
#endif

Expand All @@ -34,6 +34,9 @@
#define NUMBER_OF_ENCODERS (sizeof(encoders_pad_a) / sizeof(pin_t))
static pin_t encoders_pad_a[] = ENCODERS_PAD_A;
static pin_t encoders_pad_b[] = ENCODERS_PAD_B;
#ifdef ENCODER_RESOLUTIONS
static uint8_t encoder_resolutions[] = ENCODER_RESOLUTIONS;
#endif

#ifndef ENCODER_DIRECTION_FLIP
# define ENCODER_CLOCKWISE true
Expand Down Expand Up @@ -65,9 +68,15 @@ void encoder_init(void) {
if (!isLeftHand) {
const pin_t encoders_pad_a_right[] = ENCODERS_PAD_A_RIGHT;
const pin_t encoders_pad_b_right[] = ENCODERS_PAD_B_RIGHT;
# if defined(ENCODER_RESOLUTIONS_RIGHT)
const uint8_t encoder_resolutions_right[] = ENCODER_RESOLUTIONS_RIGHT;
# endif
for (uint8_t i = 0; i < NUMBER_OF_ENCODERS; i++) {
encoders_pad_a[i] = encoders_pad_a_right[i];
encoders_pad_b[i] = encoders_pad_b_right[i];
# if defined(ENCODER_RESOLUTIONS_RIGHT)
encoder_resolutions[i] = encoder_resolutions_right[i];
# endif
}
}
#endif
Expand All @@ -87,19 +96,26 @@ void encoder_init(void) {

static void encoder_update(int8_t index, uint8_t state) {
uint8_t i = index;

#ifdef ENCODER_RESOLUTIONS
int8_t resolution = encoder_resolutions[i];
#else
int8_t resolution = ENCODER_RESOLUTION;
#endif

#ifdef SPLIT_KEYBOARD
index += thisHand;
#endif
encoder_pulses[i] += encoder_LUT[state & 0xF];
if (encoder_pulses[i] >= ENCODER_RESOLUTION) {
if (encoder_pulses[i] >= resolution) {
encoder_value[index]++;
encoder_update_kb(index, ENCODER_COUNTER_CLOCKWISE);
}
if (encoder_pulses[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise
encoder_value[index]--;
encoder_update_kb(index, ENCODER_CLOCKWISE);
}
encoder_pulses[i] %= ENCODER_RESOLUTION;
encoder_pulses[i] %= resolution;
}

void encoder_read(void) {
Expand Down
12 changes: 10 additions & 2 deletions quantum/joystick.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#pragma once

#include "quantum.h"

#include <stdint.h>

#ifndef JOYSTICK_BUTTON_COUNT
# define JOYSTICK_BUTTON_COUNT 8
#endif
Expand All @@ -8,9 +12,13 @@
# define JOYSTICK_AXES_COUNT 4
#endif

#include "quantum.h"
#ifndef JOYSTICK_AXES_RESOLUTION
# define JOYSTICK_AXES_RESOLUTION 8
#elif JOYSTICK_AXES_RESOLUTION < 8 || JOYSTICK_AXES_RESOLUTION > 16
# error JOYSTICK_AXES_RESOLUTION must be between 8 and 16
#endif

#include <stdint.h>
#define JOYSTICK_RESOLUTION ((1L << (JOYSTICK_AXES_RESOLUTION - 1)) - 1)

// configure on input_pin of the joystick_axes array entry to JS_VIRTUAL_AXIS
// to prevent it from being read from the ADC. This allows outputing forged axis value.
Expand Down
3 changes: 3 additions & 0 deletions quantum/mcu_selection.mk
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ ifneq (,$(filter $(MCU),atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 a
ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
endif
ifneq (,$(filter $(MCU),atmega16u2 atmega32u2))
NO_I2C = yes
endif
endif

ifneq (,$(filter $(MCU),atmega32a))
Expand Down
8 changes: 4 additions & 4 deletions quantum/process_keycode/process_joystick.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,17 @@ bool process_joystick_analogread_quantum() {
// test the converted value against the lower range
int32_t ref = joystick_axes[axis_index].mid_digit;
int32_t range = joystick_axes[axis_index].min_digit;
int32_t ranged_val = ((axis_val - ref) * -127) / (range - ref);
int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_RESOLUTION) / (range - ref);

if (ranged_val > 0) {
// the value is in the higher range
range = joystick_axes[axis_index].max_digit;
ranged_val = ((axis_val - ref) * 127) / (range - ref);
ranged_val = ((axis_val - ref) * JOYSTICK_RESOLUTION) / (range - ref);
}

// clamp the result in the valid range
ranged_val = ranged_val < -127 ? -127 : ranged_val;
ranged_val = ranged_val > 127 ? 127 : ranged_val;
ranged_val = ranged_val < -JOYSTICK_RESOLUTION ? -JOYSTICK_RESOLUTION : ranged_val;
ranged_val = ranged_val > JOYSTICK_RESOLUTION ? JOYSTICK_RESOLUTION : ranged_val;

if (ranged_val != joystick_status.axes[axis_index]) {
joystick_status.axes[axis_index] = ranged_val;
Expand Down
Loading