Compare commits
1 Commits
xap
..
venv-script
| Author | SHA1 | Date | |
|---|---|---|---|
| 855b234813 |
@@ -36,7 +36,6 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip3 install -r requirements-dev.txt
|
||||
apt-get update && apt-get install -y rsync doxygen
|
||||
# install nvm
|
||||
touch $HOME/.bashrc
|
||||
@@ -57,7 +56,7 @@ jobs:
|
||||
|
||||
- name: Deploy
|
||||
if: ${{ github.event_name == 'push' && github.repository == 'qmk/qmk_firmware' }}
|
||||
uses: JamesIves/github-pages-deploy-action@v4.7.2
|
||||
uses: JamesIves/github-pages-deploy-action@v4.7.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: gh-pages
|
||||
|
||||
@@ -21,10 +21,6 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install python reqs
|
||||
run: |
|
||||
python3 -m pip install -r requirements.txt
|
||||
|
||||
- name: Run qmk generators
|
||||
run: |
|
||||
util/regen.sh
|
||||
|
||||
@@ -21,10 +21,6 @@ jobs:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip3 install -r requirements-dev.txt
|
||||
|
||||
- name: Run qmk generators
|
||||
run: |
|
||||
util/regen.sh
|
||||
|
||||
@@ -103,7 +103,6 @@ secrets.tar
|
||||
# Python things
|
||||
__pycache__
|
||||
.python-version
|
||||
*.egg-info
|
||||
.venv
|
||||
|
||||
# Prerequisites for updating ChibiOS
|
||||
|
||||
@@ -197,10 +197,8 @@ ifneq ("$(wildcard $(KEYMAP_JSON))", "")
|
||||
KEYMAP_C := $(INTERMEDIATE_OUTPUT)/src/keymap.c
|
||||
KEYMAP_H := $(INTERMEDIATE_OUTPUT)/src/config.h
|
||||
|
||||
ifeq ($(OTHER_KEYMAP_C),)
|
||||
# Load the keymap-level rules.mk if exists (and we havent already loaded it for keymap.c)
|
||||
-include $(KEYMAP_PATH)/rules.mk
|
||||
endif
|
||||
# Load the keymap-level rules.mk if exists
|
||||
-include $(KEYMAP_PATH)/rules.mk
|
||||
|
||||
# Load any rules.mk content from keymap.json
|
||||
INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --output $(INTERMEDIATE_OUTPUT)/src/rules.mk $(KEYMAP_JSON))
|
||||
|
||||
@@ -45,10 +45,6 @@ else ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), api)
|
||||
OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(XAP_ENABLE)), yes)
|
||||
include $(BUILDDEFS_PATH)/xap.mk
|
||||
endif
|
||||
|
||||
AUDIO_ENABLE ?= no
|
||||
ifeq ($(strip $(AUDIO_ENABLE)), yes)
|
||||
ifeq ($(PLATFORM),CHIBIOS)
|
||||
@@ -632,22 +628,6 @@ ifeq ($(strip $(VIA_ENABLE)), yes)
|
||||
TRI_LAYER_ENABLE := yes
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(XAP_ENABLE)), yes)
|
||||
ifeq ($(strip $(VIA_ENABLE)), yes)
|
||||
$(error 'XAP_ENABLE = $(XAP_ENABLE)' deprecates 'VIA_ENABLE = $(VIA_ENABLE)'. Please set 'VIA_ENABLE = no')
|
||||
endif
|
||||
|
||||
DYNAMIC_KEYMAP_ENABLE := yes
|
||||
FNV_ENABLE := yes
|
||||
SECURE_ENABLE := yes
|
||||
BOOTMAGIC_ENABLE := yes
|
||||
|
||||
OPT_DEFS += -DXAP_ENABLE
|
||||
OPT_DEFS += -DBOOTLOADER_JUMP_SUPPORTED
|
||||
VPATH += $(QUANTUM_DIR)/xap
|
||||
SRC += $(QUANTUM_DIR)/xap/xap.c $(QUANTUM_DIR)/xap/xap_handlers.c
|
||||
endif
|
||||
|
||||
VALID_CUSTOM_MATRIX_TYPES:= yes lite no
|
||||
|
||||
CUSTOM_MATRIX ?= no
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# Copyright 2022 Nick Brassel (@tzarc)
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
XAP_FILES := $(shell ls -1 data/xap/* | sort | xargs echo)
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_1)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYBOARD_PATH_1)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_2)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYBOARD_PATH_2)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_3)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYBOARD_PATH_3)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_4)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYBOARD_PATH_4)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(KEYBOARD_PATH_5)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYBOARD_PATH_5)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(KEYMAP_PATH)/xap.hjson)","")
|
||||
XAP_FILES += $(KEYMAP_PATH)/xap.hjson
|
||||
endif
|
||||
ifneq ("$(wildcard $(USER_NAME)/xap.hjson)","")
|
||||
XAP_FILES += $(USER_NAME)/xap.hjson
|
||||
endif
|
||||
|
||||
$(INTERMEDIATE_OUTPUT)/src/config_blob_gz.h: $(INFO_JSON_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) xap-generate-qmk-blob-h -o "$(INTERMEDIATE_OUTPUT)/src/config_blob_gz.h" -kb $(KEYBOARD) -km $(KEYMAP))
|
||||
@$(BUILD_CMD)
|
||||
|
||||
$(INTERMEDIATE_OUTPUT)/src/xap_generated.inl: $(XAP_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) xap-generate-qmk-inc -o "$(INTERMEDIATE_OUTPUT)/src/xap_generated.inl" -kb $(KEYBOARD) -km $(KEYMAP))
|
||||
@$(BUILD_CMD)
|
||||
|
||||
$(INTERMEDIATE_OUTPUT)/src/xap_generated.h: $(XAP_FILES)
|
||||
@$(SILENT) || printf "$(MSG_GENERATING) $@" | $(AWK_CMD)
|
||||
$(eval CMD=$(QMK_BIN) xap-generate-qmk-h -o "$(INTERMEDIATE_OUTPUT)/src/xap_generated.h" -kb $(KEYBOARD) -km $(KEYMAP))
|
||||
@$(BUILD_CMD)
|
||||
|
||||
generated-files: $(INTERMEDIATE_OUTPUT)/src/config_blob_gz.h $(INTERMEDIATE_OUTPUT)/src/xap_generated.inl $(INTERMEDIATE_OUTPUT)/src/xap_generated.h
|
||||
|
||||
VPATH += $(INTERMEDIATE_OUTPUT)/src
|
||||
@@ -1140,7 +1140,7 @@
|
||||
"0x00AE": {
|
||||
"group": "media",
|
||||
"key": "KC_MEDIA_PLAY_PAUSE",
|
||||
"label": "Play/Pause Track",
|
||||
"label": "Mute",
|
||||
"aliases": [
|
||||
"KC_MPLY"
|
||||
]
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
{
|
||||
"groups": {
|
||||
"reactive": {
|
||||
"define": "LED_MATRIX_KEYREACTIVE_ENABLED"
|
||||
}
|
||||
},
|
||||
"effects": {
|
||||
"0x00": {
|
||||
"key": "SOLID"
|
||||
},
|
||||
"0x01": {
|
||||
"key": "ALPHAS_MODS"
|
||||
},
|
||||
"0x02": {
|
||||
"key": "BREATHING"
|
||||
},
|
||||
"0x03": {
|
||||
"key": "BAND"
|
||||
},
|
||||
"0x04": {
|
||||
"key": "BAND_PINWHEEL"
|
||||
},
|
||||
"0x05": {
|
||||
"key": "BAND_SPIRAL"
|
||||
},
|
||||
"0x06": {
|
||||
"key": "CYCLE_LEFT_RIGHT"
|
||||
},
|
||||
"0x07": {
|
||||
"key": "CYCLE_UP_DOWN"
|
||||
},
|
||||
"0x08": {
|
||||
"key": "CYCLE_OUT_IN"
|
||||
},
|
||||
"0x09": {
|
||||
"key": "DUAL_BEACON"
|
||||
},
|
||||
"0x0A": {
|
||||
"key": "WAVE_LEFT_RIGHT"
|
||||
},
|
||||
"0x0B": {
|
||||
"key": "WAVE_UP_DOWN"
|
||||
},
|
||||
|
||||
"0x0C": {
|
||||
"key": "SOLID_REACTIVE_SIMPLE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x0D": {
|
||||
"key": "SOLID_REACTIVE_WIDE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x0E": {
|
||||
"key": "SOLID_REACTIVE_MULTIWIDE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x0F": {
|
||||
"key": "SOLID_REACTIVE_CROSS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x10": {
|
||||
"key": "SOLID_REACTIVE_MULTICROSS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x11": {
|
||||
"key": "SOLID_REACTIVE_NEXUS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x12": {
|
||||
"key": "SOLID_REACTIVE_MULTINEXUS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x13": {
|
||||
"key": "SOLID_SPLASH",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x14": {
|
||||
"key": "SOLID_MULTISPLASH",
|
||||
"group": "reactive"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
{
|
||||
"groups": {
|
||||
"framebuffer": {
|
||||
"define": "RGB_MATRIX_FRAMEBUFFER_EFFECTS"
|
||||
},
|
||||
"reactive": {
|
||||
"define": "RGB_MATRIX_KEYREACTIVE_ENABLED"
|
||||
}
|
||||
},
|
||||
"effects": {
|
||||
"0x00": {
|
||||
"key": "SOLID_COLOR"
|
||||
},
|
||||
"0x01": {
|
||||
"key": "ALPHAS_MODS"
|
||||
},
|
||||
"0x02": {
|
||||
"key": "GRADIENT_UP_DOWN"
|
||||
},
|
||||
"0x03": {
|
||||
"key": "GRADIENT_LEFT_RIGHT"
|
||||
},
|
||||
"0x04": {
|
||||
"key": "BREATHING"
|
||||
},
|
||||
"0x05": {
|
||||
"key": "BAND_SAT"
|
||||
},
|
||||
"0x06": {
|
||||
"key": "BAND_VAL"
|
||||
},
|
||||
"0x07": {
|
||||
"key": "BAND_PINWHEEL_SAT"
|
||||
},
|
||||
"0x08": {
|
||||
"key": "BAND_PINWHEEL_VAL"
|
||||
},
|
||||
"0x09": {
|
||||
"key": "BAND_SPIRAL_SAT"
|
||||
},
|
||||
"0x0A": {
|
||||
"key": "BAND_SPIRAL_VAL"
|
||||
},
|
||||
"0x0B": {
|
||||
"key": "CYCLE_ALL"
|
||||
},
|
||||
"0x0C": {
|
||||
"key": "CYCLE_LEFT_RIGHT"
|
||||
},
|
||||
"0x0D": {
|
||||
"key": "CYCLE_UP_DOWN"
|
||||
},
|
||||
"0x0E": {
|
||||
"key": "CYCLE_OUT_IN"
|
||||
},
|
||||
"0x0F": {
|
||||
"key": "CYCLE_OUT_IN_DUAL"
|
||||
},
|
||||
"0x10": {
|
||||
"key": "RAINBOW_MOVING_CHEVRON"
|
||||
},
|
||||
"0x11": {
|
||||
"key": "CYCLE_PINWHEEL"
|
||||
},
|
||||
"0x12": {
|
||||
"key": "CYCLE_SPIRAL"
|
||||
},
|
||||
"0x13": {
|
||||
"key": "DUAL_BEACON"
|
||||
},
|
||||
"0x14": {
|
||||
"key": "RAINBOW_BEACON"
|
||||
},
|
||||
"0x15": {
|
||||
"key": "RAINBOW_PINWHEELS"
|
||||
},
|
||||
"0x16": {
|
||||
"key": "RAINDROPS"
|
||||
},
|
||||
"0x17": {
|
||||
"key": "JELLYBEAN_RAINDROPS"
|
||||
},
|
||||
"0x18": {
|
||||
"key": "HUE_BREATHING"
|
||||
},
|
||||
"0x19": {
|
||||
"key": "HUE_PENDULUM"
|
||||
},
|
||||
"0x1A": {
|
||||
"key": "HUE_WAVE"
|
||||
},
|
||||
"0x1B": {
|
||||
"key": "PIXEL_FRACTAL"
|
||||
},
|
||||
"0x1C": {
|
||||
"key": "PIXEL_FLOW"
|
||||
},
|
||||
"0x1D": {
|
||||
"key": "PIXEL_RAIN"
|
||||
},
|
||||
|
||||
"0x1E": {
|
||||
"key": "TYPING_HEATMAP",
|
||||
"group": "framebuffer"
|
||||
},
|
||||
"0x1F": {
|
||||
"key": "DIGITAL_RAIN",
|
||||
"group": "framebuffer"
|
||||
},
|
||||
|
||||
"0x20": {
|
||||
"key": "SOLID_REACTIVE_SIMPLE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x21": {
|
||||
"key": "SOLID_REACTIVE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x22": {
|
||||
"key": "SOLID_REACTIVE_WIDE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x23": {
|
||||
"key": "SOLID_REACTIVE_MULTIWIDE",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x24": {
|
||||
"key": "SOLID_REACTIVE_CROSS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x25": {
|
||||
"key": "SOLID_REACTIVE_MULTICROSS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x26": {
|
||||
"key": "SOLID_REACTIVE_NEXUS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x27": {
|
||||
"key": "SOLID_REACTIVE_MULTINEXUS",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x28": {
|
||||
"key": "SPLASH",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x29": {
|
||||
"key": "MULTISPLASH",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x2A": {
|
||||
"key": "SOLID_SPLASH",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x2B": {
|
||||
"key": "SOLID_MULTISPLASH",
|
||||
"group": "reactive"
|
||||
},
|
||||
"0x2C": {
|
||||
"key": "FLOWER_BLOOMING"
|
||||
},
|
||||
"0x2D": {
|
||||
"key": "STARLIGHT"
|
||||
},
|
||||
"0x2E": {
|
||||
"key": "STARLIGHT_DUAL_SAT"
|
||||
},
|
||||
"0x2F": {
|
||||
"key": "STARLIGHT_DUAL_HUE"
|
||||
},
|
||||
"0x30": {
|
||||
"key": "RIVERFLOW"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
{
|
||||
"effects": {
|
||||
"0x00": {
|
||||
"key": "STATIC_LIGHT"
|
||||
},
|
||||
"0x01": {
|
||||
"key": "BREATHING"
|
||||
},
|
||||
"0x02": {
|
||||
"key": "BREATHING_2"
|
||||
},
|
||||
"0x03": {
|
||||
"key": "BREATHING_3"
|
||||
},
|
||||
"0x04": {
|
||||
"key": "BREATHING_4"
|
||||
},
|
||||
"0x05": {
|
||||
"key": "RAINBOW_MOOD"
|
||||
},
|
||||
"0x06": {
|
||||
"key": "RAINBOW_MOOD_2"
|
||||
},
|
||||
"0x07": {
|
||||
"key": "RAINBOW_MOOD_3"
|
||||
},
|
||||
"0x08": {
|
||||
"key": "RAINBOW_SWIRL"
|
||||
},
|
||||
"0x09": {
|
||||
"key": "RAINBOW_SWIRL_2"
|
||||
},
|
||||
"0x0A": {
|
||||
"key": "RAINBOW_SWIRL_3"
|
||||
},
|
||||
"0x0B": {
|
||||
"key": "RAINBOW_SWIRL_4"
|
||||
},
|
||||
"0x0C": {
|
||||
"key": "RAINBOW_SWIRL_5"
|
||||
},
|
||||
"0x0D": {
|
||||
"key": "RAINBOW_SWIRL_6"
|
||||
},
|
||||
"0x0E": {
|
||||
"key": "SNAKE"
|
||||
},
|
||||
"0x0F": {
|
||||
"key": "SNAKE_2"
|
||||
},
|
||||
"0x10": {
|
||||
"key": "SNAKE_3"
|
||||
},
|
||||
"0x11": {
|
||||
"key": "SNAKE_4"
|
||||
},
|
||||
"0x12": {
|
||||
"key": "SNAKE_5"
|
||||
},
|
||||
"0x13": {
|
||||
"key": "SNAKE_6"
|
||||
},
|
||||
"0x14": {
|
||||
"key": "KNIGHT"
|
||||
},
|
||||
"0x15": {
|
||||
"key": "KNIGHT_2"
|
||||
},
|
||||
"0x16": {
|
||||
"key": "KNIGHT_3"
|
||||
},
|
||||
"0x17": {
|
||||
"key": "CHRISTMAS"
|
||||
},
|
||||
"0x18": {
|
||||
"key": "STATIC_GRADIENT"
|
||||
},
|
||||
"0x19": {
|
||||
"key": "STATIC_GRADIENT_2"
|
||||
},
|
||||
"0x1A": {
|
||||
"key": "STATIC_GRADIENT_3"
|
||||
},
|
||||
"0x1B": {
|
||||
"key": "STATIC_GRADIENT_4"
|
||||
},
|
||||
"0x1C": {
|
||||
"key": "STATIC_GRADIENT_5"
|
||||
},
|
||||
"0x1D": {
|
||||
"key": "STATIC_GRADIENT_6"
|
||||
},
|
||||
"0x1E": {
|
||||
"key": "STATIC_GRADIENT_7"
|
||||
},
|
||||
"0x1F": {
|
||||
"key": "STATIC_GRADIENT_8"
|
||||
},
|
||||
"0x20": {
|
||||
"key": "STATIC_GRADIENT_9"
|
||||
},
|
||||
"0x21": {
|
||||
"key": "STATIC_GRADIENT_10"
|
||||
},
|
||||
"0x22": {
|
||||
"key": "RGB_TEST"
|
||||
},
|
||||
"0x23": {
|
||||
"key": "ALTERNATING"
|
||||
},
|
||||
"0x24": {
|
||||
"key": "TWINKLE"
|
||||
},
|
||||
"0x25": {
|
||||
"key": "TWINKLE_2"
|
||||
},
|
||||
"0x26": {
|
||||
"key": "TWINKLE_3"
|
||||
},
|
||||
"0x27": {
|
||||
"key": "TWINKLE_4"
|
||||
},
|
||||
"0x28": {
|
||||
"key": "TWINKLE_5"
|
||||
},
|
||||
"0x29": {
|
||||
"key": "TWINKLE_6"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"secure": {
|
||||
"unlock_sequence": [ [0,0] ],
|
||||
"unlock_timeout": 5000,
|
||||
"idle_timeout": 60000
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,6 @@
|
||||
"type": "object",
|
||||
"additionalProperties": {"type": "boolean"}
|
||||
},
|
||||
"build_target": {
|
||||
"oneOf": [
|
||||
{"$ref": "#/keyboard_keymap_tuple"},
|
||||
{"$ref": "#/json_file_path"}
|
||||
]
|
||||
},
|
||||
"define": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 50,
|
||||
"pattern": "^[A-Z_]*$"
|
||||
},
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
@@ -199,10 +187,6 @@
|
||||
"minLength": 1,
|
||||
"maxLength": 250
|
||||
},
|
||||
"text_unsigned_int": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-8]+$"
|
||||
},
|
||||
"unsigned_decimal": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "qmk.xap.v1",
|
||||
"title": "XAP Spec",
|
||||
"definitions": {
|
||||
"data_type": {
|
||||
"oneOf": [
|
||||
{
|
||||
"enum": [
|
||||
"bool",
|
||||
"u8",
|
||||
"u16",
|
||||
"u32",
|
||||
"u64",
|
||||
"struct",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^u\\d{1,2}\\[\\d{1,2}\\]*$"
|
||||
}
|
||||
]
|
||||
},
|
||||
"router_type": {
|
||||
"enum": [
|
||||
"command",
|
||||
"router"
|
||||
]
|
||||
},
|
||||
"permission": {
|
||||
"enum": [
|
||||
"secure"
|
||||
]
|
||||
},
|
||||
"struct": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"type"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "qmk.definitions.v1#/hex_number_2d"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"type",
|
||||
"define"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/router_type"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"define": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"permissions": {
|
||||
"$ref": "#/definitions/permission"
|
||||
},
|
||||
"enable_if_preprocessor": {
|
||||
"type": "string"
|
||||
},
|
||||
"request_type": {
|
||||
"$ref": "#/definitions/data_type"
|
||||
},
|
||||
"request_struct_length": {
|
||||
"type": "number"
|
||||
},
|
||||
"request_purpose": {
|
||||
"type": "string"
|
||||
},
|
||||
"return_type": {
|
||||
"$ref": "#/definitions/data_type"
|
||||
},
|
||||
"request_struct_members": {
|
||||
"$ref": "#definitions/struct"
|
||||
},
|
||||
"return_struct_length": {
|
||||
"type": "number"
|
||||
},
|
||||
"return_constant": {
|
||||
"type": [
|
||||
"array",
|
||||
"string"
|
||||
]
|
||||
},
|
||||
"return_struct_members": {
|
||||
"$ref": "#definitions/struct"
|
||||
},
|
||||
"return_purpose": {
|
||||
"type": "string"
|
||||
},
|
||||
"return_execute": {
|
||||
"type": "string"
|
||||
},
|
||||
"routes": {
|
||||
"$ref": "#/definitions/route"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"$ref": "qmk.definitions.v1#/bcd_version"
|
||||
},
|
||||
"define": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"documentation": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"order": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"term_definitions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type_docs": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type_definitions": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/data_type"
|
||||
},
|
||||
"struct_length": {
|
||||
"type": "number"
|
||||
},
|
||||
"struct_members": {
|
||||
"$ref": "#definitions/struct"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"response_flags": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"define_prefix": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"bits": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "qmk.definitions.v1#/text_unsigned_int"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"define": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"broadcast_messages": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"define_prefix": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"messages": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"$ref": "qmk.definitions.v1#/hex_number_2d"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"define": {
|
||||
"$ref": "qmk.definitions.v1#/define"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"return_type": {
|
||||
"$ref": "#/definitions/data_type"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"routes": {
|
||||
"$ref": "#/definitions/route"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_SH_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_SH_LIKE }}
|
||||
from enum import IntEnum
|
||||
|
||||
|
||||
# version: 0.0.1
|
||||
class RgblightModes(IntEnum):
|
||||
{% for id, effect in specs.rgblight.effects | dictsort %}
|
||||
{{ effect.key }} = {{ id }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
# version: 0.0.1
|
||||
class RgbMatrixModes(IntEnum):
|
||||
{% for id, effect in specs.rgb_matrix.effects | dictsort %}
|
||||
{{ effect.key }} = {{ id }}
|
||||
{% endfor %}
|
||||
|
||||
# noqa: W391
|
||||
@@ -1,22 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_SH_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_SH_LIKE }}
|
||||
class XAPRouteError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class XAPRoutes():
|
||||
{% for id, route in xap.routes | dictsort %}
|
||||
{% if route.routes %}
|
||||
# {{route.define}}
|
||||
{% for subid, subroute in route.routes | dictsort %}
|
||||
{{route.define}}_{{subroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}'
|
||||
{% if subroute.routes %}
|
||||
{% for subsubid, subsubroute in subroute.routes | dictsort %}
|
||||
{{route.define}}_{{subroute.define}}_{{subsubroute.define}} = b'\x{{ '%02d' % id|int(base=16) }}\x{{ '%02d' % subid|int(base=16) }}\x{{ '%02d' % subsubid|int(base=16) }}'
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
# noqa: W391
|
||||
@@ -1,68 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_SH_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_SH_LIKE }}
|
||||
from collections import namedtuple
|
||||
from enum import IntFlag, IntEnum
|
||||
from struct import Struct
|
||||
|
||||
|
||||
{% macro gen_struct(name, members, fmt) -%}
|
||||
class {{ name }}(namedtuple('{{ name }}', '{{ members }}')):
|
||||
fmt = Struct('{{ fmt }}')
|
||||
|
||||
def __new__(cls, *args):
|
||||
return super().__new__(cls, *args)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data):
|
||||
return cls._make(cls.fmt.unpack(data))
|
||||
|
||||
def to_bytes(self):
|
||||
return self.fmt.pack(*list(self))
|
||||
|
||||
|
||||
{% endmacro -%}
|
||||
|
||||
{% set type_definitions = [
|
||||
{'name':'XAPRequest', 'members': 'token length data', 'fmt':'<HB61s'},
|
||||
{'name':'XAPResponse', 'members': 'token flags length data', 'fmt':'<HBB60s'},
|
||||
{'name':'XAPBroadcast', 'members': 'token event length data', 'fmt':'<HBB60s'},
|
||||
{'name':'XAPConfigBacklight', 'members': 'enable mode val', 'fmt':'<BBB'},
|
||||
{'name':'XAPConfigRgblight', 'members': 'enable mode hue sat val speed', 'fmt':'<BBBBBB'},
|
||||
{'name':'XAPConfigRgbMatrix', 'members': 'enable mode hue sat val speed flags', 'fmt':'<BBBBBBB'}
|
||||
] %}
|
||||
{% for item in type_definitions -%}{{ gen_struct(item.name, item.members, item.fmt) }}{% endfor -%}
|
||||
|
||||
# Spec structs
|
||||
{% for item in xap.routes.values() recursive %}
|
||||
{%- if item.routes -%}
|
||||
{{ loop(item.routes.values()) }}
|
||||
{%- endif -%}
|
||||
{%- if item.request_struct_members %}
|
||||
# TODO: gen inbound object for {{ item.define | to_snake }}
|
||||
{% endif -%}
|
||||
{%- if item.return_struct_members %}
|
||||
# TODO: gen outbound object for {{ item.define | to_snake }}
|
||||
{% endif %}
|
||||
{%- endfor %}
|
||||
|
||||
|
||||
class XAPSecureStatus(IntEnum):
|
||||
LOCKED = 0x00
|
||||
UNLOCKING = 0x01
|
||||
UNLOCKED = 0x02
|
||||
|
||||
|
||||
class XAPEventType(IntEnum):
|
||||
{% for id, message in xap.broadcast_messages.messages | dictsort %}
|
||||
{{ message.define }} = {{ id }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
class XAPFlags(IntFlag):
|
||||
{% for bitnum, bitinfo in xap.response_flags.bits | dictsort %}
|
||||
{% if bitinfo.define != "-" %}
|
||||
{{ bitinfo.define }} = 1 << {{ bitnum }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
# noqa: W391
|
||||
@@ -1,4 +0,0 @@
|
||||
{% for id, message in xap.broadcast_messages.messages | dictsort %}
|
||||
### {{ message.name }} - `{{ id }}`
|
||||
{{ message.description }}
|
||||
{% endfor %}
|
||||
@@ -1,11 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_XML_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_XML_LIKE }}
|
||||
{%- for item in xap.documentation.order %}
|
||||
{% if not item[0:1] == '!' %}
|
||||
{{ xap.documentation.get(item) }}
|
||||
|
||||
{% else %}
|
||||
{% include item[1:] %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1,12 +0,0 @@
|
||||
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} Bit {{ bitnum }} |{% endfor %}
|
||||
|
||||
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} -- |{% endfor %}
|
||||
|
||||
|{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %} `{{ bitinfo.define }}` |{% endfor %}
|
||||
|
||||
|
||||
{% for bitnum, bitinfo in xap.response_flags.bits | dictsort | reverse %}
|
||||
{% if bitinfo.define != "-" -%}
|
||||
* Bit {{ bitnum }} (`{{ bitinfo.define }}`): {{ bitinfo.description }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1,52 +0,0 @@
|
||||
{%- macro gen_payload(name, type, purpose, members) -%}
|
||||
{%- if type == 'struct' -%}
|
||||
__{{ name }}:__
|
||||
{%- for member in members -%}
|
||||
<br>{{ " "|safe*4 }}* {{ member.name }}: `{{ member.type }}`
|
||||
{%- endfor -%}
|
||||
{%- elif purpose -%}
|
||||
__{{ name }}:__<br>{{ " "|safe*4 }}* {{ purpose }}: `{{ type }}`
|
||||
{%- elif type -%}
|
||||
__{{ name }}:__ `{{ type }}`
|
||||
{%- endif -%}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro gen_payloads(route) -%}
|
||||
{{ gen_payload('Request', route.request_type, route.request_purpose, route.request_struct_members) }}{%- if route.return_type and route.request_type -%}<br><br>{% endif %}{{ gen_payload('Response', route.return_type, null, route.return_struct_members) }}
|
||||
{%- endmacro -%}
|
||||
|
||||
{%- macro gen_tags(route) -%}
|
||||
{% if 'secure' == route.permissions %}__Secure__{% endif %}
|
||||
{%- endmacro -%}
|
||||
|
||||
{% for id, route in xap.routes | dictsort %}
|
||||
### {{ route.name }} - `{{ id }}`
|
||||
{{ route.description }}
|
||||
|
||||
{% if route.routes %}
|
||||
| Name | Route | Tags | Payloads | Description |
|
||||
| -- | -- | -- | -- | -- |
|
||||
{% for subid, subroute in route.routes | dictsort %}
|
||||
{% if not subroute.routes %}
|
||||
| {{ subroute.name }} | `{{ id }} {{ subid }}` | {{ gen_tags(subroute) }} | {{ gen_payloads(subroute) }} | {{ subroute.description | newline_to_br }}|
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% for subid, subroute in route.routes | dictsort %}
|
||||
{%- if subroute.routes %}
|
||||
|
||||
#### {{ subroute.name }} - `{{ id }} {{ subid }}`
|
||||
{{ subroute.description }}
|
||||
|
||||
| Name | Route | Tags | Payloads | Description |
|
||||
| -- | -- | -- | -- | -- |
|
||||
{% for subsubid, subsubroute in subroute.routes | dictsort %}
|
||||
{% if not subsubroute.routes %}
|
||||
| {{ subsubroute.name }} | `{{ id }} {{ subid }} {{ subsubid }}` | {{ gen_tags(subsubroute) }} | {{ gen_payloads(subsubroute) }} | {{ subsubroute.description | newline_to_br }}|
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
@@ -1,8 +0,0 @@
|
||||
| Name | Definition |
|
||||
| -- | -- |
|
||||
{% for type, definition in xap.term_definitions | dictsort %}
|
||||
| _{{ type }}_ | {{ definition }} |
|
||||
{% endfor %}
|
||||
{% for type, definition in xap.type_definitions | dictsort %}
|
||||
| _{{ definition.name }}_ | {{ definition.description }}{% if 'struct' == definition.type %} Takes the format:{% for item in definition.struct_members %}<br>`{{ item.type }}` - {{ item.name }}{%- endfor %}{% endif %} |
|
||||
{% endfor %}
|
||||
@@ -1,5 +0,0 @@
|
||||
| Name | Definition |
|
||||
| -- | -- |
|
||||
{% for type, definition in xap.type_docs | dictsort %}
|
||||
| _{{ type }}_ | {{ definition }} |
|
||||
{% endfor %}
|
||||
@@ -1,6 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_XML_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_XML_LIKE }}
|
||||
|
||||
{%- for ver in versions | reverse -%}
|
||||
* [XAP Version {{ ver }}](xap_{{ ver }}.md)
|
||||
{% endfor %}
|
||||
@@ -1,171 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_C_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_C_LIKE }}
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Versions and identifiers
|
||||
|
||||
#define XAP_BCD_VERSION UINT32_C({{ xap.version | triplet_to_bcd }})
|
||||
#define QMK_BCD_VERSION UINT32_C({{ qmk_version | triplet_to_bcd }})
|
||||
#define XAP_KEYBOARD_IDENTIFIER UINT32_C({{ keyboard | fnv1a_32 }})
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Response flag definitions
|
||||
|
||||
{% for bit,data in xap.response_flags.bits | dictsort %}
|
||||
#define {{ xap.response_flags.define_prefix }}_{{ data.define | to_snake | upper }} (UINT32_C(1) << ({{ bit }}))
|
||||
{% endfor %}
|
||||
#define {{ xap.response_flags.define_prefix }}_FAILED 0x00
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast message definitions
|
||||
|
||||
{% for message_id,data in xap.broadcast_messages.messages | dictsort %}
|
||||
#define {{ xap.broadcast_messages.define_prefix }}_{{ data.define | to_snake | upper }} {{ message_id }}
|
||||
{% if 'return_type' in data %}
|
||||
void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}({{ data.return_type | type_to_c('value') }});
|
||||
{% else %}
|
||||
void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}(const void *data, size_t length);
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
#define XAP_BROADCAST_TOKEN 0xFFFF
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Type definitions
|
||||
|
||||
{% for name,data in xap.type_definitions | dictsort %}
|
||||
{% if data.type != 'struct' %}
|
||||
typedef {{ data.type | type_to_c('xap_'+(name|to_snake|lower)+'_t') }};
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for name,data in xap.type_definitions | dictsort %}
|
||||
{% if data.type == 'struct' %}
|
||||
typedef struct {
|
||||
{% for member in data.struct_members %}
|
||||
{{ member.type | type_to_c(member.name) }};
|
||||
{% endfor %}
|
||||
} __attribute__((__packed__)) xap_{{ name | to_snake | lower }}_t{{ data.type | type_to_c_after }};
|
||||
_Static_assert(sizeof(xap_{{ name | to_snake | lower }}_t) == {{ data.struct_length }}, "xap_{{ name | to_snake | lower }}_t needs to be {{ data.struct_length }} bytes in size");
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Route definitions
|
||||
|
||||
{% macro export_route_types(prefix, container) %}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
|
||||
{% if 'request_struct_members' in data %}
|
||||
typedef struct {
|
||||
{% for member in data.request_struct_members %}
|
||||
{{ member.type | type_to_c(member.name|lower) }};
|
||||
{% endfor %}
|
||||
} __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_arg_t;
|
||||
_Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_arg_t) == {{ data.request_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_arg_t needs to be {{ data.request_struct_length }} bytes in size");
|
||||
{% elif 'request_type' in data %}
|
||||
{% if '[' in data.request_type %}
|
||||
typedef struct __attribute__((__packed__)) { {{ data.request_type | type_to_c('x') }}; } {{ this_prefix_lc }}_arg_t;
|
||||
{% else %}
|
||||
typedef {{ data.request_type | type_to_c(this_prefix_lc+'_arg_t') }};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if 'return_struct_members' in data %}
|
||||
typedef struct {
|
||||
{% for member in data.return_struct_members %}
|
||||
{{ member.type | type_to_c(member.name|lower) }};
|
||||
{% endfor %}
|
||||
} __attribute__((__packed__)) {{ this_prefix_lc | to_snake | lower }}_t;
|
||||
_Static_assert(sizeof({{ this_prefix_lc | to_snake | lower }}_t) == {{ data.return_struct_length }}, "{{ this_prefix_lc | to_snake | lower }}_t needs to be {{ data.return_struct_length }} bytes in size");
|
||||
{% elif 'return_type' in data %}
|
||||
{% if '[' in data.return_type %}
|
||||
typedef struct __attribute__((__packed__)) { {{ data.return_type | type_to_c('x') }}; } {{ this_prefix_lc }}_t;
|
||||
{% else %}
|
||||
typedef {{ data.return_type | type_to_c(this_prefix_lc+'_t') }};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{{ export_route_types(this_prefix_lc, data) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_types('xap_route', xap) }}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Capabilities IDs
|
||||
|
||||
{% macro export_route_ids(prefix, container) %}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
#define {{ this_prefix_uc }} {{ route }}
|
||||
{{ export_route_ids(this_prefix_uc, data) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_ids('XAP_ROUTE', xap) }}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Capabilities Masks
|
||||
|
||||
{% macro export_route_masks(prefix, container, preprocessor_condition) %}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
{% if 'enable_if_preprocessor' in data %}
|
||||
{% if preprocessor_condition == 'TRUE' %}
|
||||
{% set condition = "(" + data.enable_if_preprocessor + ")" %}
|
||||
{% else %}
|
||||
{% set condition = "(" + preprocessor_condition + " && (" + data.enable_if_preprocessor + "))" %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set condition = preprocessor_condition %}
|
||||
{% endif %}
|
||||
{% if condition == 'TRUE' %}
|
||||
#define {{ this_prefix_uc }}_MASK (UINT32_C(1) << ({{ this_prefix_uc }}))
|
||||
{% else %}
|
||||
#if ({{ condition }})
|
||||
#define {{ this_prefix_uc }}_MASK (UINT32_C(1) << ({{ this_prefix_uc }}))
|
||||
#else // ({{ condition }})
|
||||
#define {{ this_prefix_uc }}_MASK 0
|
||||
#endif // ({{ condition }})
|
||||
{% endif %}
|
||||
{{ export_route_masks(this_prefix_uc, data, condition) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_masks('XAP_ROUTE', xap, 'TRUE') }}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Capabilities Values
|
||||
|
||||
{% macro export_route_capabilities(prefix, container) %}
|
||||
{% if 'routes' in container %}
|
||||
#define {{ prefix }}_CAPABILITIES (0 \
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
| ({{ this_prefix_uc }}_MASK) \
|
||||
{% endfor %}
|
||||
)
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{{ export_route_capabilities(this_prefix_uc, data) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_capabilities('XAP_ROUTE', xap) }}
|
||||
@@ -1,166 +0,0 @@
|
||||
{{ constants.GPL2_HEADER_C_LIKE }}
|
||||
{{ constants.GENERATED_HEADER_C_LIKE }}
|
||||
|
||||
{% macro route_conditions(route_stack) %}
|
||||
{% set conditions = [] %}
|
||||
{% for data in route_stack %}
|
||||
{% if 'enable_if_preprocessor' in data %}
|
||||
{{ conditions.append(data.enable_if_preprocessor) or '' }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if conditions %}
|
||||
#if ({{ conditions | join(' && ') }})
|
||||
{% endif %}
|
||||
{{ caller() }}
|
||||
{%- if conditions %}
|
||||
#endif // ({{ conditions | join(' && ') }})
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Broadcast
|
||||
|
||||
{% for message_id,data in xap.broadcast_messages.messages | dictsort %}
|
||||
{% if 'return_type' in data %}
|
||||
void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}({{ data.return_type | type_to_c('value') }}) { xap_broadcast({{ message_id }}, &value, sizeof(value)); }
|
||||
{% else %}
|
||||
void {{ xap.broadcast_messages.define_prefix | lower }}_{{ data.define | to_snake | lower }}(const void *data, size_t length) { xap_broadcast({{ message_id }}, data, length); }
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Decl
|
||||
|
||||
{% macro export_route_declaration(prefix, container) %}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
{% if 'return_execute' in data %}
|
||||
|
||||
{% if 'request_struct_members' in data %}
|
||||
{% set arg_type = ( this_prefix_lc | to_snake | lower ) + '_arg_t' %}
|
||||
{% set arg_var = arg_type + ' arg' %}
|
||||
{% elif 'request_type' in data %}
|
||||
{% set arg_type = data.request_type | type_to_c() %}
|
||||
{% set arg_var = data.request_type | type_to_c('arg') %}
|
||||
{% endif %}
|
||||
|
||||
__attribute__((weak)) bool xap_execute_{{ data.return_execute }}(xap_token_t token{% if arg_type %}, {{ (arg_type + '* arg') if 'xap_route' in arg_type else arg_var }}{% endif %}) { return false; }
|
||||
__attribute__((weak)) bool xap_respond_{{ data.return_execute }}(xap_token_t token, const uint8_t *data, size_t data_len) {
|
||||
{% if arg_type %}
|
||||
if (data_len != sizeof({{ arg_type }})) {
|
||||
return false;
|
||||
}
|
||||
{{ arg_var }};
|
||||
memcpy(&arg, data, sizeof({{ arg_type }}));
|
||||
{% endif %}
|
||||
|
||||
return xap_execute_{{ data.return_execute }}(token{% if arg_type %}, {{ '&' if 'xap_route' in arg_type else '' }}arg{% endif %});
|
||||
}
|
||||
{% endif %}
|
||||
{{ export_route_declaration(this_prefix_lc, data) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_declaration('xap_route', xap) }}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Data
|
||||
|
||||
{% macro export_route_data(prefix, container, route_stack) %}
|
||||
{% set this_route_stack = route_stack.copy() %}
|
||||
{{ this_route_stack.append(container) or '' }}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
{% if 'return_constant' in data %}
|
||||
{% if data.return_type == 'struct' %}
|
||||
{% call route_conditions(this_route_stack) %}
|
||||
static const {{ this_prefix_lc }}_t {{ this_prefix_lc }}_data PROGMEM = {
|
||||
{% for member in data.return_constant %}
|
||||
{{ member }},
|
||||
{% endfor %}
|
||||
};
|
||||
{% endcall %}
|
||||
{% elif data.return_type == 'string' %}
|
||||
{% call route_conditions(this_route_stack) %}
|
||||
static const char {{ this_prefix_lc }}_str[] PROGMEM = {{ data.return_constant }};
|
||||
{% endcall %}
|
||||
{% else %}
|
||||
{% call route_conditions(this_route_stack) %}
|
||||
static const {{ data.return_type | type_to_c_before }} {{ this_prefix_lc }}_data PROGMEM = {{ data.return_constant }};
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{{ export_route_data(this_prefix_lc, data, this_route_stack) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ export_route_data('XAP_ROUTE', xap, []) }}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Routes
|
||||
|
||||
{% macro append_routing_table(prefix, container, route_stack) %}
|
||||
{% set this_route_stack = route_stack.copy() %}
|
||||
{{ this_route_stack.append(container) or '' }}
|
||||
{% if 'routes' in container %}
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set this_prefix_uc = (prefix + '_' + data.define) | upper %}
|
||||
{% set this_prefix_lc = this_prefix_uc | lower %}
|
||||
{{ append_routing_table(this_prefix_lc, data, this_route_stack) }}
|
||||
{% endfor %}
|
||||
{% call route_conditions(this_route_stack) %}
|
||||
static const xap_route_t {{ prefix | lower}}_table[] PROGMEM = {
|
||||
{% for route, data in container.routes | dictsort %}
|
||||
{% set inner_route_stack = this_route_stack.copy() %}
|
||||
{{ inner_route_stack.append(data) or '' }}
|
||||
{% if 'permissions' in data %}
|
||||
{% set secure_status = 'ROUTE_PERMISSIONS_SECURE' %}
|
||||
{% else %}
|
||||
{% set secure_status = 'ROUTE_PERMISSIONS_INSECURE' %}
|
||||
{% endif %}
|
||||
{% call route_conditions(inner_route_stack) %}
|
||||
[{{ prefix | upper }}_{{ data.define }}] = {
|
||||
{% if 'routes' in data %}
|
||||
.flags = {
|
||||
.type = XAP_ROUTE,
|
||||
.secure = {{ secure_status }},
|
||||
},
|
||||
.child_routes = {{ prefix | lower }}_{{ data.define | lower }}_table,
|
||||
.child_routes_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_table)/sizeof(xap_route_t),
|
||||
{% elif 'return_execute' in data %}
|
||||
.flags = {
|
||||
.type = XAP_EXECUTE,
|
||||
.secure = {{ secure_status }},
|
||||
},
|
||||
.handler = xap_respond_{{ data.return_execute | lower }},
|
||||
{% elif 'return_constant' in data and data.return_type == 'string' %}
|
||||
.flags = {
|
||||
.type = XAP_CONST_MEM,
|
||||
.secure = {{ secure_status }},
|
||||
},
|
||||
.const_data = {{ prefix | lower }}_{{ data.define | lower }}_str,
|
||||
.const_data_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_str) - 1,
|
||||
{% elif 'return_constant' in data %}
|
||||
.flags = {
|
||||
.type = XAP_CONST_MEM,
|
||||
.secure = {{ secure_status }},
|
||||
},
|
||||
.const_data = &{{ prefix | lower }}_{{ data.define | lower }}_data,
|
||||
.const_data_len = sizeof({{ prefix | lower }}_{{ data.define | lower }}_data),
|
||||
{% endif %}
|
||||
},
|
||||
{% endcall %}
|
||||
{% endfor %}
|
||||
};
|
||||
{% endcall %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{{ append_routing_table("xap_route", xap, []) }}
|
||||
@@ -1,237 +0,0 @@
|
||||
{
|
||||
version: 0.0.1
|
||||
|
||||
// Needed for table generation
|
||||
define: XAP_ROUTE
|
||||
|
||||
// Documentation section is used purely for `qmk xap-generate-docs`.
|
||||
documentation: {
|
||||
order: [
|
||||
page_header
|
||||
type_docs
|
||||
!type_docs.md.j2
|
||||
term_definitions
|
||||
!term_definitions.md.j2
|
||||
request_response
|
||||
reserved_tokens
|
||||
response_flags
|
||||
!response_flags.md.j2
|
||||
example_conversation
|
||||
routes
|
||||
!routes.md.j2
|
||||
]
|
||||
|
||||
page_header:
|
||||
'''
|
||||
# QMK Firmware XAP Specs
|
||||
|
||||
This document describes the requirements of the QMK XAP ("extensible application protocol") API.
|
||||
'''
|
||||
|
||||
type_docs:
|
||||
'''
|
||||
## Types
|
||||
|
||||
**All integral types are little-endian.**
|
||||
'''
|
||||
|
||||
term_definitions:
|
||||
'''
|
||||
## Definitions
|
||||
|
||||
This list defines the terms used across the entire set of XAP protocol documentation.
|
||||
'''
|
||||
|
||||
request_response:
|
||||
'''
|
||||
## Requests and Responses
|
||||
|
||||
Communication generally follows a request/response pattern.
|
||||
|
||||
Each request needs to include a _token_ -- this `u16` value prefixes each outbound request from the host application and its corresponding response.
|
||||
This allows response messages to be correlated with their request, even if multiple host applications are communicating with the firmware simultaneously.
|
||||
Host applications should randomly generate a token ID for **every** outbound request, unless using a reserved token defined below.
|
||||
To ensure host interoperability, valid token values are within the range `0x0100`-`0xFFFF`.
|
||||
|
||||
This token is followed by a `u8` signifying the length of data in the request.
|
||||
'''
|
||||
|
||||
// This documentation section reserved for next version
|
||||
reserved_tokens: ''
|
||||
|
||||
response_flags:
|
||||
'''
|
||||
Response messages will always be prefixed by the originating request _token_, directly followed by that request's _response flags_, then the response payload length:
|
||||
'''
|
||||
|
||||
example_conversation:
|
||||
'''
|
||||
### Example "conversation":
|
||||
|
||||
**Request** -- version query:
|
||||
|
||||
| Byte | 0 | 1 | 2 | 3 | 4 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **Purpose** | Token | Token | Payload Length | Route | Route |
|
||||
| **Value** | `0x43` | `0x2B` | `0x02` | `0x00` | `0x00` |
|
||||
|
||||
**Response** -- matching token, successful flag, payload of `0x03170192` = 3.17.192:
|
||||
|
||||
| Byte | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| **Purpose** | Token | Token | Response Flags | Payload Length | Payload | Payload | Payload | Payload |
|
||||
| **Value** | `0x43` | `0x2B` | `0x01` | `0x04` | `0x92` | `0x01` | `0x17` | `0x03` |
|
||||
'''
|
||||
|
||||
routes:
|
||||
'''
|
||||
## Routes
|
||||
|
||||
Subsystem validity should be queried through the “Enabled-in-firmware subsystem query” under the QMK subsystem (route=0x00,0x01).
|
||||
This is the primary method for determining if a subsystem has been enabled in the running firmware.
|
||||
'''
|
||||
}
|
||||
|
||||
type_docs: {
|
||||
u8:
|
||||
'''
|
||||
An unsigned 8-bit integral (octet, or byte), commonly seen as `uint8_t` from _stdint.h_.
|
||||
'''
|
||||
u16:
|
||||
'''
|
||||
An unsigned 16-bit integral, commonly seen as `uint16_t` from _stdint.h_.
|
||||
'''
|
||||
u32:
|
||||
'''
|
||||
An unsigned 32-bit integral, commonly seen as `uint32_t` from _stdint.h_.
|
||||
'''
|
||||
"type[n]":
|
||||
'''
|
||||
An array of `type`, with array extent of `N` -- e.g. `u8[2]` signifies two consecutive octets.
|
||||
'''
|
||||
}
|
||||
|
||||
term_definitions: {
|
||||
Subsystem:
|
||||
'''
|
||||
A high-level area of functionality within XAP.
|
||||
'''
|
||||
Route:
|
||||
'''
|
||||
A sequence of _IDs_ describing the route to invoke a _handler_.
|
||||
'''
|
||||
Handler:
|
||||
'''
|
||||
A piece of code that is executed when a specific _route_ is received.
|
||||
'''
|
||||
Response:
|
||||
'''
|
||||
The data sent back to the host during execution of a _handler_.
|
||||
'''
|
||||
Payload:
|
||||
'''
|
||||
Any received data appended to the _route_, which gets delivered to the _handler_ when received.
|
||||
'''
|
||||
}
|
||||
|
||||
type_definitions: {
|
||||
identifier: {
|
||||
name: ID
|
||||
description: A single octet / 8-bit byte, representing Subsystem or Route index.
|
||||
type: u8
|
||||
}
|
||||
|
||||
response_flags: {
|
||||
name: Response Flags
|
||||
description: An `u8` containing the status of the request.
|
||||
type: u8
|
||||
}
|
||||
|
||||
token: {
|
||||
name: Token
|
||||
description: A `u16` associated with a specific request as well as its corresponding response. Valid token values are within the range `0x0100`-`0xFFFF`.
|
||||
type: u16
|
||||
}
|
||||
|
||||
request_header: {
|
||||
name: Request Header
|
||||
description: Packet format for inbound data.
|
||||
type: struct
|
||||
struct_length: 3
|
||||
struct_members: [
|
||||
{
|
||||
type: token
|
||||
name: token
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: length
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
response_header: {
|
||||
name: Response Header
|
||||
description: Packet format for outbound data.
|
||||
type: struct
|
||||
struct_length: 4
|
||||
struct_members: [
|
||||
{
|
||||
type: token
|
||||
name: token
|
||||
},
|
||||
{
|
||||
type: response_flags
|
||||
name: flags
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: length
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
response_flags: {
|
||||
define_prefix: XAP_RESPONSE_FLAG
|
||||
bits: {
|
||||
0: {
|
||||
name: Success
|
||||
define: SUCCESS
|
||||
description:
|
||||
'''
|
||||
When this bit is set, the request was successfully handled. If not set, all payload data should be disregarded, and the request retried if appropriate (with a new token).
|
||||
'''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
routes: {
|
||||
0x00: {
|
||||
type: router
|
||||
name: XAP
|
||||
define: XAP
|
||||
description:
|
||||
'''
|
||||
This subsystem is always present, and provides the ability to query information about the XAP protocol of the connected device.
|
||||
'''
|
||||
routes: {
|
||||
0x00: {
|
||||
type: command
|
||||
name: Version Query
|
||||
define: VERSION_QUERY
|
||||
description:
|
||||
'''
|
||||
XAP protocol version query.
|
||||
|
||||
* Returns the BCD-encoded version in the format of XX.YY.ZZZZ => `0xXXYYZZZZ`
|
||||
* e.g. 3.2.115 will match `0x03020115`, or bytes {0x15,0x01,0x02,0x03}.
|
||||
'''
|
||||
return_type: u32
|
||||
return_purpose: bcd-version
|
||||
return_constant: XAP_BCD_VERSION
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,200 +0,0 @@
|
||||
{
|
||||
version: 0.2.0
|
||||
|
||||
routes: {
|
||||
0x04: {
|
||||
type: router
|
||||
name: Keymap
|
||||
define: KEYMAP
|
||||
description:
|
||||
'''
|
||||
This subsystem allows for query of currently configured keycodes.
|
||||
'''
|
||||
routes: {
|
||||
0x01: {
|
||||
type: command
|
||||
name: Capabilities Query
|
||||
define: CAPABILITIES_QUERY
|
||||
description:
|
||||
'''
|
||||
Keymap subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
|
||||
'''
|
||||
return_type: u32
|
||||
return_purpose: capabilities
|
||||
return_constant: XAP_ROUTE_KEYMAP_CAPABILITIES
|
||||
}
|
||||
0x02: {
|
||||
type: command
|
||||
name: Get Layer Count
|
||||
define: GET_LAYER_COUNT
|
||||
description: Query maximum number of layers that can be addressed within the keymap.
|
||||
return_type: u8
|
||||
return_execute: keymap_get_layer_count
|
||||
}
|
||||
0x03: {
|
||||
type: command
|
||||
name: Get Keycode
|
||||
define: GET_KEYMAP_KEYCODE
|
||||
description: Query the Keycode at the requested location.
|
||||
request_type: struct
|
||||
request_struct_length: 3
|
||||
request_struct_members: [
|
||||
{
|
||||
type: u8
|
||||
name: Layer
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Row
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Column
|
||||
}
|
||||
]
|
||||
return_type: u16
|
||||
return_execute: get_keymap_keycode
|
||||
}
|
||||
0x04: {
|
||||
type: command
|
||||
name: Get Encoder Keycode
|
||||
define: GET_ENCODER_KEYCODE
|
||||
description: Query the Keycode at the requested location.
|
||||
enable_if_preprocessor: defined(ENCODER_MAP_ENABLE)
|
||||
request_type: struct
|
||||
request_struct_length: 3
|
||||
request_struct_members: [
|
||||
{
|
||||
type: u8
|
||||
name: Layer
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Encoder
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Clockwise
|
||||
}
|
||||
]
|
||||
return_type: u16
|
||||
return_execute: get_encoder_keycode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0x05: {
|
||||
type: router
|
||||
name: Remapping
|
||||
define: REMAPPING
|
||||
description:
|
||||
'''
|
||||
This subsystem allows for live reassignment of keycodes without rebuilding the firmware.
|
||||
'''
|
||||
enable_if_preprocessor: defined(DYNAMIC_KEYMAP_ENABLE)
|
||||
routes: {
|
||||
0x01: {
|
||||
type: command
|
||||
name: Capabilities Query
|
||||
define: CAPABILITIES_QUERY
|
||||
description:
|
||||
'''
|
||||
Remapping subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
|
||||
'''
|
||||
return_type: u32
|
||||
return_purpose: capabilities
|
||||
return_constant: XAP_ROUTE_REMAPPING_CAPABILITIES
|
||||
}
|
||||
0x02: {
|
||||
type: command
|
||||
name: Get Layer Count
|
||||
define: GET_DYNAMIC_LAYER_COUNT
|
||||
description: Query maximum number of layers that can be addressed within the keymap.
|
||||
return_type: u8
|
||||
return_constant: DYNAMIC_KEYMAP_LAYER_COUNT
|
||||
}
|
||||
0x03: {
|
||||
type: command
|
||||
name: Set Keycode
|
||||
define: SET_KEYMAP_KEYCODE
|
||||
description: Modify the Keycode at the requested location.
|
||||
permissions: secure
|
||||
request_type: struct
|
||||
request_struct_length: 5
|
||||
request_struct_members: [
|
||||
{
|
||||
type: u8
|
||||
name: Layer
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Row
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Column
|
||||
},
|
||||
{
|
||||
type: u16
|
||||
name: Keycode
|
||||
}
|
||||
]
|
||||
return_execute: dynamic_keymap_set_keycode
|
||||
}
|
||||
0x04: {
|
||||
type: command
|
||||
name: Set Encoder Keycode
|
||||
define: SET_ENCODER_KEYCODE
|
||||
permissions: secure
|
||||
description: Modify the Keycode at the requested location.
|
||||
enable_if_preprocessor: defined(ENCODER_MAP_ENABLE)
|
||||
request_type: struct
|
||||
request_struct_length: 5
|
||||
request_struct_members: [
|
||||
{
|
||||
type: u8
|
||||
name: Layer
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Encoder
|
||||
},
|
||||
{
|
||||
type: u8
|
||||
name: Clockwise
|
||||
},
|
||||
{
|
||||
type: u16
|
||||
name: Keycode
|
||||
}
|
||||
]
|
||||
return_execute: dynamic_encoder_set_keycode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0x06: {
|
||||
type: router
|
||||
name: Lighting
|
||||
define: LIGHTING
|
||||
description:
|
||||
'''
|
||||
This subsystem allows for control over the lighting subsystem.
|
||||
'''
|
||||
routes: {
|
||||
0x01: {
|
||||
type: command
|
||||
name: Capabilities Query
|
||||
define: CAPABILITIES_QUERY
|
||||
description:
|
||||
'''
|
||||
Lighting subsystem capabilities query. Each bit should be considered as a "usable" route within this subsystem.
|
||||
'''
|
||||
return_type: u32
|
||||
return_purpose: capabilities
|
||||
return_constant: XAP_ROUTE_LIGHTING_CAPABILITIES
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user