mirror of
https://github.com/qmk/qmk_firmware.git
synced 2025-07-27 11:31:13 +00:00
Merge 2dce308337
into 36c3f4deba
This commit is contained in:
commit
0b74613f79
31
keyboards/tssouthpaw/README.md
Normal file
31
keyboards/tssouthpaw/README.md
Normal file
@ -0,0 +1,31 @@
|
||||
# TS Southpaw
|
||||
|
||||
A southpaw numpad keyboard with RGB matrix and work timer functionality.
|
||||
|
||||
* Keyboard Maintainer: [TS Design Works LLC](https://github.com/tsdesignworks)
|
||||
* Hardware Supported: TS Southpaw Rev 1.6, RP2040 MCU
|
||||
* Hardware Availability: [GitHub](https://github.com/tsdesignworks/tssouthpaw)
|
||||
|
||||
Make example for this keyboard (after setting up your build environment):
|
||||
|
||||
make tssouthpaw:default
|
||||
|
||||
Flashing example for this keyboard:
|
||||
|
||||
make tssouthpaw: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).
|
||||
|
||||
## Bootloader
|
||||
|
||||
Enter the bootloader in 2 ways:
|
||||
|
||||
* **Physical reset button**: Briefly press the button on the back of the PCB
|
||||
* **Bootmagic reset**: Hold down the ESC key and plug in the keyboard
|
||||
|
||||
## Features
|
||||
|
||||
* RGB Matrix with 104 LEDs
|
||||
* Rotary encoder for volume control
|
||||
* Work timer with visual notifications
|
||||
* Southpaw numpad layout (numpad on the left side)
|
50
keyboards/tssouthpaw/config.h
Normal file
50
keyboards/tssouthpaw/config.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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
|
||||
|
||||
// LED indices for indicator LEDs
|
||||
#define CAPS_LOCK_LED 62 // LED index for Caps Lock indicator
|
||||
#define NUM_LOCK_LED 2 // LED index for Num Lock indicator
|
||||
#define MIC_MUTE_LED 3 // LED index for KC_MICMUTE
|
||||
|
||||
// Work Timer LED indices
|
||||
#define WORK_TIMER_LED_START 4 // F1 key LED
|
||||
#define WORK_TIMER_LED_END 15 // F12 key LED
|
||||
|
||||
// Work Timer EEPROM addresses
|
||||
// Using built-in wear leveling for RP2040
|
||||
#define EEPROM_WORK_TIMER_ACTIVE 32 // 1 byte - active state
|
||||
#define EEPROM_WORK_TIMER_START 33 // 4 bytes - start time
|
||||
#define EEPROM_WORK_TIMER_ELAPSED 37 // 4 bytes - elapsed work time
|
||||
// 4 bytes - total break time (37+4)
|
||||
// 1 byte - timer type (37+8)
|
||||
// 1 byte - break state (37+9)
|
||||
// 4 bytes - break start time (37+10)
|
||||
// Total: 15 bytes
|
||||
|
||||
// Work Timer Notification Configuration
|
||||
#define WORK_TIMER_NOTIFICATION_BRIGHTNESS 250 // Brightness for work timer notifications
|
||||
|
||||
// Minimum brightness for timer notifications to ensure visibility
|
||||
#define RGB_MATRIX_MINIMUM_BRIGHTNESS 64 // Ensures timer notifications remain visible
|
||||
|
||||
// Dynamic macro configuration
|
||||
#define DYNAMIC_MACRO_SIZE 128 // More efficient macro size
|
||||
|
||||
// RP2040 Settings
|
||||
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET // Enable double-tap reset
|
||||
#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U // Timeout window in ms
|
310
keyboards/tssouthpaw/keyboard.json
Normal file
310
keyboards/tssouthpaw/keyboard.json
Normal file
@ -0,0 +1,310 @@
|
||||
{
|
||||
"manufacturer": "TS Design Works LLC",
|
||||
"keyboard_name": "TS-Southpaw-Rev-1.6",
|
||||
"maintainer": "ts_design_works_llc",
|
||||
"bootloader": "rp2040",
|
||||
"processor": "RP2040",
|
||||
"diode_direction": "COL2ROW",
|
||||
"url": "https://github.com/tsdesignworks/tssouthpaw",
|
||||
"build": {
|
||||
"lto": true
|
||||
},
|
||||
"matrix_pins": {
|
||||
"cols": ["GP26", "GP25", "GP24", "GP23", "GP22", "GP16", "GP17", "GP15", "GP14", "GP13", "GP12", "GP11", "GP10", "GP9", "GP8", "GP7", "GP6", "GP5", "GP4", "GP3", "GP2"],
|
||||
"rows": ["GP29", "GP28", "GP21", "GP20", "GP19", "GP18"]
|
||||
},
|
||||
"usb": {
|
||||
"device_version": "0.0.2",
|
||||
"pid": "0x6163",
|
||||
"vid": "0x7856",
|
||||
"suspend_wakeup_delay": 200
|
||||
},
|
||||
"ws2812": {
|
||||
"driver": "vendor",
|
||||
"pin": "GP27"
|
||||
},
|
||||
"encoder": {
|
||||
"rotary": [
|
||||
{"pin_a": "GP1", "pin_b": "GP0", "resolution": 4}
|
||||
]
|
||||
},
|
||||
"features": {
|
||||
"bootmagic": true,
|
||||
"caps_word": true,
|
||||
"extrakey": true,
|
||||
"nkro": true,
|
||||
"rgb_matrix": true,
|
||||
"dynamic_macro": true,
|
||||
|
||||
},
|
||||
"debounce": 5,
|
||||
"rgb_matrix": {
|
||||
"driver": "ws2812",
|
||||
"sleep": true,
|
||||
"led_process_limit": 8,
|
||||
"default_mode": "SOLID_COLOR",
|
||||
"animations": {
|
||||
"solid_color": true,
|
||||
"breathing": true,
|
||||
"gradient_up_down": true,
|
||||
"gradient_left_right": true,
|
||||
"cycle_out_in": true,
|
||||
"cycle_out_in_dual": true,
|
||||
"cycle_pinwheel": true,
|
||||
"cycle_spiral": true,
|
||||
"rainbow_moving_chevron": true,
|
||||
"rainbow_beacon": true,
|
||||
"rainbow": true,
|
||||
"rainbow_swirl": true,
|
||||
"rainbow_pinwheels": true,
|
||||
"solid_reactive_simple": true,
|
||||
"solid_reactive": true,
|
||||
"splash": true,
|
||||
"multisplash": true,
|
||||
"solid_splash": true,
|
||||
"raindrops": true,
|
||||
"jellybean_raindrops": true,
|
||||
"typing_heatmap": true,
|
||||
"digital_rain": true,
|
||||
"pixel_rain": true
|
||||
},
|
||||
"layout": [
|
||||
{"matrix": [0, 0], "x": 0, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 1], "x": 11, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 2], "x": 22, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 3], "x": 34, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 8], "x": 45, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 9], "x": 56, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 10], "x": 68, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 11], "x": 78, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 12], "x": 89, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 13], "x": 100, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 14], "x": 112, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 15], "x": 123, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 16], "x": 134, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 17], "x": 146, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 18], "x": 157, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 19], "x": 168, "y": 0, "flags": 4},
|
||||
{"matrix": [0, 20], "x": 179, "y": 0, "flags": 4},
|
||||
{"matrix": [1, 0], "x": 191, "y": 0, "flags": 4},
|
||||
{"matrix": [1, 1], "x": 202, "y": 0, "flags": 4},
|
||||
{"matrix": [1, 2], "x": 213, "y": 0, "flags": 4},
|
||||
{"matrix": [1, 3], "x": 224, "y": 0, "flags": 4},
|
||||
{"matrix": [1, 4], "x": 0, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 5], "x": 11, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 6], "x": 22, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 7], "x": 34, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 8], "x": 45, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 9], "x": 56, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 10], "x": 68, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 11], "x": 78, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 12], "x": 89, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 13], "x": 100, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 14], "x": 112, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 15], "x": 123, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 16], "x": 134, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 17], "x": 146, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 18], "x": 157, "y": 13, "flags": 4},
|
||||
{"matrix": [1, 20], "x": 168, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 0], "x": 179, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 1], "x": 191, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 2], "x": 202, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 4], "x": 213, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 5], "x": 224, "y": 13, "flags": 4},
|
||||
{"matrix": [2, 7], "x": 0, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 8], "x": 11, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 9], "x": 22, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 10], "x": 34, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 11], "x": 45, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 12], "x": 56, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 13], "x": 68, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 14], "x": 78, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 15], "x": 89, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 16], "x": 100, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 17], "x": 112, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 18], "x": 123, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 19], "x": 134, "y": 26, "flags": 4},
|
||||
{"matrix": [2, 20], "x": 146, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 0], "x": 157, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 1], "x": 168, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 2], "x": 179, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 3], "x": 191, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 4], "x": 202, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 5], "x": 213, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 7], "x": 224, "y": 26, "flags": 4},
|
||||
{"matrix": [3, 8], "x": 0, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 9], "x": 11, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 10], "x": 22, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 11], "x": 34, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 12], "x": 45, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 13], "x": 56, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 14], "x": 68, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 15], "x": 78, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 16], "x": 89, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 17], "x": 100, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 18], "x": 112, "y": 38, "flags": 4},
|
||||
{"matrix": [3, 20], "x": 123, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 0], "x": 134, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 1], "x": 146, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 2], "x": 157, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 5], "x": 168, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 7], "x": 179, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 9], "x": 191, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 10], "x": 202, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 11], "x": 213, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 12], "x": 224, "y": 38, "flags": 4},
|
||||
{"matrix": [4, 13], "x": 0, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 14], "x": 11, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 15], "x": 22, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 16], "x": 34, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 17], "x": 45, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 18], "x": 56, "y": 51, "flags": 4},
|
||||
{"matrix": [4, 19], "x": 68, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 1], "x": 78, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 2], "x": 89, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 3], "x": 100, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 4], "x": 112, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 5], "x": 123, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 6], "x": 134, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 8], "x": 146, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 9], "x": 157, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 13], "x": 168, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 16], "x": 179, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 18], "x": 191, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 19], "x": 202, "y": 51, "flags": 4},
|
||||
{"matrix": [5, 20], "x": 213, "y": 51, "flags": 4}
|
||||
]
|
||||
},
|
||||
"layouts": {
|
||||
"LAYOUT": {
|
||||
"layout": [
|
||||
{"label": "Esc", "matrix": [0, 0], "x": 0, "y": 0},
|
||||
{"label": "Delete", "matrix": [0, 1], "x": 1, "y": 0},
|
||||
{"label": "Num Lock", "matrix": [0, 2], "x": 2, "y": 0},
|
||||
{"label": "MicMute", "matrix": [0, 3], "x": 3, "y": 0},
|
||||
{"label": "No", "matrix": [0, 4], "x": 4, "y": 0},
|
||||
{"label": "No", "matrix": [0, 5], "x": 5, "y": 0},
|
||||
{"label": "No", "matrix": [0, 6], "x": 6, "y": 0},
|
||||
{"label": "Mute", "matrix": [0, 7], "x": 7, "y": 0},
|
||||
{"label": "F1", "matrix": [0, 8], "x": 8, "y": 0},
|
||||
{"label": "F2", "matrix": [0, 9], "x": 9, "y": 0},
|
||||
{"label": "F3", "matrix": [0, 10], "x": 10, "y": 0},
|
||||
{"label": "F4", "matrix": [0, 11], "x": 11, "y": 0},
|
||||
{"label": "F5", "matrix": [0, 12], "x": 12, "y": 0},
|
||||
{"label": "F6", "matrix": [0, 13], "x": 13, "y": 0},
|
||||
{"label": "F7", "matrix": [0, 14], "x": 14, "y": 0},
|
||||
{"label": "F8", "matrix": [0, 15], "x": 15, "y": 0},
|
||||
{"label": "F9", "matrix": [0, 16], "x": 16, "y": 0},
|
||||
{"label": "F10", "matrix": [0, 17], "x": 17, "y": 0},
|
||||
{"label": "F11", "matrix": [0, 18], "x": 18, "y": 0},
|
||||
{"label": "F12", "matrix": [0, 19], "x": 19, "y": 0},
|
||||
{"label": "LGUI(KC_L)", "matrix": [0, 20], "x": 20, "y": 0},
|
||||
{"label": "Backspace", "matrix": [1, 0], "x": 0, "y": 1},
|
||||
{"label": "/", "matrix": [1, 1], "x": 1, "y": 1},
|
||||
{"label": "*", "matrix": [1, 2], "x": 2, "y": 1},
|
||||
{"label": "-", "matrix": [1, 3], "x": 3, "y": 1},
|
||||
{"label": "DM_REC1", "matrix": [1, 4], "x": 4, "y": 1},
|
||||
{"label": "DM_REC2", "matrix": [1, 5], "x": 5, "y": 1},
|
||||
{"label": "`", "matrix": [1, 6], "x": 6, "y": 1},
|
||||
{"label": "1", "matrix": [1, 7], "x": 7, "y": 1},
|
||||
{"label": "2", "matrix": [1, 8], "x": 8, "y": 1},
|
||||
{"label": "3", "matrix": [1, 9], "x": 9, "y": 1},
|
||||
{"label": "4", "matrix": [1, 10], "x": 10, "y": 1},
|
||||
{"label": "5", "matrix": [1, 11], "x": 11, "y": 1},
|
||||
{"label": "6", "matrix": [1, 12], "x": 12, "y": 1},
|
||||
{"label": "7", "matrix": [1, 13], "x": 13, "y": 1},
|
||||
{"label": "8", "matrix": [1, 14], "x": 14, "y": 1},
|
||||
{"label": "9", "matrix": [1, 15], "x": 15, "y": 1},
|
||||
{"label": "0", "matrix": [1, 16], "x": 16, "y": 1},
|
||||
{"label": "-", "matrix": [1, 17], "x": 17, "y": 1},
|
||||
{"label": "=", "matrix": [1, 18], "x": 18, "y": 1},
|
||||
{"label": "No", "matrix": [1, 19], "x": 19, "y": 1},
|
||||
{"label": "Backspace", "matrix": [1, 20], "x": 20, "y": 1},
|
||||
{"label": "7", "matrix": [2, 0], "x": 0, "y": 2},
|
||||
{"label": "8", "matrix": [2, 1], "x": 1, "y": 2},
|
||||
{"label": "9", "matrix": [2, 2], "x": 2, "y": 2},
|
||||
{"label": "NO", "matrix": [2, 3], "x": 3, "y": 2},
|
||||
{"label": "Calculator", "matrix": [2, 4], "x": 4, "y": 2},
|
||||
{"label": "Print Screen", "matrix": [2, 5], "x": 5, "y": 2},
|
||||
{"label": "No", "matrix": [2, 6], "x": 6, "y": 2},
|
||||
{"label": "Tab", "matrix": [2, 7], "x": 7, "y": 2},
|
||||
{"label": "Q", "matrix": [2, 8], "x": 8, "y": 2},
|
||||
{"label": "W", "matrix": [2, 9], "x": 9, "y": 2},
|
||||
{"label": "E", "matrix": [2, 10], "x": 10, "y": 2},
|
||||
{"label": "R", "matrix": [2, 11], "x": 11, "y": 2},
|
||||
{"label": "T", "matrix": [2, 12], "x": 12, "y": 2},
|
||||
{"label": "Y", "matrix": [2, 13], "x": 13, "y": 2},
|
||||
{"label": "U", "matrix": [2, 14], "x": 14, "y": 2},
|
||||
{"label": "I", "matrix": [2, 15], "x": 15, "y": 2},
|
||||
{"label": "O", "matrix": [2, 16], "x": 16, "y": 2},
|
||||
{"label": "P", "matrix": [2, 17], "x": 17, "y": 2},
|
||||
{"label": "[", "matrix": [2, 18], "x": 18, "y": 2},
|
||||
{"label": "]", "matrix": [2, 19], "x": 19, "y": 2},
|
||||
{"label": "\\", "matrix": [2, 20], "x": 20, "y": 2},
|
||||
{"label": "4", "matrix": [3, 0], "x": 0, "y": 3},
|
||||
{"label": "5", "matrix": [3, 1], "x": 1, "y": 3},
|
||||
{"label": "6", "matrix": [3, 2], "x": 2, "y": 3},
|
||||
{"label": "PSLS", "matrix": [3, 3], "x": 3, "y": 3},
|
||||
{"label": "DM_PLY1", "matrix": [3, 4], "x": 4, "y": 3},
|
||||
{"label": "DM_PLY2", "matrix": [3, 5], "x": 5, "y": 3},
|
||||
{"label": "No", "matrix": [3, 6], "x": 6, "y": 3},
|
||||
{"label": "Caps Lock", "matrix": [3, 7], "x": 7, "y": 3},
|
||||
{"label": "A", "matrix": [3, 8], "x": 8, "y": 3},
|
||||
{"label": "S", "matrix": [3, 9], "x": 9, "y": 3},
|
||||
{"label": "D", "matrix": [3, 10], "x": 10, "y": 3},
|
||||
{"label": "F", "matrix": [3, 11], "x": 11, "y": 3},
|
||||
{"label": "G", "matrix": [3, 12], "x": 12, "y": 3},
|
||||
{"label": "H", "matrix": [3, 13], "x": 13, "y": 3},
|
||||
{"label": "J", "matrix": [3, 14], "x": 14, "y": 3},
|
||||
{"label": "K", "matrix": [3, 15], "x": 15, "y": 3},
|
||||
{"label": "L", "matrix": [3, 16], "x": 16, "y": 3},
|
||||
{"label": ";", "matrix": [3, 17], "x": 17, "y": 3},
|
||||
{"label": "'", "matrix": [3, 18], "x": 18, "y": 3},
|
||||
{"label": "No", "matrix": [3, 19], "x": 19, "y": 3},
|
||||
{"label": "Enter", "matrix": [3, 20], "x": 20, "y": 3},
|
||||
{"label": "1", "matrix": [4, 0], "x": 0, "y": 4},
|
||||
{"label": "2", "matrix": [4, 1], "x": 1, "y": 4},
|
||||
{"label": "3", "matrix": [4, 2], "x": 2, "y": 4},
|
||||
{"label": "NO", "matrix": [4, 3], "x": 3, "y": 4},
|
||||
{"label": "No", "matrix": [4, 4], "x": 4, "y": 4},
|
||||
{"label": "Up", "matrix": [4, 5], "x": 5, "y": 4},
|
||||
{"label": "No", "matrix": [4, 6], "x": 6, "y": 4},
|
||||
{"label": "Shift", "matrix": [4, 7], "x": 7, "y": 4},
|
||||
{"label": "No", "matrix": [4, 8], "x": 8, "y": 4},
|
||||
{"label": "Z", "matrix": [4, 9], "x": 9, "y": 4},
|
||||
{"label": "X", "matrix": [4, 10], "x": 10, "y": 4},
|
||||
{"label": "C", "matrix": [4, 11], "x": 11, "y": 4},
|
||||
{"label": "V", "matrix": [4, 12], "x": 12, "y": 4},
|
||||
{"label": "B", "matrix": [4, 13], "x": 13, "y": 4},
|
||||
{"label": "N", "matrix": [4, 14], "x": 14, "y": 4},
|
||||
{"label": "M", "matrix": [4, 15], "x": 15, "y": 4},
|
||||
{"label": ",", "matrix": [4, 16], "x": 16, "y": 4},
|
||||
{"label": ".", "matrix": [4, 17], "x": 17, "y": 4},
|
||||
{"label": "/", "matrix": [4, 18], "x": 18, "y": 4},
|
||||
{"label": "Shift", "matrix": [4, 19], "x": 19, "y": 4},
|
||||
{"label": "No", "matrix": [4, 20], "x": 20, "y": 4},
|
||||
{"label": "No", "matrix": [5, 0], "x": 0, "y": 5},
|
||||
{"label": "0", "matrix": [5, 1], "x": 1, "y": 5},
|
||||
{"label": ".", "matrix": [5, 2], "x": 2, "y": 5},
|
||||
{"label": "Enter", "matrix": [5, 3], "x": 3, "y": 5},
|
||||
{"label": "Left", "matrix": [5, 4], "x": 4, "y": 5},
|
||||
{"label": "Down", "matrix": [5, 5], "x": 5, "y": 5},
|
||||
{"label": "Right", "matrix": [5, 6], "x": 6, "y": 5},
|
||||
{"label": "No", "matrix": [5, 7], "x": 7, "y": 5},
|
||||
{"label": "Ctrl", "matrix": [5, 8], "x": 8, "y": 5},
|
||||
{"label": "Alt", "matrix": [5, 9], "x": 9, "y": 5},
|
||||
{"label": "No", "matrix": [5, 10], "x": 10, "y": 5},
|
||||
{"label": "No", "matrix": [5, 11], "x": 11, "y": 5},
|
||||
{"label": "No", "matrix": [5, 12], "x": 12, "y": 5},
|
||||
{"label": "Space", "matrix": [5, 13], "x": 13, "y": 5},
|
||||
{"label": "No", "matrix": [5, 14], "x": 14, "y": 5},
|
||||
{"label": "No", "matrix": [5, 15], "x": 15, "y": 5},
|
||||
{"label": "Alt", "matrix": [5, 16], "x": 16, "y": 5},
|
||||
{"label": "No", "matrix": [5, 17], "x": 17, "y": 5},
|
||||
{"label": "GUI", "matrix": [5, 18], "x": 18, "y": 5},
|
||||
{"label": "MO(FN)", "matrix": [5, 19], "x": 19, "y": 5},
|
||||
{"label": "Ctrl", "matrix": [5, 20], "x": 20, "y": 5}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
169
keyboards/tssouthpaw/keymaps/default/keymap.c
Normal file
169
keyboards/tssouthpaw/keymaps/default/keymap.c
Normal file
@ -0,0 +1,169 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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
|
||||
#include "rgb_effects/rgb_effects.h"
|
||||
#include "rgb_effects/work_timer.h"
|
||||
|
||||
// Define layers
|
||||
enum layers {
|
||||
BASE, // Base layer
|
||||
FN, // Function layer
|
||||
};
|
||||
|
||||
// Define custom keycodes
|
||||
enum custom_keycodes {
|
||||
KC_MICMUTE = SAFE_RANGE, // Microphone mute key
|
||||
KC_MEDIA, // Media layer toggle
|
||||
KC_WRKTMR, // Work timer toggle
|
||||
KC_WRKPAU, // Work timer pause/resume
|
||||
KC_CPSWRD, // Caps word toggle
|
||||
KC_30MIN, // 30-minute timer
|
||||
KC_1HR, // 1-hour timer
|
||||
KC_4HR, // 4-hour timer
|
||||
KC_8HR, // 8-hour timer
|
||||
KC_10HR, // 10-hour timer
|
||||
};
|
||||
|
||||
#ifdef ENCODER_MAP_ENABLE
|
||||
const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
|
||||
[BASE] = { ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
|
||||
[FN] = { ENCODER_CCW_CW(_______, _______) },
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Key processing function
|
||||
* Handles custom keycodes and effects
|
||||
*/
|
||||
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
|
||||
// Handle ESC key with ripple effect
|
||||
if (keycode == KC_ESC) {
|
||||
if (record->event.pressed) {
|
||||
start_esc_ripple_effect();
|
||||
return true; // Let QMK handle the actual keypress
|
||||
} else {
|
||||
stop_esc_ripple_effect();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle other custom keycodes
|
||||
switch (keycode) {
|
||||
case KC_MUTE:
|
||||
if (record->event.pressed) {
|
||||
tap_code(KC_MUTE); // Trigger mute on key press
|
||||
tap_code(KC_MPLY); // Play/pause on key press
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_MICMUTE:
|
||||
if (record->event.pressed) {
|
||||
tap_code16(LCTL(LSFT(KC_M))); // MS Teams mic mute shortcut
|
||||
toggle_mic_mute_effect(); // Toggle the red-pulse effect
|
||||
}
|
||||
return false;
|
||||
|
||||
// Work timer keycodes
|
||||
case KC_WRKTMR:
|
||||
if (record->event.pressed) {
|
||||
toggle_work_timer(); // Toggle timer on/off
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_30MIN:
|
||||
if (record->event.pressed) {
|
||||
start_timer(TIMER_TYPE_30MIN);
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_1HR:
|
||||
if (record->event.pressed) {
|
||||
start_timer(TIMER_TYPE_1HR);
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_4HR:
|
||||
if (record->event.pressed) {
|
||||
start_timer(TIMER_TYPE_4HR);
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_8HR:
|
||||
if (record->event.pressed) {
|
||||
start_timer(TIMER_TYPE_8HR);
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_10HR:
|
||||
if (record->event.pressed) {
|
||||
start_timer(TIMER_TYPE_10HR);
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_WRKPAU:
|
||||
if (record->event.pressed) {
|
||||
toggle_pause_work_timer(); // Pause/resume the work timer
|
||||
}
|
||||
return false;
|
||||
|
||||
case KC_CPSWRD:
|
||||
if (record->event.pressed) {
|
||||
caps_word_toggle(); // Toggle caps word mode
|
||||
}
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true; // Process all other keycodes normally
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caps Word Callback
|
||||
* Determine which keys should maintain caps word mode
|
||||
*/
|
||||
bool caps_word_press_user(uint16_t keycode) {
|
||||
switch (keycode) {
|
||||
// These keys don't disable Caps Word
|
||||
case KC_A ... KC_Z:
|
||||
case KC_MINS:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keymap definitions
|
||||
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
|
||||
/* Base Layer */
|
||||
[BASE] = LAYOUT(
|
||||
KC_ESC, KC_DEL, KC_NUM_LOCK, KC_MICMUTE, KC_NO, KC_NO, KC_NO, KC_MUTE, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, LGUI(KC_L),
|
||||
KC_BSPC, KC_PSLS, KC_PAST, KC_PMNS, DM_REC1, DM_REC2, KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_NO, KC_BSPC,
|
||||
KC_P7, KC_P8, KC_P9, KC_NO, KC_CALC, KC_PSCR, KC_NO, KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS,
|
||||
KC_P4, KC_P5, KC_P6, KC_PPLS, DM_PLY1, DM_PLY2, KC_NO, KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_NO, KC_ENT,
|
||||
KC_P1, KC_P2, KC_P3, KC_NO, KC_NO, KC_UP, KC_NO, KC_LSFT, KC_NO, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_NO,
|
||||
KC_NO, KC_P0, KC_PDOT, KC_PENT, KC_LEFT, KC_DOWN, KC_RIGHT, KC_NO, KC_LCTL, KC_LALT, KC_NO, KC_NO, KC_NO, KC_SPC, KC_NO, KC_NO, KC_RALT, KC_NO, KC_RGUI, MO(FN), KC_RCTL
|
||||
),
|
||||
|
||||
/* Function Layer */
|
||||
[FN] = LAYOUT(
|
||||
_______, _______, _______, _______, KC_NO , KC_NO , KC_NO , _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
|
||||
_______, _______, _______, RGB_VAD, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_NO , _______,
|
||||
_______, KC_8HR , KC_10HR, KC_NO , _______, _______, KC_NO , _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
|
||||
KC_4HR , _______, _______, RGB_VAI, _______, _______, KC_NO , KC_CPSWRD, _______, KC_WRKTMR, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_NO , _______,
|
||||
KC_1HR , _______, KC_30MIN, KC_NO , KC_NO , RGB_HUI, KC_NO , _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_NO ,
|
||||
KC_NO , _______, _______, _______, RGB_RMOD, RGB_HUD, RGB_MOD, KC_NO , _______, _______, KC_NO , KC_NO , KC_NO , KC_WRKPAU, KC_NO , KC_NO , _______, KC_NO , _______, _______, _______
|
||||
),
|
||||
};
|
35
keyboards/tssouthpaw/keymaps/default/readme.md
Normal file
35
keyboards/tssouthpaw/keymaps/default/readme.md
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This is a readme.md file that should be placed in the keymaps/default/ directory
|
||||
* to provide information about the keymap.
|
||||
*/
|
||||
|
||||
## keymaps/default/readme.md
|
||||
|
||||
```markdown
|
||||
# Default Keymap for TS Southpaw
|
||||
|
||||
This is the default keymap for the TS Southpaw keyboard. It includes a base layer with a standard QWERTY layout plus southpaw numpad, and a function layer with work timer controls and RGB lighting adjustments.
|
||||
|
||||
## Features
|
||||
|
||||
- Southpaw numpad layout (numpad on the left side)
|
||||
- Work timer system with multiple duration options
|
||||
- Volume control via rotary encoder
|
||||
- RGB lighting effects and controls
|
||||
- Arrow keys highlighted in blue
|
||||
- ESC key ripple effect
|
||||
- Caps Lock and Num Lock indicators
|
||||
- Microphone mute function with indicator
|
||||
|
||||
## Layers
|
||||
|
||||
### Base Layer
|
||||
- Standard QWERTY layout
|
||||
- Left-side numpad
|
||||
- Media controls
|
||||
- Function key row (F1-F12)
|
||||
|
||||
### Function Layer (Accessed via key to left of right Control)
|
||||
- RGB lighting controls
|
||||
- Work timer controls (30MIN, 1HR, 4HR, 8HR, 10HR)
|
||||
- Caps Word toggle
|
267
keyboards/tssouthpaw/rgb_effects/rgb_effects.c
Normal file
267
keyboards/tssouthpaw/rgb_effects/rgb_effects.c
Normal file
@ -0,0 +1,267 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TS Southpaw RGB Effects System
|
||||
*/
|
||||
|
||||
#include "rgb_effects.h"
|
||||
#include "quantum.h"
|
||||
#include "rgb_matrix.h"
|
||||
#include "work_timer.h"
|
||||
|
||||
/**
|
||||
* Color definitions for RGB effects
|
||||
*/
|
||||
static const rgb_color_t CAPS_LOCK_COLOR = {255, 60, 0}; // Orange for Caps Lock
|
||||
static const rgb_color_t NUM_LOCK_COLOR = {255, 60, 0}; // Orange for Num Lock
|
||||
static const rgb_color_t ESC_RIPPLE_COLOR = {255, 0, 0}; // Red for ESC ripple effect
|
||||
static const rgb_color_t MIC_MUTE_COLOR = {255, 0, 0}; // Red for Mic Mute
|
||||
static const rgb_color_t ARROW_KEYS_COLOR = {0, 0, 255}; // Blue for arrow keys
|
||||
|
||||
/**
|
||||
* State tracking structure for LED effects
|
||||
*/
|
||||
typedef struct {
|
||||
bool active;
|
||||
uint8_t prev_rgb_mode;
|
||||
} led_state_t;
|
||||
|
||||
/**
|
||||
* State variables for LED indicators
|
||||
*/
|
||||
static led_state_t caps_lock_state = {false, 0};
|
||||
static led_state_t num_lock_state = {false, 0};
|
||||
static led_state_t mic_mute_state = {false, 0};
|
||||
|
||||
/**
|
||||
* ESC ripple effect state variables
|
||||
*/
|
||||
static struct {
|
||||
bool active;
|
||||
uint8_t step;
|
||||
uint16_t timer;
|
||||
bool expanding;
|
||||
} ripple_state = {false, 0, 0, true};
|
||||
|
||||
/**
|
||||
* ESC ripple effect LED configuration
|
||||
*/
|
||||
#define MAX_RIPPLE_LAYERS 6
|
||||
#define MAX_LEDS_PER_LAYER 10
|
||||
|
||||
// LED indices for each ripple layer
|
||||
// 255 is used as a placeholder for unused indices
|
||||
static const uint8_t esc_splash_ripple[MAX_RIPPLE_LAYERS][MAX_LEDS_PER_LAYER] = {
|
||||
{0, 255, 255, 255, 255, 255, 255, 255, 255, 255}, // Center LED only
|
||||
{1, 19, 255, 255, 255, 255, 255, 255, 255, 255}, // First ring
|
||||
{38, 39, 6, 7, 15, 255, 255, 255, 255, 255}, // Second ring
|
||||
{42, 43, 25, 48, 49, 255, 255, 255, 255, 255}, // Third ring
|
||||
{80, 81, 67, 68, 75, 76, 255, 255, 255, 255}, // Fourth ring
|
||||
{102, 103, 71, 36, 18, 93, 100, 255, 255, 255} // Fifth ring
|
||||
};
|
||||
|
||||
// Number of actual LEDs in each ripple layer for more efficient processing
|
||||
static const uint8_t leds_per_layer[MAX_RIPPLE_LAYERS] = {1, 2, 5, 5, 6, 7};
|
||||
|
||||
/**
|
||||
* Arrow key LED configuration
|
||||
*/
|
||||
static const uint8_t arrow_key_leds[] = {78, 94, 95, 96};
|
||||
#define ARROW_KEY_COUNT (sizeof(arrow_key_leds) / sizeof(arrow_key_leds[0]))
|
||||
|
||||
/**
|
||||
* Apply a pulsing effect to a specific LED
|
||||
*
|
||||
* @param led_index The index of the LED to apply the effect to
|
||||
* @param color The base RGB color to use
|
||||
*/
|
||||
static void apply_pulse_effect(uint8_t led_index, rgb_color_t color) {
|
||||
// Create a pulsing effect by varying brightness based on timer
|
||||
uint8_t brightness = abs((timer_read() / 2) % 510 - 255);
|
||||
rgb_matrix_set_color(led_index,
|
||||
(color.r * brightness) / 255,
|
||||
(color.g * brightness) / 255,
|
||||
(color.b * brightness) / 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the state change for an indicator LED
|
||||
*
|
||||
* @param active Whether the indicator should be active
|
||||
* @param state Pointer to the LED state structure to update
|
||||
*/
|
||||
static void update_indicator_state(bool active, led_state_t *state) {
|
||||
if (active) {
|
||||
if (!state->active) {
|
||||
state->prev_rgb_mode = rgb_matrix_get_mode();
|
||||
state->active = true;
|
||||
}
|
||||
} else if (state->active) {
|
||||
state->active = false;
|
||||
rgb_matrix_mode_noeeprom(state->prev_rgb_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Caps Lock indicator lighting
|
||||
*/
|
||||
void handle_caps_lock_rgb(void) {
|
||||
bool caps_active = host_keyboard_led_state().caps_lock;
|
||||
update_indicator_state(caps_active, &caps_lock_state);
|
||||
|
||||
if (caps_lock_state.active) {
|
||||
apply_pulse_effect(CAPS_LOCK_LED, CAPS_LOCK_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Num Lock indicator lighting
|
||||
* Note: Active when Num Lock is OFF (inverse logic)
|
||||
*/
|
||||
void handle_num_lock_rgb(void) {
|
||||
bool num_lock_inactive = !host_keyboard_led_state().num_lock;
|
||||
update_indicator_state(num_lock_inactive, &num_lock_state);
|
||||
|
||||
if (num_lock_state.active) {
|
||||
apply_pulse_effect(NUM_LOCK_LED, NUM_LOCK_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the Mic Mute effect state
|
||||
*/
|
||||
void toggle_mic_mute_effect(void) {
|
||||
if (!mic_mute_state.active) {
|
||||
mic_mute_state.prev_rgb_mode = rgb_matrix_get_mode();
|
||||
mic_mute_state.active = true;
|
||||
} else {
|
||||
mic_mute_state.active = false;
|
||||
rgb_matrix_mode_noeeprom(mic_mute_state.prev_rgb_mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Mic Mute indicator lighting
|
||||
*/
|
||||
void handle_mic_mute_rgb(void) {
|
||||
if (mic_mute_state.active) {
|
||||
apply_pulse_effect(MIC_MUTE_LED, MIC_MUTE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the ESC ripple effect
|
||||
*/
|
||||
void start_esc_ripple_effect(void) {
|
||||
ripple_state.active = true;
|
||||
ripple_state.step = 0;
|
||||
ripple_state.timer = timer_read();
|
||||
ripple_state.expanding = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the ESC ripple effect
|
||||
*/
|
||||
void stop_esc_ripple_effect(void) {
|
||||
ripple_state.active = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the ESC ripple effect animation
|
||||
*/
|
||||
void handle_esc_ripple_effect(void) {
|
||||
if (!ripple_state.active) return;
|
||||
|
||||
// Update ripple step based on timer (every 15ms)
|
||||
if (timer_elapsed(ripple_state.timer) > 15) {
|
||||
ripple_state.timer = timer_read();
|
||||
|
||||
// Update step based on direction
|
||||
if (ripple_state.expanding) {
|
||||
ripple_state.step++;
|
||||
if (ripple_state.step >= MAX_RIPPLE_LAYERS - 1) {
|
||||
ripple_state.step = MAX_RIPPLE_LAYERS - 1;
|
||||
ripple_state.expanding = false;
|
||||
}
|
||||
} else {
|
||||
if (ripple_state.step > 0) {
|
||||
ripple_state.step--;
|
||||
} else {
|
||||
ripple_state.expanding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turn off all ripple LEDs first
|
||||
for (uint8_t layer = 0; layer < MAX_RIPPLE_LAYERS; layer++) {
|
||||
for (uint8_t i = 0; i < leds_per_layer[layer]; i++) {
|
||||
uint8_t led_index = esc_splash_ripple[layer][i];
|
||||
if (led_index != 255) { // Skip placeholder values
|
||||
rgb_matrix_set_color(led_index, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set active layer LEDs
|
||||
uint8_t current_layer = ripple_state.step;
|
||||
for (uint8_t i = 0; i < leds_per_layer[current_layer]; i++) {
|
||||
uint8_t led_index = esc_splash_ripple[current_layer][i];
|
||||
if (led_index != 255) { // Skip placeholder values
|
||||
rgb_matrix_set_color(led_index,
|
||||
ESC_RIPPLE_COLOR.r,
|
||||
ESC_RIPPLE_COLOR.g,
|
||||
ESC_RIPPLE_COLOR.b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle arrow key highlighting
|
||||
*/
|
||||
static void handle_arrow_keys(void) {
|
||||
// Only override arrows in SOLID_COLOR mode
|
||||
if (rgb_matrix_get_mode() == RGB_MATRIX_SOLID_COLOR) {
|
||||
uint8_t val = rgb_matrix_get_val(); // current brightness (0–255)
|
||||
|
||||
for (uint8_t i = 0; i < ARROW_KEY_COUNT; i++) {
|
||||
rgb_matrix_set_color(arrow_key_leds[i],
|
||||
ARROW_KEYS_COLOR.r * val / 255,
|
||||
ARROW_KEYS_COLOR.g * val / 255,
|
||||
ARROW_KEYS_COLOR.b * val / 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined overlay for arrow-keys + reactive handlers
|
||||
*/
|
||||
bool rgb_matrix_indicators_user(void) {
|
||||
// Apply arrow key highlighting
|
||||
handle_arrow_keys();
|
||||
|
||||
// Run all reactive handlers
|
||||
handle_caps_lock_rgb();
|
||||
handle_num_lock_rgb();
|
||||
handle_mic_mute_rgb();
|
||||
handle_esc_ripple_effect();
|
||||
|
||||
// Handle work timer through extern call (implemented in work_timer.c)
|
||||
update_work_timer();
|
||||
handle_work_timer();
|
||||
|
||||
return true; // continue with the normal effect pipeline
|
||||
}
|
85
keyboards/tssouthpaw/rgb_effects/rgb_effects.h
Normal file
85
keyboards/tssouthpaw/rgb_effects/rgb_effects.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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"
|
||||
|
||||
// LED indices for indicator LEDs
|
||||
#define CAPS_LOCK_LED 62 // LED index for Caps Lock indicator
|
||||
#define NUM_LOCK_LED 2 // LED index for Num Lock indicator
|
||||
#define MIC_MUTE_LED 3 // LED index for KC_MICMUTE
|
||||
|
||||
/**
|
||||
* RGB Effect types enumeration
|
||||
* For potential future extension of effect types
|
||||
*/
|
||||
typedef enum {
|
||||
RGB_EFFECT_NONE,
|
||||
RGB_EFFECT_BREATHING,
|
||||
RGB_EFFECT_RAINBOW,
|
||||
RGB_EFFECT_CUSTOM
|
||||
} rgb_effect_type_t;
|
||||
|
||||
/**
|
||||
* RGB Color structure for cleaner color definitions
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
} rgb_color_t;
|
||||
|
||||
/**
|
||||
* Function declarations for handling RGB effects
|
||||
*/
|
||||
|
||||
// Handle LED indicators for keyboard state
|
||||
void handle_caps_lock_rgb(void);
|
||||
void handle_num_lock_rgb(void);
|
||||
void handle_mic_mute_rgb(void);
|
||||
|
||||
// Ripple effect functions
|
||||
void handle_esc_ripple_effect(void);
|
||||
void start_esc_ripple_effect(void);
|
||||
void stop_esc_ripple_effect(void);
|
||||
|
||||
// Microphone mute effect toggle
|
||||
void toggle_mic_mute_effect(void);
|
||||
|
||||
/**
|
||||
* Utility functions for RGB effects
|
||||
*/
|
||||
|
||||
// Gets the keyboard's current RGB mode
|
||||
static inline uint8_t get_current_rgb_mode(void) {
|
||||
return rgb_matrix_get_mode();
|
||||
}
|
||||
|
||||
// Sets an LED to a specific RGB color
|
||||
static inline void set_led_color(uint8_t led_index, uint8_t r, uint8_t g, uint8_t b) {
|
||||
rgb_matrix_set_color(led_index, r, g, b);
|
||||
}
|
||||
|
||||
// Calculate color gradient between two colors based on progress (0.0 - 1.0)
|
||||
static inline rgb_color_t calculate_gradient_color(rgb_color_t start, rgb_color_t end, float progress) {
|
||||
rgb_color_t result;
|
||||
result.r = start.r + (int)((float)(end.r - start.r) * progress);
|
||||
result.g = start.g + (int)((float)(end.g - start.g) * progress);
|
||||
result.b = start.b + (int)((float)(end.b - start.b) * progress);
|
||||
return result;
|
||||
}
|
||||
|
393
keyboards/tssouthpaw/rgb_effects/work_timer.c
Normal file
393
keyboards/tssouthpaw/rgb_effects/work_timer.c
Normal file
@ -0,0 +1,393 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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 "work_timer.h"
|
||||
#include "quantum.h"
|
||||
#include "rgb_effects/rgb_effects.h"
|
||||
#include "timer.h"
|
||||
|
||||
// Simple work timer structure
|
||||
typedef struct {
|
||||
// Basic state
|
||||
bool active; // Is the timer running?
|
||||
bool paused; // Is the timer paused?
|
||||
work_timer_type_t type; // Timer type (30min, 1hr, etc.)
|
||||
|
||||
// Time tracking
|
||||
uint32_t start_time; // When this timer started
|
||||
uint32_t end_time; // When this timer should end
|
||||
uint32_t pause_time; // When timer was paused (if paused)
|
||||
uint32_t break_time; // Break duration for this timer
|
||||
|
||||
// Midpoint break tracking
|
||||
uint32_t mid_point; // Time value at midpoint (when break starts)
|
||||
uint32_t mid_point_end; // Time value when break ends
|
||||
} work_timer_t;
|
||||
|
||||
// Global timer state
|
||||
static work_timer_t timer = {0};
|
||||
|
||||
// Timer colors
|
||||
static const rgb_color_t TIMER_START_COLOR = {WORK_TIMER_START_R, WORK_TIMER_START_G, WORK_TIMER_START_B};
|
||||
static const rgb_color_t TIMER_MID_COLOR = {WORK_TIMER_MID_R, WORK_TIMER_MID_G, WORK_TIMER_MID_B};
|
||||
static const rgb_color_t TIMER_END_COLOR = {WORK_TIMER_END_R, WORK_TIMER_END_G, WORK_TIMER_END_B};
|
||||
static const rgb_color_t TIMER_BREAK_COLOR = {WORK_TIMER_LUNCH_R, WORK_TIMER_LUNCH_G, WORK_TIMER_LUNCH_B};
|
||||
|
||||
// Function declarations
|
||||
static void save_timer_state(void);
|
||||
static void load_timer_state(void);
|
||||
static void display_progress_bar(uint8_t num_leds, float progress, float brightness);
|
||||
|
||||
/**
|
||||
* Configure a timer with the specified type
|
||||
*/
|
||||
static void configure_timer(work_timer_type_t type) {
|
||||
timer.type = type;
|
||||
|
||||
// Set break duration based on timer type
|
||||
switch (type) {
|
||||
case TIMER_TYPE_30MIN:
|
||||
timer.break_time = MID_BREAK_30MIN_DURATION;
|
||||
break;
|
||||
|
||||
case TIMER_TYPE_1HR:
|
||||
timer.break_time = MID_BREAK_1HR_DURATION;
|
||||
break;
|
||||
|
||||
case TIMER_TYPE_4HR:
|
||||
timer.break_time = MID_BREAK_4HR_DURATION;
|
||||
break;
|
||||
|
||||
case TIMER_TYPE_8HR:
|
||||
timer.break_time = LUNCH_BREAK_DURATION;
|
||||
break;
|
||||
|
||||
case TIMER_TYPE_10HR:
|
||||
timer.break_time = LUNCH_BREAK_DURATION;
|
||||
break;
|
||||
|
||||
default:
|
||||
timer.break_time = LUNCH_BREAK_DURATION;
|
||||
break;
|
||||
}
|
||||
|
||||
// Calculate timer duration (excluding break)
|
||||
uint32_t work_duration = 0;
|
||||
switch (type) {
|
||||
case TIMER_TYPE_30MIN: work_duration = TIMER_30MIN_DURATION; break;
|
||||
case TIMER_TYPE_1HR: work_duration = TIMER_1HR_DURATION; break;
|
||||
case TIMER_TYPE_4HR: work_duration = TIMER_4HR_DURATION; break;
|
||||
case TIMER_TYPE_8HR: work_duration = TIMER_8HR_DURATION; break;
|
||||
case TIMER_TYPE_10HR: work_duration = TIMER_10HR_DURATION; break;
|
||||
default: work_duration = TIMER_8HR_DURATION; break;
|
||||
}
|
||||
|
||||
// Calculate end time and midpoint
|
||||
timer.end_time = timer.start_time + work_duration;
|
||||
timer.mid_point = timer.start_time + (work_duration / 2);
|
||||
timer.mid_point_end = timer.mid_point + timer.break_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save timer state to EEPROM
|
||||
*/
|
||||
static void save_timer_state(void) {
|
||||
uint8_t buffer[24] = {0};
|
||||
|
||||
// Basic state
|
||||
buffer[0] = timer.active ? 1 : 0;
|
||||
buffer[1] = timer.paused ? 1 : 0;
|
||||
buffer[2] = (uint8_t)timer.type;
|
||||
|
||||
// Time values
|
||||
memcpy(&buffer[3], &timer.start_time, sizeof(uint32_t));
|
||||
memcpy(&buffer[7], &timer.end_time, sizeof(uint32_t));
|
||||
memcpy(&buffer[11], &timer.pause_time, sizeof(uint32_t));
|
||||
memcpy(&buffer[15], &timer.break_time, sizeof(uint32_t));
|
||||
memcpy(&buffer[19], &timer.mid_point, sizeof(uint32_t));
|
||||
|
||||
// Write to EEPROM
|
||||
eeprom_update_block(buffer, (void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load timer state from EEPROM
|
||||
*/
|
||||
static void load_timer_state(void) {
|
||||
uint8_t buffer[24] = {0};
|
||||
|
||||
// Read from EEPROM
|
||||
eeprom_read_block(buffer, (const void *)EEPROM_WORK_TIMER_ACTIVE, sizeof(buffer));
|
||||
|
||||
// Basic state
|
||||
timer.active = buffer[0] ? true : false;
|
||||
timer.paused = buffer[1] ? true : false;
|
||||
timer.type = (work_timer_type_t)buffer[2];
|
||||
|
||||
// Time values
|
||||
memcpy(&timer.start_time, &buffer[3], sizeof(uint32_t));
|
||||
memcpy(&timer.end_time, &buffer[7], sizeof(uint32_t));
|
||||
memcpy(&timer.pause_time, &buffer[11], sizeof(uint32_t));
|
||||
memcpy(&timer.break_time, &buffer[15], sizeof(uint32_t));
|
||||
memcpy(&timer.mid_point, &buffer[19], sizeof(uint32_t));
|
||||
|
||||
// Calculate mid_point_end from loaded values
|
||||
if (timer.active) {
|
||||
timer.mid_point_end = timer.mid_point + timer.break_time;
|
||||
|
||||
// Verify timer is still valid
|
||||
if (!timer.paused) {
|
||||
uint32_t current_time = timer_read32();
|
||||
if (current_time > timer.end_time) {
|
||||
timer.active = false;
|
||||
save_timer_state();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display progress bar with gradient colors
|
||||
*/
|
||||
static void display_progress_bar(uint8_t num_leds, float progress, float brightness) {
|
||||
// Progress is a value from 0.0 to 1.0
|
||||
float hours_per_led = 1.0f / (float)num_leds;
|
||||
|
||||
// Determine how many LEDs should be lit
|
||||
uint8_t leds_lit = (uint8_t)(progress / hours_per_led);
|
||||
if (leds_lit > num_leds) leds_lit = num_leds;
|
||||
|
||||
// Calculate progress for the partially-lit LED
|
||||
float current_led_progress = (progress - (leds_lit * hours_per_led)) / hours_per_led;
|
||||
|
||||
// Set colors for each LED in the progress bar
|
||||
for (uint8_t i = 0; i < num_leds; i++) {
|
||||
rgb_color_t color = {0, 0, 0}; // Default to off
|
||||
|
||||
if (i < leds_lit) {
|
||||
// Fully lit LED - calculate gradient color based on position
|
||||
float position = (float)i / (float)(num_leds - 1);
|
||||
|
||||
// Use green to orange gradient for most of bar, orange to red at end
|
||||
if (position < 0.7f) {
|
||||
// First 70% is green to orange
|
||||
float adj_progress = position / 0.7f;
|
||||
color = calculate_gradient_color(TIMER_START_COLOR, TIMER_MID_COLOR, adj_progress);
|
||||
} else {
|
||||
// Last 30% is orange to red
|
||||
float adj_progress = (position - 0.7f) / 0.3f;
|
||||
color = calculate_gradient_color(TIMER_MID_COLOR, TIMER_END_COLOR, adj_progress);
|
||||
}
|
||||
|
||||
// Apply brightness
|
||||
color.r = (uint8_t)((float)color.r * brightness);
|
||||
color.g = (uint8_t)((float)color.g * brightness);
|
||||
color.b = (uint8_t)((float)color.b * brightness);
|
||||
}
|
||||
else if (i == leds_lit && current_led_progress > 0.0f) {
|
||||
// Partially lit LED
|
||||
float position = (float)i / (float)(num_leds - 1);
|
||||
rgb_color_t full_color;
|
||||
|
||||
if (position < 0.7f) {
|
||||
float adj_progress = position / 0.7f;
|
||||
full_color = calculate_gradient_color(TIMER_START_COLOR, TIMER_MID_COLOR, adj_progress);
|
||||
} else {
|
||||
float adj_progress = (position - 0.7f) / 0.3f;
|
||||
full_color = calculate_gradient_color(TIMER_MID_COLOR, TIMER_END_COLOR, adj_progress);
|
||||
}
|
||||
|
||||
// Dim by progress and brightness
|
||||
color.r = (uint8_t)((float)full_color.r * current_led_progress * brightness);
|
||||
color.g = (uint8_t)((float)full_color.g * current_led_progress * brightness);
|
||||
color.b = (uint8_t)((float)full_color.b * current_led_progress * brightness);
|
||||
}
|
||||
|
||||
// Set the LED color
|
||||
rgb_matrix_set_color(WORK_TIMER_LED_START + i, color.r, color.g, color.b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the work timer
|
||||
*/
|
||||
void work_timer_init(void) {
|
||||
load_timer_state();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the work timer on/off
|
||||
*/
|
||||
void toggle_work_timer(void) {
|
||||
if (timer.active) {
|
||||
// Turn off timer
|
||||
timer.active = false;
|
||||
timer.paused = false;
|
||||
|
||||
save_timer_state();
|
||||
|
||||
// Force RGB refresh
|
||||
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
|
||||
} else {
|
||||
// Start a new timer
|
||||
timer.active = true;
|
||||
timer.paused = false;
|
||||
timer.start_time = timer_read32();
|
||||
timer.pause_time = 0;
|
||||
|
||||
// Configure for current timer type
|
||||
configure_timer(timer.type);
|
||||
save_timer_state();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a specific timer type
|
||||
*/
|
||||
void start_timer(work_timer_type_t type) {
|
||||
// Start fresh
|
||||
timer.active = true;
|
||||
timer.paused = false;
|
||||
timer.start_time = timer_read32();
|
||||
timer.pause_time = 0;
|
||||
|
||||
// Configure based on type
|
||||
configure_timer(type);
|
||||
save_timer_state();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause or resume the timer
|
||||
*/
|
||||
void toggle_pause_work_timer(void) {
|
||||
if (!timer.active) return;
|
||||
|
||||
if (timer.paused) {
|
||||
// Resume - calculate time paused and adjust timers
|
||||
uint32_t current_time = timer_read32();
|
||||
uint32_t pause_duration = current_time - timer.pause_time;
|
||||
|
||||
// Extend all time points by pause duration
|
||||
timer.end_time += pause_duration;
|
||||
timer.mid_point += pause_duration;
|
||||
timer.mid_point_end += pause_duration;
|
||||
|
||||
timer.paused = false;
|
||||
} else {
|
||||
// Pause
|
||||
timer.pause_time = timer_read32();
|
||||
timer.paused = true;
|
||||
}
|
||||
|
||||
save_timer_state();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the blue pulse effect should be active
|
||||
*/
|
||||
bool is_timer_pulse_active(void) {
|
||||
if (!timer.active || timer.paused) return false;
|
||||
|
||||
uint32_t current_time = timer_read32();
|
||||
|
||||
// Only return true when we're in the mid-point break window
|
||||
return (current_time >= timer.mid_point && current_time < timer.mid_point_end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update timer state
|
||||
*/
|
||||
void update_work_timer(void) {
|
||||
if (!timer.active || timer.paused) return;
|
||||
|
||||
uint32_t current_time = timer_read32();
|
||||
|
||||
// Check if timer has ended
|
||||
if (current_time >= timer.end_time) {
|
||||
timer.active = false;
|
||||
save_timer_state();
|
||||
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle work timer visualization
|
||||
*/
|
||||
void handle_work_timer(void) {
|
||||
static bool was_active = false;
|
||||
|
||||
if (!timer.active) {
|
||||
if (was_active) {
|
||||
rgb_matrix_mode_noeeprom(rgb_matrix_get_mode());
|
||||
was_active = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
was_active = true;
|
||||
|
||||
// Get current time and brightness
|
||||
uint32_t current_time = timer_read32();
|
||||
uint8_t rgb_brightness = rgb_matrix_get_val();
|
||||
float brightness_factor = (float)rgb_brightness / 255.0f;
|
||||
|
||||
// Number of LEDs in the progress bar
|
||||
const uint8_t num_leds = WORK_TIMER_LED_END - WORK_TIMER_LED_START + 1;
|
||||
|
||||
// Calculate progress based on active timer duration
|
||||
float progress;
|
||||
if (timer.paused) {
|
||||
// When paused, use pause time for progress calculation
|
||||
progress = (float)(timer.pause_time - timer.start_time) /
|
||||
(float)(timer.end_time - timer.start_time);
|
||||
} else {
|
||||
// When active, use current time for progress calculation
|
||||
progress = (float)(current_time - timer.start_time) /
|
||||
(float)(timer.end_time - timer.start_time);
|
||||
}
|
||||
|
||||
// Clamp progress to valid range
|
||||
if (progress > 1.0f) progress = 1.0f;
|
||||
if (progress < 0.0f) progress = 0.0f;
|
||||
|
||||
// Check if we're in the break window (midpoint of the timer)
|
||||
if (!timer.paused && current_time >= timer.mid_point && current_time < timer.mid_point_end) {
|
||||
// In break time - show blue pulse
|
||||
rgb_color_t color = TIMER_BREAK_COLOR;
|
||||
|
||||
// Slow pulse for break time
|
||||
uint8_t break_pulse = timer_read() / 10;
|
||||
float break_intensity = (float)(abs(break_pulse % 510 - 255)) / 255.0f;
|
||||
|
||||
// Apply blue pulse to all LEDs in progress bar
|
||||
for (uint8_t i = 0; i < num_leds; i++) {
|
||||
rgb_matrix_set_color(WORK_TIMER_LED_START + i,
|
||||
(uint8_t)((float)color.r * break_intensity * brightness_factor),
|
||||
(uint8_t)((float)color.g * break_intensity * brightness_factor),
|
||||
(uint8_t)((float)color.b * break_intensity * brightness_factor));
|
||||
}
|
||||
} else {
|
||||
// Normal progress display
|
||||
display_progress_bar(num_leds, progress, brightness_factor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Periodic timer task
|
||||
*/
|
||||
void work_timer_task(void) {
|
||||
update_work_timer();
|
||||
}
|
89
keyboards/tssouthpaw/rgb_effects/work_timer.h
Normal file
89
keyboards/tssouthpaw/rgb_effects/work_timer.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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"
|
||||
|
||||
// Work Timer type enum
|
||||
typedef enum {
|
||||
TIMER_TYPE_30MIN,
|
||||
TIMER_TYPE_1HR,
|
||||
TIMER_TYPE_4HR,
|
||||
TIMER_TYPE_8HR,
|
||||
TIMER_TYPE_10HR
|
||||
} work_timer_type_t;
|
||||
|
||||
// Work Timer state enum
|
||||
typedef enum {
|
||||
TIMER_STATE_INACTIVE,
|
||||
TIMER_STATE_ACTIVE,
|
||||
TIMER_STATE_PAUSED,
|
||||
TIMER_STATE_LUNCH,
|
||||
TIMER_STATE_MID_BREAK,
|
||||
TIMER_STATE_COMPLETED
|
||||
} work_timer_state_t;
|
||||
|
||||
// Work Timer Color definitions - modified for more yellow/orange focus
|
||||
// Start point color (green)
|
||||
#define WORK_TIMER_START_R 0
|
||||
#define WORK_TIMER_START_G 255
|
||||
#define WORK_TIMER_START_B 0
|
||||
|
||||
// Mid point color (orange)
|
||||
#define WORK_TIMER_MID_R 255
|
||||
#define WORK_TIMER_MID_G 112
|
||||
#define WORK_TIMER_MID_B 0
|
||||
|
||||
// End point color (red)
|
||||
#define WORK_TIMER_END_R 255
|
||||
#define WORK_TIMER_END_G 0
|
||||
#define WORK_TIMER_END_B 0
|
||||
|
||||
// Lunch break color (blue)
|
||||
#define WORK_TIMER_LUNCH_R 0
|
||||
#define WORK_TIMER_LUNCH_G 0
|
||||
#define WORK_TIMER_LUNCH_B 255
|
||||
|
||||
// Standard timer durations (in milliseconds)
|
||||
// IMPORTANT: These represent actual working time (excluding breaks)
|
||||
#define TIMER_30MIN_DURATION 1800000 // 30 minutes
|
||||
#define TIMER_1HR_DURATION 3600000 // 1 hour
|
||||
#define TIMER_4HR_DURATION 14400000 // 4 hours
|
||||
#define TIMER_8HR_DURATION 28800000 // 8 hours (real world 9 hours with lunch)
|
||||
#define TIMER_10HR_DURATION 36000000 // 10 hours (real world 11 hours with lunch)
|
||||
|
||||
// Break durations
|
||||
#define BREAK_WARNING_TIME 60000 // 60 seconds (kept for calculations but not used for warnings)
|
||||
#define MID_BREAK_30MIN_DURATION 30000 // 30 seconds
|
||||
#define MID_BREAK_1HR_DURATION 45000 // 45 seconds
|
||||
#define MID_BREAK_4HR_DURATION 60000 // 60 seconds
|
||||
#define LUNCH_BREAK_DURATION 3600000 // 1 hour default
|
||||
|
||||
// Define maximum valid duration for sanity checking
|
||||
#define TIMER_MAX_DURATION 50400000 // 14 hours - sanity check
|
||||
|
||||
// Function declarations for work timer
|
||||
void work_timer_init(void);
|
||||
void start_timer(work_timer_type_t timer_type);
|
||||
void toggle_pause_work_timer(void);
|
||||
void update_work_timer(void);
|
||||
void handle_work_timer(void);
|
||||
void toggle_work_timer(void);
|
||||
void work_timer_task(void);
|
||||
|
||||
// Functions for checking timer states
|
||||
bool is_timer_pulse_active(void);
|
5
keyboards/tssouthpaw/rules.mk
Normal file
5
keyboards/tssouthpaw/rules.mk
Normal file
@ -0,0 +1,5 @@
|
||||
# This is the rules.mk file for the RGB Effects firmware.
|
||||
SRC += rgb_effects/rgb_effects.c rgb_effects/work_timer.c
|
||||
|
||||
# Enable encoder functionality
|
||||
ENCODER_ENABLE = yes
|
88
keyboards/tssouthpaw/tssouthpaw.c
Normal file
88
keyboards/tssouthpaw/tssouthpaw.c
Normal file
@ -0,0 +1,88 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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 "tssouthpaw.h"
|
||||
#include "rgb_effects/rgb_effects.h"
|
||||
#include "rgb_effects/work_timer.h"
|
||||
|
||||
/**
|
||||
* Keyboard initialization
|
||||
* Called once at startup
|
||||
*/
|
||||
void keyboard_post_init_kb(void) {
|
||||
// Initialize RGB lighting effects
|
||||
rgb_matrix_mode_noeeprom(RGB_MATRIX_SOLID_COLOR);
|
||||
rgb_matrix_sethsv_noeeprom(HSV_GREEN); // Set default color to green
|
||||
|
||||
// Enable N-Key Rollover
|
||||
keymap_config.nkro = true;
|
||||
|
||||
// Load saved work timer state from EEPROM if applicable
|
||||
work_timer_init();
|
||||
|
||||
// Continue with any user-level initialization
|
||||
keyboard_post_init_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Power management function - Called when system is going to sleep
|
||||
*/
|
||||
void suspend_power_down_kb(void) {
|
||||
// If timer pulse is active, ensure RGB matrix remains active
|
||||
if (is_timer_pulse_active()) {
|
||||
// Ensure RGB matrix has reasonable brightness for notifications
|
||||
uint8_t val = rgb_matrix_get_val();
|
||||
if (val < RGB_MATRIX_MINIMUM_BRIGHTNESS) {
|
||||
rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), RGB_MATRIX_MINIMUM_BRIGHTNESS);
|
||||
}
|
||||
|
||||
// Return early to skip deep sleep routines
|
||||
return;
|
||||
}
|
||||
|
||||
// Only disable RGB effects when no timer pulse is active
|
||||
rgb_matrix_set_suspend_state(true);
|
||||
suspend_power_down_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Power management function - Called when system is waking from sleep
|
||||
*/
|
||||
void suspend_wakeup_init_kb(void) {
|
||||
// Make sure the timer pulse state is properly reflected
|
||||
// This ensures the timer visuals are immediately visible
|
||||
if (is_timer_pulse_active()) {
|
||||
// Ensure RGB matrix effect is enabled with proper brightness
|
||||
uint8_t val = rgb_matrix_get_val();
|
||||
if (val < RGB_MATRIX_MINIMUM_BRIGHTNESS) {
|
||||
rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), RGB_MATRIX_MINIMUM_BRIGHTNESS);
|
||||
}
|
||||
}
|
||||
|
||||
suspend_wakeup_init_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular task hook to ensure timer updates even during suspend
|
||||
*/
|
||||
void housekeeping_task_kb(void) {
|
||||
// Update work timer even during suspend
|
||||
if (is_timer_pulse_active()) {
|
||||
update_work_timer();
|
||||
handle_work_timer();
|
||||
}
|
||||
}
|
||||
|
21
keyboards/tssouthpaw/tssouthpaw.h
Normal file
21
keyboards/tssouthpaw/tssouthpaw.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* Copyright 2025 TS Design Works LLC
|
||||
*
|
||||
* 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 to ensure backward compatibility with existing user keymaps
|
Loading…
Reference in New Issue
Block a user